use crate::syntax::map::UnorderedMap;
use crate::syntax::set::{OrderedSet as Set, UnorderedSet};
use crate::syntax::{Api, Enum, ExternFn, NamedType, Pair, Struct, Type};
use proc_macro2::Ident;
use std::fmt::{self, Display};
#[derive(Copy, Clone)]
pub(crate) enum TrivialReason<'a> {
StructField(&'a Struct),
FunctionArgument(&'a ExternFn),
FunctionReturn(&'a ExternFn),
BoxTarget,
VecElement,
SliceElement { mutable: bool },
UnpinnedMut(&'a ExternFn),
}
pub(crate) fn required_trivial_reasons<'a>(
apis: &'a [Api],
all: &Set<&'a Type>,
structs: &UnorderedMap<&'a Ident, &'a Struct>,
enums: &UnorderedMap<&'a Ident, &'a Enum>,
cxx: &UnorderedSet<&'a Ident>,
) -> UnorderedMap<&'a Ident, Vec<TrivialReason<'a>>> {
let mut required_trivial = UnorderedMap::new();
let mut insist_extern_types_are_trivial = |ident: &'a NamedType, reason| {
if cxx.contains(&ident.rust)
&& !structs.contains_key(&ident.rust)
&& !enums.contains_key(&ident.rust)
{
required_trivial
.entry(&ident.rust)
.or_insert_with(Vec::new)
.push(reason);
}
};
for api in apis {
match api {
Api::Struct(strct) => {
for field in &strct.fields {
if let Type::Ident(ident) = &field.ty {
let reason = TrivialReason::StructField(strct);
insist_extern_types_are_trivial(ident, reason);
}
}
}
Api::CxxFunction(efn) | Api::RustFunction(efn) => {
if let Some(receiver) = &efn.receiver {
if receiver.mutable && !receiver.pinned {
let reason = TrivialReason::UnpinnedMut(efn);
insist_extern_types_are_trivial(&receiver.ty, reason);
}
}
for arg in &efn.args {
match &arg.ty {
Type::Ident(ident) => {
let reason = TrivialReason::FunctionArgument(efn);
insist_extern_types_are_trivial(ident, reason);
}
Type::Ref(ty) => {
if ty.mutable && !ty.pinned {
if let Type::Ident(ident) = &ty.inner {
let reason = TrivialReason::UnpinnedMut(efn);
insist_extern_types_are_trivial(ident, reason);
}
}
}
_ => {}
}
}
if let Some(ret) = &efn.ret {
match ret {
Type::Ident(ident) => {
let reason = TrivialReason::FunctionReturn(efn);
insist_extern_types_are_trivial(ident, reason);
}
Type::Ref(ty) => {
if ty.mutable && !ty.pinned {
if let Type::Ident(ident) = &ty.inner {
let reason = TrivialReason::UnpinnedMut(efn);
insist_extern_types_are_trivial(ident, reason);
}
}
}
_ => {}
}
}
}
_ => {}
}
}
for ty in all {
match ty {
Type::RustBox(ty) => {
if let Type::Ident(ident) = &ty.inner {
let reason = TrivialReason::BoxTarget;
insist_extern_types_are_trivial(ident, reason);
}
}
Type::RustVec(ty) => {
if let Type::Ident(ident) = &ty.inner {
let reason = TrivialReason::VecElement;
insist_extern_types_are_trivial(ident, reason);
}
}
Type::SliceRef(ty) => {
if let Type::Ident(ident) = &ty.inner {
let reason = TrivialReason::SliceElement {
mutable: ty.mutable,
};
insist_extern_types_are_trivial(ident, reason);
}
}
_ => {}
}
}
required_trivial
}
pub(crate) fn as_what<'a>(name: &'a Pair, reasons: &'a [TrivialReason]) -> impl Display + 'a {
struct Description<'a> {
name: &'a Pair,
reasons: &'a [TrivialReason<'a>],
}
impl<'a> Display for Description<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut field_of = Set::new();
let mut argument_of = Set::new();
let mut return_of = Set::new();
let mut box_target = false;
let mut vec_element = false;
let mut slice_shared_element = false;
let mut slice_mut_element = false;
let mut unpinned_mut = Set::new();
for reason in self.reasons {
match reason {
TrivialReason::StructField(strct) => {
field_of.insert(&strct.name.rust);
}
TrivialReason::FunctionArgument(efn) => {
argument_of.insert(&efn.name.rust);
}
TrivialReason::FunctionReturn(efn) => {
return_of.insert(&efn.name.rust);
}
TrivialReason::BoxTarget => box_target = true,
TrivialReason::VecElement => vec_element = true,
TrivialReason::SliceElement { mutable } => {
if *mutable {
slice_mut_element = true;
} else {
slice_shared_element = true;
}
}
TrivialReason::UnpinnedMut(efn) => {
unpinned_mut.insert(&efn.name.rust);
}
}
}
let mut clauses = Vec::new();
if !field_of.is_empty() {
clauses.push(Clause::Set {
article: "a",
desc: "field of",
set: &field_of,
});
}
if !argument_of.is_empty() {
clauses.push(Clause::Set {
article: "an",
desc: "argument of",
set: &argument_of,
});
}
if !return_of.is_empty() {
clauses.push(Clause::Set {
article: "a",
desc: "return value of",
set: &return_of,
});
}
if box_target {
clauses.push(Clause::Ty1 {
article: "type",
desc: "Box",
param: self.name,
});
}
if vec_element {
clauses.push(Clause::Ty1 {
article: "a",
desc: "vector element in Vec",
param: self.name,
});
}
if slice_shared_element || slice_mut_element {
clauses.push(Clause::Slice {
article: "a",
desc: "slice element in",
shared: slice_shared_element,
mutable: slice_mut_element,
param: self.name,
});
}
if !unpinned_mut.is_empty() {
clauses.push(Clause::Set {
article: "a",
desc: "non-pinned mutable reference in signature of",
set: &unpinned_mut,
});
}
for (i, clause) in clauses.iter().enumerate() {
if i == 0 {
write!(f, "{} ", clause.article())?;
} else if i + 1 < clauses.len() {
write!(f, ", ")?;
} else {
write!(f, " or ")?;
}
clause.fmt(f)?;
}
Ok(())
}
}
enum Clause<'a> {
Set {
article: &'a str,
desc: &'a str,
set: &'a Set<&'a Ident>,
},
Ty1 {
article: &'a str,
desc: &'a str,
param: &'a Pair,
},
Slice {
article: &'a str,
desc: &'a str,
shared: bool,
mutable: bool,
param: &'a Pair,
},
}
impl<'a> Clause<'a> {
fn article(&self) -> &'a str {
match self {
Clause::Set { article, .. }
| Clause::Ty1 { article, .. }
| Clause::Slice { article, .. } => article,
}
}
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Clause::Set {
article: _,
desc,
set,
} => {
write!(f, "{} ", desc)?;
for (i, ident) in set.iter().take(3).enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "`{}`", ident)?;
}
Ok(())
}
Clause::Ty1 {
article: _,
desc,
param,
} => write!(f, "{}<{}>", desc, param.rust),
Clause::Slice {
article: _,
desc,
shared,
mutable,
param,
} => {
write!(f, "{} ", desc)?;
if *shared {
write!(f, "&[{}]", param.rust)?;
}
if *shared && *mutable {
write!(f, " and ")?;
}
if *mutable {
write!(f, "&mut [{}]", param.rust)?;
}
Ok(())
}
}
}
}
Description { name, reasons }
}