use {
crate::{
Alignment,
Formattable,
MakeSegsState,
SplitGroupBuilder,
SplitGroupIdx,
new_sg,
new_sg_lit,
sg_general::{
append_binary,
append_whitespace,
new_sg_binary,
new_sg_macro,
new_sg_outer_attrs,
},
sg_general_lists::{
InlineListSuffix,
append_bracketed_list_common,
append_inline_list,
new_sg_bracketed_list,
new_sg_bracketed_list_common,
},
},
proc_macro2::{
LineColumn,
Span,
},
quote::ToTokens,
std::fmt::Write,
syn::{
AngleBracketedGenericArguments,
BareFnArg,
Expr,
FnArg,
GenericArgument,
GenericParam,
Generics,
LifetimeParam,
Path,
QSelf,
ReturnType,
Token,
Type,
TypeParamBound,
WhereClause,
WherePredicate,
spanned::Spanned,
},
};
pub(crate) fn append_angle_bracketed_generics(
out: &mut MakeSegsState,
base_indent: &Alignment,
sg: &mut SplitGroupBuilder,
generics: &AngleBracketedGenericArguments,
) {
if generics.args.is_empty() {
return;
}
sg.child(new_sg_bracketed_list_common(
out,
base_indent,
generics.lt_token.span.start(),
&generics.lt_token.to_token_stream().to_string(),
&generics.args,
generics.gt_token.span.start(),
&generics.gt_token.to_token_stream().to_string(),
));
}
pub(crate) fn append_generics(
out: &mut MakeSegsState,
base_indent: &Alignment,
sg: &mut SplitGroupBuilder,
generics: &Generics,
) {
append_generics_part_a(out, base_indent, sg, generics);
if let Some(wh) = &generics.where_clause {
sg.child(build_generics_part_b(out, base_indent, wh));
}
}
pub(crate) fn append_generics_part_a(
out: &mut MakeSegsState,
base_indent: &Alignment,
sg: &mut SplitGroupBuilder,
generics: &Generics,
) {
if generics.params.is_empty() {
return;
}
sg.child(new_sg_bracketed_list_common(
out,
base_indent,
generics.lt_token.map(|s| s.span.start()).unwrap_or(LineColumn {
line: 0,
column: 0,
}),
"<",
&generics.params,
generics.gt_token.map(|s| s.span.start()).unwrap_or(LineColumn {
line: 0,
column: 0,
}),
">",
));
}
pub(crate) fn append_path<
'a,
>(
out: &mut MakeSegsState,
node: &mut SplitGroupBuilder,
base_indent: &Alignment,
mut prefix: Option<Option<LineColumn>>,
pairs: impl Iterator<Item = syn::punctuated::Pair<&'a syn::PathSegment, &'a Token![::]>>,
) {
let indent = base_indent.clone();
for (i, seg) in pairs.enumerate() {
if i > 0 {
node.split(out, indent.clone(), true);
}
if let Some(d) = prefix.take() {
if let Some(t) = d {
append_whitespace(out, base_indent, node, t);
};
node.seg(out, "::");
}
append_whitespace(out, base_indent, node, seg.value().ident.span().start());
node.seg(out, &seg.value().ident.to_string());
match &seg.value().arguments {
syn::PathArguments::None => { },
syn::PathArguments::AngleBracketed(a) => {
node.child(
new_sg_bracketed_list_common(
out,
&indent,
a.lt_token.span.start(),
&format!("{}{}", match &a.colon2_token {
Some(_) => "::",
None => "",
}, "<"),
&a.args,
a.gt_token.span.start(),
">",
),
);
},
syn::PathArguments::Parenthesized(a) => {
node.child(
new_sg_bracketed_list_common(
out,
&indent,
a.paren_token.span.open().start(),
"(",
&a.inputs,
a.paren_token.span.close().start(),
")",
),
);
match &a.output {
ReturnType::Default => { },
ReturnType::Type(_, ty) => {
node.seg(out, " -> ");
node.child(ty.make_segs(out, &indent));
},
}
},
};
match seg.punct() {
Some(p) => prefix = Some(Some(p.spans[0].start())),
None => prefix = None,
}
}
}
pub(crate) fn build_array_type(
out: &mut MakeSegsState,
base_indent: &Alignment,
start: LineColumn,
expr: impl Formattable,
len: &Expr,
) -> SplitGroupIdx {
let mut sg = new_sg(out);
append_whitespace(out, base_indent, &mut sg, start);
sg.seg(out, "[");
sg.child(expr.make_segs(out, base_indent));
sg.seg(out, "; ");
sg.child(len.make_segs(out, base_indent));
sg.seg(out, "]");
sg.build(out)
}
pub(crate) fn build_extended_path(
out: &mut MakeSegsState,
base_indent: &Alignment,
qself: &Option<QSelf>,
p: &Path,
) -> SplitGroupIdx {
let mut node = new_sg(out);
match qself {
Some(qself) => {
append_whitespace(out, base_indent, &mut node, qself.lt_token.span.start());
node.seg(out, "<");
let taken = match qself.position {
0 => {
node.child(qself.ty.make_segs(out, base_indent));
0
},
n => {
node.child(qself.ty.make_segs(out, base_indent));
node.seg(out, " as ");
append_path(
out,
&mut node,
base_indent,
p.leading_colon.map(|t| Some(t.spans[0].start())),
p.segments.pairs().take(n),
);
n
},
};
append_whitespace(out, base_indent, &mut node, qself.gt_token.span.start());
node.seg(out, ">");
append_path(out, &mut node, base_indent, Some(None), p.segments.pairs().skip(taken));
},
None => {
append_path(
out,
&mut node,
base_indent,
p.leading_colon.map(|t| Some(t.spans[0].start())),
p.segments.pairs(),
);
},
};
node.build(out)
}
pub(crate) fn build_generics_part_b(
out: &mut MakeSegsState,
base_indent: &Alignment,
wh: &WhereClause,
) -> SplitGroupIdx {
let mut sg = new_sg(out);
if out.config.split_where {
sg.initial_split();
}
sg.seg_unsplit(out, " ");
append_whitespace(out, base_indent, &mut sg, wh.where_token.span.start());
sg.split(out, base_indent.clone(), true);
sg.seg(out, "where");
sg.seg_unsplit(out, " ");
append_inline_list(out, base_indent, &mut sg, ",", &wh.predicates, InlineListSuffix::<Expr>::None);
sg.build(out)
}
pub(crate) fn build_path(out: &mut MakeSegsState, base_indent: &Alignment, path: &syn::Path) -> SplitGroupIdx {
let mut node = new_sg(out);
append_path(
out,
&mut node,
base_indent,
path.leading_colon.map(|t| Some(t.spans[0].start())),
path.segments.pairs(),
);
node.build(out)
}
pub(crate) fn build_ref(
out: &mut MakeSegsState,
base_indent: &Alignment,
start: LineColumn,
raw: bool,
mutability: Option<(&Span, BuildRefMutability)>,
expr: impl Formattable,
) -> SplitGroupIdx {
let mut sg = new_sg(out);
append_whitespace(out, base_indent, &mut sg, start);
sg.seg(out, "&");
if raw {
sg.seg(out, "raw ");
}
if let Some(m) = mutability {
match m {
(m, BuildRefMutability::Const) => {
append_whitespace(out, base_indent, &mut sg, m.start());
sg.seg(out, "const ");
},
(m, BuildRefMutability::Mut) => {
append_whitespace(out, base_indent, &mut sg, m.start());
sg.seg(out, "mut ");
},
}
}
sg.child(expr.make_segs(out, base_indent));
sg.build(out)
}
pub(crate) enum BuildRefMutability {
Const,
Mut,
}
impl Formattable for &Path {
fn has_attrs(&self) -> bool {
false
}
fn make_segs(&self, out: &mut MakeSegsState, base_indent: &Alignment) -> SplitGroupIdx {
build_path(out, base_indent, self)
}
}
impl Formattable for &Type {
fn has_attrs(&self) -> bool {
false
}
fn make_segs(&self, out: &mut MakeSegsState, base_indent: &Alignment) -> SplitGroupIdx {
#[deny(clippy::wildcard_enum_match_arm)]
match self {
Type::Array(x) => build_array_type(
out,
base_indent,
x.bracket_token.span.open().start(),
x.elem.as_ref(),
&x.len,
),
Type::BareFn(x) => {
let mut sg = new_sg(out);
if let Some(hot) = &x.lifetimes {
append_whitespace(out, base_indent, &mut sg, hot.for_token.span.start());
sg.seg(out, "for");
append_bracketed_list_common(
out,
base_indent,
&mut sg,
hot.lt_token.span.start(),
"<",
&hot.lifetimes,
hot.gt_token.span.start(),
"> ",
);
}
if let Some(un) = &x.unsafety {
append_whitespace(out, base_indent, &mut sg, un.span.start());
sg.seg(out, "unsafe ");
}
if let Some(abi) = &x.abi {
append_whitespace(out, base_indent, &mut sg, abi.extern_token.span.start());
sg.seg(out, "extern ");
if let Some(name) = &abi.name {
append_whitespace(out, base_indent, &mut sg, name.span().start());
sg.seg(out, format!("{} ", name.to_token_stream()));
}
}
append_whitespace(out, base_indent, &mut sg, x.fn_token.span.start());
sg.seg(out, "fn");
sg.child(
new_sg_bracketed_list(
out,
base_indent,
x.paren_token.span.open().start(),
"(",
false,
",",
&x.inputs,
match &x.variadic {
Some(v) => InlineListSuffix::Extra(|out: &mut MakeSegsState, _base_indent: &Alignment| {
new_sg_lit(out, Some((base_indent, vec![v.dots.spans[0].start()])), "...")
}),
None => if out.macro_depth.get() == 0 {
InlineListSuffix::Punct
} else {
InlineListSuffix::VerbatimPunct
},
},
x.paren_token.span.close().start(),
")",
),
);
match &x.output {
ReturnType::Default => { },
ReturnType::Type(t, ty) => {
append_whitespace(out, base_indent, &mut sg, t.spans[0].start());
sg.seg(out, " -> ");
sg.child(ty.make_segs(out, base_indent));
},
}
sg.build(out)
},
Type::Group(x) => x.elem.make_segs(out, base_indent),
Type::ImplTrait(x) => {
let mut node = new_sg(out);
append_binary(out, base_indent, &mut node, "impl", |out: &mut MakeSegsState, base_indent: &Alignment| {
let mut node = new_sg(out);
append_inline_list(out, base_indent, &mut node, " +", &x.bounds, InlineListSuffix::<Expr>::None);
node.build(out)
});
node.build(out)
},
Type::Infer(x) => new_sg_lit(out, Some((base_indent, vec![x.underscore_token.span.start()])), "_"),
Type::Macro(x) => new_sg_macro(out, base_indent, &x.mac, false),
Type::Never(x) => new_sg_lit(out, Some((base_indent, vec![x.bang_token.span.start()])), "!"),
Type::Paren(x) => {
let mut node = new_sg(out);
node.seg(out, "(");
node.child(x.elem.make_segs(out, base_indent));
node.seg(out, ")");
node.build(out)
},
Type::Path(x) => build_extended_path(out, base_indent, &x.qself, &x.path),
Type::Ptr(x) => {
let mut node = new_sg(out);
let mut prefix = String::new();
prefix.write_str("*").unwrap();
if x.const_token.is_some() {
prefix.write_str("const ").unwrap();
}
if x.mutability.is_some() {
prefix.write_str("mut ").unwrap();
}
node.seg(out, prefix);
node.child(x.elem.make_segs(out, base_indent));
node.build(out)
},
Type::Reference(x) => {
let mut node = new_sg(out);
append_whitespace(out, base_indent, &mut node, x.and_token.span.start());
node.seg(out, "&");
if let Some(l) = &x.lifetime {
append_whitespace(out, base_indent, &mut node, l.span().start());
node.seg(out, format!("{} ", l));
}
if let Some(m) = &x.mutability {
append_whitespace(out, base_indent, &mut node, m.span.start());
node.seg(out, "mut ");
}
node.child(x.elem.make_segs(out, base_indent));
node.build(out)
},
Type::Slice(x) => {
let mut node = new_sg(out);
node.seg(out, "[");
node.child(x.elem.make_segs(out, base_indent));
node.seg(out, "]");
node.build(out)
},
Type::TraitObject(x) => {
let mut node = new_sg(out);
append_binary(out, base_indent, &mut node, "dyn", |out: &mut MakeSegsState, base_indent: &Alignment| {
let mut node = new_sg(out);
append_inline_list(out, base_indent, &mut node, " +", &x.bounds, InlineListSuffix::<Expr>::None);
node.build(out)
});
node.build(out)
},
Type::Tuple(x) => new_sg_bracketed_list(
out,
base_indent,
x.paren_token.span.open().start(),
"(",
false,
",",
&x.elems,
InlineListSuffix::UnitPunct::<Expr>,
x.paren_token.span.close().start(),
")",
),
Type::Verbatim(x) => new_sg_lit(out, None, x),
_ => unreachable!(),
}
}
}
impl Formattable for BareFnArg {
fn has_attrs(&self) -> bool {
!self.attrs.is_empty()
}
fn make_segs(&self, out: &mut MakeSegsState, base_indent: &Alignment) -> SplitGroupIdx {
new_sg_outer_attrs(
out,
base_indent,
&self.attrs,
self.span(),
|out: &mut MakeSegsState, base_indent: &Alignment| {
if let Some(name) = &self.name {
let mut node = new_sg(out);
node.seg(out, format!("{}: ", name.0));
node.child(self.ty.make_segs(out, base_indent));
node.build(out)
} else {
self.ty.make_segs(out, base_indent)
}
},
)
}
}
impl Formattable for FnArg {
fn has_attrs(&self) -> bool {
match self {
FnArg::Receiver(x) => !x.attrs.is_empty(),
FnArg::Typed(x) => !x.attrs.is_empty(),
}
}
fn make_segs(&self, out: &mut MakeSegsState, base_indent: &Alignment) -> SplitGroupIdx {
match self {
FnArg::Receiver(x) => new_sg_outer_attrs(
out,
base_indent,
&x.attrs,
self.span(),
|out: &mut MakeSegsState, base_indent: &Alignment| {
let build_self = |out: &mut MakeSegsState, base_indent: &Alignment| {
let mut sg = new_sg(out);
let mut need_space = false;
if let Some(y) = &x.reference {
append_whitespace(out, base_indent, &mut sg, y.0.span.start());
sg.seg(out, "&");
if let Some(lt) = &y.1 {
append_whitespace(out, base_indent, &mut sg, lt.apostrophe.start());
sg.seg(out, lt.to_string());
need_space = true;
}
}
if let Some(y) = &x.mutability {
append_whitespace(out, base_indent, &mut sg, y.span.start());
sg.seg(out, format!("{}mut", if need_space {
" "
} else {
""
}));
need_space = true;
}
append_whitespace(out, base_indent, &mut sg, x.self_token.span.start());
sg.seg(out, format!("{}self", if need_space {
" "
} else {
""
}));
sg.build(out)
};
if let Some(colon) = &x.colon_token {
return new_sg_binary(out, base_indent, build_self, colon.span.start(), ":", x.ty.as_ref());
} else {
return build_self(out, base_indent);
}
},
),
FnArg::Typed(x) => new_sg_outer_attrs(
out,
base_indent,
&x.attrs,
self.span(),
|out: &mut MakeSegsState, base_indent: &Alignment| {
new_sg_binary(out, base_indent, x.pat.as_ref(), x.colon_token.span.start(), ":", x.ty.as_ref())
},
),
}
}
}
impl Formattable for GenericArgument {
fn has_attrs(&self) -> bool {
false
}
fn make_segs(&self, out: &mut MakeSegsState, base_indent: &Alignment) -> SplitGroupIdx {
#[deny(clippy::wildcard_enum_match_arm)]
match self {
GenericArgument::Lifetime(l) => l.make_segs(out, base_indent),
GenericArgument::Type(t) => t.make_segs(out, base_indent),
GenericArgument::Const(c) => c.make_segs(out, base_indent),
GenericArgument::AssocType(b) => new_sg_binary(
out,
base_indent,
|out: &mut MakeSegsState, base_indent: &Alignment| {
let mut sg = new_sg(out);
sg.child(b.ident.make_segs(out, base_indent));
if let Some(g) = &b.generics {
append_angle_bracketed_generics(out, base_indent, &mut sg, g);
}
sg.build(out)
},
b.eq_token.span.start(),
" =",
&b.ty,
),
GenericArgument::AssocConst(b) => new_sg_binary(
out,
base_indent,
|out: &mut MakeSegsState, base_indent: &Alignment| {
let mut sg = new_sg(out);
sg.child(b.ident.make_segs(out, base_indent));
if let Some(g) = &b.generics {
append_angle_bracketed_generics(out, base_indent, &mut sg, g);
}
sg.build(out)
},
b.eq_token.span.start(),
" =",
&b.value,
),
GenericArgument::Constraint(c) => {
let mut node = new_sg(out);
node.seg(out, &c.ident);
append_binary(out, base_indent, &mut node, ":", |out: &mut MakeSegsState, base_indent: &Alignment| {
let mut node = new_sg(out);
append_inline_list(out, base_indent, &mut node, " +", &c.bounds, InlineListSuffix::<Expr>::None);
node.build(out)
});
node.build(out)
},
_ => todo!(),
}
}
}
impl Formattable for GenericParam {
fn has_attrs(&self) -> bool {
false
}
fn make_segs(&self, out: &mut MakeSegsState, base_indent: &Alignment) -> SplitGroupIdx {
match self {
GenericParam::Type(t) => new_sg_outer_attrs(
out,
base_indent,
&t.attrs,
self.span(),
|out: &mut MakeSegsState, base_indent: &Alignment| {
let build_base = |out: &mut MakeSegsState, base_indent: &Alignment| {
let mut sg = new_sg(out);
append_whitespace(out, base_indent, &mut sg, t.ident.span().start());
sg.seg(out, &t.ident);
if t.colon_token.is_some() && !t.bounds.is_empty() {
sg.seg(out, ": ");
append_inline_list(
out,
base_indent,
&mut sg,
" +",
&t.bounds,
InlineListSuffix::<Expr>::None,
);
}
sg.build(out)
};
if let Some(def) = &t.default {
new_sg_binary(out, base_indent, build_base, t.eq_token.unwrap().span.start(), " =", def)
} else {
build_base(out, base_indent)
}
},
),
GenericParam::Lifetime(l) => l.make_segs(out, base_indent),
GenericParam::Const(c) => new_sg_outer_attrs(
out,
base_indent,
&c.attrs,
self.span(),
|out: &mut MakeSegsState, base_indent: &Alignment| {
let build_base = |out: &mut MakeSegsState, base_indent: &Alignment| {
let mut sg = new_sg(out);
append_whitespace(out, base_indent, &mut sg, c.const_token.span.start());
sg.seg(out, "const ");
append_whitespace(out, base_indent, &mut sg, c.ident.span().start());
sg.seg(out, &c.ident.to_string());
sg.seg(out, ": ");
sg.child(c.ty.make_segs(out, base_indent));
sg.build(out)
};
if let Some(def) = &c.default {
new_sg_binary(out, base_indent, build_base, c.eq_token.unwrap().span.start(), " =", def)
} else {
build_base(out, base_indent)
}
},
),
}
}
}
impl Formattable for LifetimeParam {
fn has_attrs(&self) -> bool {
!self.attrs.is_empty()
}
fn make_segs(&self, out: &mut MakeSegsState, base_indent: &Alignment) -> SplitGroupIdx {
new_sg_outer_attrs(
out,
base_indent,
&self.attrs,
self.span(),
|out: &mut MakeSegsState, base_indent: &Alignment| {
let mut node = new_sg(out);
node.seg(out, &self.lifetime);
if self.colon_token.is_some() {
append_binary(
out,
base_indent,
&mut node,
":",
|out: &mut MakeSegsState, base_indent: &Alignment| {
let mut node = new_sg(out);
append_inline_list(
out,
base_indent,
&mut node,
" +",
&self.bounds,
InlineListSuffix::None::<Expr>,
);
node.build(out)
},
);
}
node.build(out)
},
)
}
}
impl Formattable for syn::CapturedParam {
fn has_attrs(&self) -> bool {
return false;
}
fn make_segs(&self, out: &mut MakeSegsState, base_indent: &Alignment) -> SplitGroupIdx {
#[deny(clippy::wildcard_enum_match_arm)]
match self {
syn::CapturedParam::Lifetime(lifetime) => {
return lifetime.make_segs(out, base_indent);
},
syn::CapturedParam::Ident(ident) => {
return ident.make_segs(out, base_indent);
},
_ => unreachable!(),
}
}
}
impl Formattable for syn::Lifetime {
fn has_attrs(&self) -> bool {
false
}
fn make_segs(&self, out: &mut MakeSegsState, base_indent: &Alignment) -> SplitGroupIdx {
new_sg_lit(out, Some((base_indent, vec![self.apostrophe.start()])), self)
}
}
impl Formattable for Type {
fn has_attrs(&self) -> bool {
(&self).has_attrs()
}
fn make_segs(&self, out: &mut MakeSegsState, base_indent: &Alignment) -> SplitGroupIdx {
(&self).make_segs(out, base_indent)
}
}
impl Formattable for TypeParamBound {
fn has_attrs(&self) -> bool {
false
}
fn make_segs(&self, out: &mut MakeSegsState, base_indent: &Alignment) -> SplitGroupIdx {
let mut sg = new_sg(out);
#[deny(clippy::wildcard_enum_match_arm)]
match self {
syn::TypeParamBound::Trait(t) => {
if t.paren_token.is_some() {
sg.seg(out, "(");
}
match t.modifier {
syn::TraitBoundModifier::None => { },
syn::TraitBoundModifier::Maybe(m) => {
append_whitespace(out, base_indent, &mut sg, m.span.start());
sg.seg(out, "?");
},
}
if let Some(hot) = &t.lifetimes {
append_whitespace(out, base_indent, &mut sg, hot.for_token.span.start());
sg.seg(out, "for");
append_bracketed_list_common(
out,
base_indent,
&mut sg,
hot.lt_token.span.start(),
"<",
&hot.lifetimes,
hot.gt_token.span.start(),
"> ",
);
}
append_path(
out,
&mut sg,
base_indent,
t.path.leading_colon.map(|t| Some(t.spans[0].start())),
t.path.segments.pairs(),
);
if t.paren_token.is_some() {
sg.seg(out, ")");
}
},
syn::TypeParamBound::Lifetime(l) => {
sg.seg(out, l.to_string());
},
TypeParamBound::PreciseCapture(p) => {
append_whitespace(out, base_indent, &mut sg, p.use_token.span.start());
sg.seg(out, p.use_token.to_token_stream().to_string());
append_bracketed_list_common(
out,
base_indent,
&mut sg,
p.lt_token.span.start(),
"<",
&p.params,
p.gt_token.span.start(),
"> ",
);
},
TypeParamBound::Verbatim(x) => {
new_sg_lit(out, None, x);
},
_ => unreachable!(),
}
sg.build(out)
}
}
impl Formattable for WherePredicate {
fn has_attrs(&self) -> bool {
false
}
fn make_segs(&self, out: &mut MakeSegsState, base_indent: &Alignment) -> SplitGroupIdx {
#[deny(clippy::wildcard_enum_match_arm)]
match self {
WherePredicate::Type(t) => {
let mut sg = new_sg(out);
if let Some(hot) = &t.lifetimes {
append_whitespace(out, base_indent, &mut sg, hot.for_token.span.start());
sg.seg(out, "for");
append_bracketed_list_common(
out,
base_indent,
&mut sg,
hot.lt_token.span.start(),
"<",
&hot.lifetimes,
hot.gt_token.span.start(),
"> ",
);
}
sg.child(t.bounded_ty.make_segs(out, base_indent));
sg.seg(out, ":");
sg.seg_unsplit(out, " ");
append_inline_list(out, base_indent, &mut sg, " +", &t.bounds, InlineListSuffix::<Expr>::None);
sg.build(out)
},
WherePredicate::Lifetime(l) => {
let mut sg = new_sg(out);
sg.seg(out, &l.lifetime);
sg.seg(out, ":");
sg.seg_unsplit(out, " ");
append_inline_list(out, base_indent, &mut sg, " +", &l.bounds, InlineListSuffix::<Expr>::None);
sg.build(out)
},
_ => unreachable!(),
}
}
}