use std::{
cmp::Ordering,
collections::{BTreeMap, HashSet},
};
use indexmap::IndexMap;
use itertools::Itertools as _;
use pyo3_stub_gen::{
generate::{
ClassDef, EnumDef, MemberDef, MethodDef, MethodType, Module, Parameter, ParameterDefault,
Parameters,
},
type_info::{DeprecatedInfo, IgnoreTarget, ParameterKind},
StubInfo, TypeInfo,
};
pub fn sort(stub: &mut StubInfo) {
let StubInfo {
modules,
python_root: _,
} = stub;
<BTreeMap<String, Module>>::values_mut(modules).for_each(sort_module);
}
trait ArbitraryOrd {
#[must_use]
fn cmp(&self, other: &Self) -> Ordering;
}
struct Arbitrary<'a, T>(&'a T);
impl<T: ArbitraryOrd> PartialEq for Arbitrary<'_, T> {
fn eq(&self, other: &Self) -> bool {
self.cmp(other) == Ordering::Equal
}
}
impl<T: ArbitraryOrd> Eq for Arbitrary<'_, T> {}
impl<T: ArbitraryOrd> PartialOrd for Arbitrary<'_, T> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<T: ArbitraryOrd> Ord for Arbitrary<'_, T> {
fn cmp(&self, other: &Self) -> Ordering {
self.0.cmp(other.0)
}
}
macro_rules! arbitrary_ord_structs {
($(
$struct:ident { $($field:ident),* $(,)? };
)*) => {
$(
#[automatically_derived]
impl ArbitraryOrd for $struct {
fn cmp(&self, other: &Self) -> Ordering {
let $struct { $($field),* } = self;
let result = Ordering::Equal;
$(
let result = $field.cmp(&other.$field);
if result != Ordering::Equal {
return result;
}
)*
return result;
}
}
)*
}
}
impl<T: Ord> ArbitraryOrd for HashSet<T> {
fn cmp(&self, other: &Self) -> Ordering {
let self_sorted: Vec<_> = self.iter().sorted().collect();
let other_sorted: Vec<_> = other.iter().sorted().collect();
self_sorted.cmp(&other_sorted)
}
}
impl<T: ArbitraryOrd> ArbitraryOrd for Option<T> {
fn cmp(&self, other: &Self) -> Ordering {
match (self, other) {
(None, None) => Ordering::Equal,
(None, Some(_)) => Ordering::Less,
(Some(_), None) => Ordering::Greater,
(Some(left), Some(right)) => left.cmp(right),
}
}
}
impl<T: ArbitraryOrd> ArbitraryOrd for (T, T) {
fn cmp(&self, other: &Self) -> Ordering {
self.0.cmp(&other.0).then_with(|| self.1.cmp(&other.1))
}
}
impl<T: ArbitraryOrd> ArbitraryOrd for &T {
fn cmp(&self, other: &Self) -> Ordering {
(*self).cmp(*other)
}
}
impl<T: ArbitraryOrd> ArbitraryOrd for Vec<T> {
fn cmp<'a>(&'a self, other: &'a Self) -> Ordering {
let sort = |vec: &'a Self| -> Vec<_> {
vec.iter()
.sorted_by(ArbitraryOrd::cmp)
.map(Arbitrary)
.collect()
};
sort(self).cmp(&sort(other))
}
}
impl<K: Ord, V: ArbitraryOrd> ArbitraryOrd for IndexMap<K, V> {
fn cmp<'a>(&'a self, other: &'a Self) -> Ordering {
let sort = |map: &'a Self| -> Vec<_> {
map.iter()
.sorted_by(|(lk, _), (rk, _)| lk.cmp(rk))
.map(|(k, v)| (k, Arbitrary(v)))
.collect()
};
sort(self).cmp(&sort(other))
}
}
impl ArbitraryOrd for IgnoreTarget {
fn cmp(&self, other: &Self) -> Ordering {
match (self, other) {
(Self::All, Self::All) => Ordering::Equal,
(Self::All, Self::Specified(_)) => Ordering::Less,
(Self::Specified(_), Self::All) => Ordering::Greater,
(Self::Specified(left), Self::Specified(right)) => left.cmp(right),
}
}
}
impl ArbitraryOrd for MethodType {
fn cmp(&self, other: &Self) -> Ordering {
match (self, other) {
(Self::Instance, Self::Instance)
| (Self::Static, Self::Static)
| (Self::Class, Self::Class)
| (Self::New, Self::New) => Ordering::Equal,
(Self::Instance | Self::Static | Self::Class, _) => Ordering::Less,
(_, Self::Instance | Self::Static | Self::Class) => Ordering::Greater,
}
}
}
impl ArbitraryOrd for ParameterKind {
fn cmp(&self, other: &Self) -> Ordering {
match (self, other) {
(Self::PositionalOnly, Self::PositionalOnly)
| (Self::PositionalOrKeyword, Self::PositionalOrKeyword)
| (Self::KeywordOnly, Self::KeywordOnly)
| (Self::VarPositional, Self::VarPositional)
| (Self::VarKeyword, Self::VarKeyword) => Ordering::Equal,
(
Self::PositionalOnly
| Self::PositionalOrKeyword
| Self::KeywordOnly
| Self::VarPositional,
_,
) => Ordering::Less,
(
_,
Self::PositionalOrKeyword
| Self::PositionalOnly
| Self::KeywordOnly
| Self::VarPositional,
) => Ordering::Greater,
}
}
}
impl ArbitraryOrd for ParameterDefault {
fn cmp(&self, other: &Self) -> Ordering {
match (self, other) {
(Self::None, Self::None) => Ordering::Equal,
(Self::None, _) => Ordering::Less,
(_, Self::None) => Ordering::Greater,
(Self::Expr(x), Self::Expr(y)) => x.cmp(y),
}
}
}
arbitrary_ord_structs! {
TypeInfo { name, import };
MemberDef { name, r#type, doc, default, deprecated };
MethodDef { name, parameters, r#return, doc, r#type, is_async, deprecated, type_ignored, is_overload };
DeprecatedInfo { since, note };
ClassDef { name, doc, attrs, getter_setters, methods, bases, classes, match_args, subclass };
Parameter { name, kind, type_info, default };
Parameters { positional_only, positional_or_keyword, keyword_only, varargs, varkw };
}
fn cmp_strings<T>(k1: &String, _: &T, k2: &String, _: &T) -> Ordering {
k1.cmp(k2)
}
fn sort_class(class: &mut ClassDef) {
let ClassDef {
name: _, doc: _,
attrs: _, bases: _, classes,
match_args: _, subclass: _,
methods, getter_setters,
} = class;
methods.sort_by(cmp_strings);
getter_setters.sort_by(cmp_strings);
classes.iter_mut().for_each(sort_class);
classes.sort_by(ArbitraryOrd::cmp);
}
fn sort_enum(r#enum: &mut EnumDef) {
let EnumDef {
name: _, doc: _,
variants: _, methods,
attrs: _, getters,
setters,
} = r#enum;
methods.sort_by(ArbitraryOrd::cmp);
getters.sort_by(ArbitraryOrd::cmp);
setters.sort_by(ArbitraryOrd::cmp);
}
fn sort_module(module: &mut Module) {
let Module {
name: _, doc: _,
default_module_name: _,
class,
enum_,
function: _,
variables: _,
submodules: _,
} = module;
class.values_mut().for_each(sort_class);
enum_.values_mut().for_each(sort_enum);
}