feat(codegen): derive Eq and Hash for most enum types

This commit is contained in:
pyrite 2025-05-27 19:56:03 +02:00
parent 1af37bea89
commit 3ad92fb8c8
Signed by: pyrite
GPG key ID: 7F1BA9170CD35D15
4 changed files with 583 additions and 277 deletions

View file

@ -8,7 +8,7 @@ use crate::openapi::{
r#type::{OpenApiType, OpenApiVariants}, r#type::{OpenApiType, OpenApiVariants},
}; };
use super::{object::PrimitiveType, ResolvedSchema}; use super::{object::PrimitiveType, Model, ResolvedSchema};
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EnumRepr { pub enum EnumRepr {
@ -143,6 +143,18 @@ impl EnumVariantTupleValue {
}, },
} }
} }
pub fn is_comparable(&self, resolved: &ResolvedSchema) -> bool {
match self {
Self::Primitive(_) => true,
Self::Enum { inner, .. } => inner.is_comparable(resolved),
Self::Ref { ty_name } | Self::ArrayOfRefs { ty_name } => resolved
.models
.get(ty_name)
.map(|m| matches!(m, Model::Newtype(_)))
.unwrap_or_default(),
}
}
} }
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
@ -173,6 +185,13 @@ impl EnumVariantValue {
} }
} }
pub fn is_comparable(&self, resolved: &ResolvedSchema) -> bool {
match self {
Self::Repr(_) | Self::String { .. } => true,
Self::Tuple(values) => values.iter().all(|v| v.is_comparable(resolved)),
}
}
pub fn codegen_display(&self, name: &str) -> Option<TokenStream> { pub fn codegen_display(&self, name: &str) -> Option<TokenStream> {
let variant = format_ident!("{name}"); let variant = format_ident!("{name}");
@ -417,6 +436,12 @@ impl Enum {
self.variants.iter().all(|v| v.value.is_display(resolved)) self.variants.iter().all(|v| v.value.is_display(resolved))
} }
pub fn is_comparable(&self, resolved: &ResolvedSchema) -> bool {
self.variants
.iter()
.all(|v| v.value.is_comparable(resolved))
}
pub fn codegen(&self, resolved: &ResolvedSchema) -> Option<TokenStream> { pub fn codegen(&self, resolved: &ResolvedSchema) -> Option<TokenStream> {
let repr = self.repr.map(|r| match r { let repr = self.repr.map(|r| match r {
EnumRepr::U8 => quote! { #[repr(u8)]}, EnumRepr::U8 => quote! { #[repr(u8)]},
@ -458,7 +483,11 @@ impl Enum {
} }
if self.copy { if self.copy {
derives.push(quote! { Copy, Hash }); derives.push(quote! { Copy });
}
if self.is_comparable(resolved) {
derives.push(quote! { Eq, Hash });
} }
let serde_attr = self.untagged.then(|| { let serde_attr = self.untagged.then(|| {

View file

@ -315,7 +315,7 @@ The default value [Self::{}](self::{}#variant.{})"#,
let inner_ty = items.codegen_type_name(&inner_name); let inner_ty = items.codegen_type_name(&inner_name);
code.extend(quote! { code.extend(quote! {
#[derive(Debug, Clone)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct #name(pub Vec<#inner_ty>); pub struct #name(pub Vec<#inner_ty>);
impl std::fmt::Display for #name { impl std::fmt::Display for #name {

View file

@ -3,7 +3,7 @@
"info": { "info": {
"title": "Torn API", "title": "Torn API",
"description": "\n * The development of Torn's API v2 is still ongoing.\n * If selections remain unaltered, they will default to the API v1 version.\n * Unlike API v1, API v2 accepts both selections and IDs as path and query parameters.\n * If any discrepancies or errors are found, please submit a [bug report](https://www.torn.com/forums.php#/p=forums&f=19&b=0&a=0) on the Torn Forums.\n * In case you're using bots to check for changes on openapi.json file, make sure to specificy a custom user-agent header - CloudFlare sometimes prevents requests from default user-agents.", "description": "\n * The development of Torn's API v2 is still ongoing.\n * If selections remain unaltered, they will default to the API v1 version.\n * Unlike API v1, API v2 accepts both selections and IDs as path and query parameters.\n * If any discrepancies or errors are found, please submit a [bug report](https://www.torn.com/forums.php#/p=forums&f=19&b=0&a=0) on the Torn Forums.\n * In case you're using bots to check for changes on openapi.json file, make sure to specificy a custom user-agent header - CloudFlare sometimes prevents requests from default user-agents.",
"version": "1.7.0" "version": "1.8.0"
}, },
"servers": [ "servers": [
{ {
@ -1188,12 +1188,7 @@
"description": "Category of races returned", "description": "Category of races returned",
"required": false, "required": false,
"schema": { "schema": {
"type": "string", "$ref": "#/components/schemas/RacingRaceTypeEnum"
"default": "custom",
"enum": [
"official",
"custom"
]
} }
}, },
{ {
@ -1504,8 +1499,18 @@
"description": "selection id", "description": "selection id",
"required": false, "required": false,
"schema": { "schema": {
"oneOf": [
{
"$ref": "#/components/schemas/UserId"
},
{
"$ref": "#/components/schemas/TornCrimeId"
},
{
"type": "string" "type": "string"
} }
]
}
}, },
{ {
"$ref": "#/components/parameters/ApiLimit" "$ref": "#/components/parameters/ApiLimit"
@ -1522,10 +1527,23 @@
{ {
"name": "cat", "name": "cat",
"in": "query", "in": "query",
"description": "Selection category", "description": "Selection category. Can belong to one of the specified types.",
"required": false, "required": false,
"schema": { "schema": {
"type": "string" "oneOf": [
{
"$ref": "#/components/schemas/ReportTypeEnum"
},
{
"$ref": "#/components/schemas/UserListEnum"
},
{
"$ref": "#/components/schemas/PersonalStatsCategoryEnum"
},
{
"$ref": "#/components/schemas/RacingRaceTypeEnum"
}
]
} }
}, },
{ {
@ -1533,8 +1551,13 @@
"in": "query", "in": "query",
"description": "Selection stat", "description": "Selection stat",
"required": false, "required": false,
"style": "form",
"explode": false,
"schema": { "schema": {
"type": "string" "type": "array",
"items": {
"$ref": "#/components/schemas/PersonalStatsStatName"
}
} }
}, },
{ {
@ -3021,6 +3044,51 @@
"x-stability": "Stable" "x-stability": "Stable"
} }
}, },
"/faction/search": {
"get": {
"tags": [
"Faction"
],
"summary": "Search factions by name or other criteria",
"description": "Requires public access key. <br>This selection is standalone and cannot be used together with other selections.",
"operationId": "01c192f9b41ce29372df54667bea2b43",
"parameters": [
{
"$ref": "#/components/parameters/ApiName"
},
{
"$ref": "#/components/parameters/ApiFactionSearchFilter"
},
{
"$ref": "#/components/parameters/ApiTimestamp"
},
{
"$ref": "#/components/parameters/ApiComment"
},
{
"$ref": "#/components/parameters/ApiKeyPublic"
}
],
"responses": {
"200": {
"description": "Successful operation",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/FactionSearchResponse"
}
}
}
}
},
"security": [
{
"api_key": []
}
],
"x-stability": "Unstable"
}
},
"/faction/stats": { "/faction/stats": {
"get": { "get": {
"tags": [ "tags": [
@ -4644,71 +4712,6 @@
"x-stability": "Stable" "x-stability": "Stable"
} }
}, },
"/racing/races": {
"get": {
"tags": [
"Racing"
],
"summary": "Get races",
"description": "Requires public access key. <br>Returns a list of races, ordered by race start timestamp.",
"operationId": "4be921a67d32b5e82c68835ef56175d0",
"parameters": [
{
"$ref": "#/components/parameters/ApiLimit100"
},
{
"$ref": "#/components/parameters/ApiSortDesc"
},
{
"$ref": "#/components/parameters/ApiTo"
},
{
"$ref": "#/components/parameters/ApiFrom"
},
{
"name": "cat",
"in": "query",
"description": "Category of races returned",
"required": false,
"schema": {
"type": "string",
"default": "custom",
"enum": [
"official",
"custom"
]
}
},
{
"$ref": "#/components/parameters/ApiTimestamp"
},
{
"$ref": "#/components/parameters/ApiComment"
},
{
"$ref": "#/components/parameters/ApiKeyPublic"
}
],
"responses": {
"200": {
"description": "Successful operation",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/RacingRacesResponse"
}
}
}
}
},
"security": [
{
"api_key": []
}
],
"x-stability": "Stable"
}
},
"/racing/{raceId}/race": { "/racing/{raceId}/race": {
"get": { "get": {
"tags": [ "tags": [
@ -4960,7 +4963,14 @@
"description": "selection id", "description": "selection id",
"required": false, "required": false,
"schema": { "schema": {
"type": "string" "oneOf": [
{
"$ref": "#/components/schemas/RaceId"
},
{
"$ref": "#/components/schemas/RaceTrackId"
}
]
} }
}, },
{ {
@ -4981,7 +4991,14 @@
"description": "Selection category", "description": "Selection category",
"required": false, "required": false,
"schema": { "schema": {
"type": "string" "oneOf": [
{
"$ref": "#/components/schemas/RacingRaceTypeEnum"
},
{
"$ref": "#/components/schemas/RaceClassEnum"
}
]
} }
}, },
{ {
@ -5972,7 +5989,26 @@
"description": "selection id", "description": "selection id",
"required": false, "required": false,
"schema": { "schema": {
"type": "string" "oneOf": [
{
"$ref": "#/components/schemas/LogCategoryId"
},
{
"$ref": "#/components/schemas/TornCrimeId"
},
{
"type": "array",
"items": {
"$ref": "#/components/schemas/ItemId"
}
},
{
"type": "array",
"items": {
"$ref": "#/components/schemas/FactionTerritoryEnum"
}
}
]
} }
}, },
{ {
@ -5996,7 +6032,17 @@
"description": "Selection category", "description": "Selection category",
"required": false, "required": false,
"schema": { "schema": {
"type": "string" "oneOf": [
{
"$ref": "#/components/schemas/TornFactionHofCategory"
},
{
"$ref": "#/components/schemas/TornHofCategory"
},
{
"$ref": "#/components/schemas/TornItemCategory"
}
]
} }
}, },
{ {
@ -10310,6 +10356,13 @@
"peace" "peace"
] ]
}, },
"RacingRaceTypeEnum": {
"type": "string",
"enum": [
"official",
"custom"
]
},
"FactionPositionAbilityEnum": { "FactionPositionAbilityEnum": {
"type": "string", "type": "string",
"enum": [ "enum": [
@ -10908,6 +10961,24 @@
7 7
] ]
}, },
"Parameters": {
"type": "string",
"oneOf": [
{
"type": "string",
"enum": [
"destroyed",
"notDestroyed",
"recruiting",
"notRecruiting"
]
},
{
"type": "string",
"pattern": "^(id|respect|members)(Equal|NotEqual|Less|LessOrEqual|GreaterOrEqual|Greater)\\d+$"
}
]
},
"ReviveSetting": { "ReviveSetting": {
"type": "string", "type": "string",
"enum": [ "enum": [
@ -12143,6 +12214,22 @@
}, },
"type": "object" "type": "object"
}, },
"SelectionCategoryEnum": {
"oneOf": [
{
"$ref": "#/components/schemas/ReportTypeEnum"
},
{
"$ref": "#/components/schemas/UserListEnum"
},
{
"$ref": "#/components/schemas/PersonalStatsCategoryEnum"
},
{
"$ref": "#/components/schemas/RacingRaceTypeEnum"
}
]
},
"UserCurrentEducation": { "UserCurrentEducation": {
"required": [ "required": [
"id", "id",
@ -13538,6 +13625,8 @@
"type": "object" "type": "object"
}, },
"UserSelectionName": { "UserSelectionName": {
"oneOf": [
{
"description": "The following selections will fallback to API v1 and may change at any time: 'ammo','bars','basic','battlestats','bazaar','cooldowns','criminalrecord','discord','display','education','equipment','events','gym','honors','icons','inventory','jobpoints','log','medals','merits','messages','missions','money','networth','newevents','newmessages','notifications','perks','profile','properties','refills','reports','skills','stocks','travel','weaponexp','workstats'.", "description": "The following selections will fallback to API v1 and may change at any time: 'ammo','bars','basic','battlestats','bazaar','cooldowns','criminalrecord','discord','display','education','equipment','events','gym','honors','icons','inventory','jobpoints','log','medals','merits','messages','missions','money','networth','newevents','newmessages','notifications','perks','profile','properties','refills','reports','skills','stocks','travel','weaponexp','workstats'.",
"type": "string", "type": "string",
"enum": [ "enum": [
@ -13603,6 +13692,11 @@
"workstats" "workstats"
] ]
}, },
{
"type": "string"
}
]
},
"UserLookupResponse": { "UserLookupResponse": {
"required": [ "required": [
"selections" "selections"
@ -16680,6 +16774,120 @@
}, },
"type": "object" "type": "object"
}, },
"FactionSearchLeader": {
"required": [
"id",
"name"
],
"properties": {
"id": {
"$ref": "#/components/schemas/UserId"
},
"name": {
"type": "string"
}
},
"type": "object"
},
"FactionSearch": {
"required": [
"id",
"name",
"respect",
"members",
"leader",
"co_leader",
"image",
"tag_image",
"tag",
"is_destroyed",
"is_recruiting"
],
"properties": {
"id": {
"$ref": "#/components/schemas/FactionId"
},
"name": {
"type": "string"
},
"respect": {
"type": "integer",
"format": "int32"
},
"members": {
"type": "integer",
"format": "int32"
},
"leader": {
"$ref": "#/components/schemas/FactionSearchLeader"
},
"co_leader": {
"oneOf": [
{
"$ref": "#/components/schemas/FactionSearchLeader"
},
{
"type": "null"
}
]
},
"image": {
"oneOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"tag_image": {
"oneOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"tag": {
"oneOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"is_destroyed": {
"type": "boolean"
},
"is_recruiting": {
"type": "boolean"
}
},
"type": "object"
},
"FactionSearchResponse": {
"required": [
"search",
"_metadata"
],
"properties": {
"search": {
"type": "array",
"items": {
"$ref": "#/components/schemas/FactionSearch"
}
},
"_metadata": {
"$ref": "#/components/schemas/RequestMetadataWithLinks"
}
},
"type": "object"
},
"FactionTerritoryWarFinished": { "FactionTerritoryWarFinished": {
"required": [ "required": [
"id", "id",
@ -18905,7 +19113,9 @@
"type": "object" "type": "object"
}, },
"FactionSelectionName": { "FactionSelectionName": {
"description": "The following selections will fallback to API v1 and may change at any time: 'armor', 'boosters', 'caches', 'cesium', 'crimeexp', 'drugs', 'medical', 'positions', 'reports', 'temporary', 'weapons'.\n * The following selections are not available in API v2: 'armorynews', 'attacknews', 'crimenews', 'currency', 'donations', 'fundsnews', 'mainnews', 'membershipnews', 'territorynews'.", "oneOf": [
{
"description": "The following selections will fallback to API v1 and may change at any time: 'armor', 'boosters', 'caches', 'cesium', 'crimeexp', 'drugs', 'medical', 'temporary', 'weapons'.\n * The following selections are not available in API v2: 'armorynews', 'attacknews', 'crimenews', 'currency', 'donations', 'fundsnews', 'mainnews', 'membershipnews', 'territorynews'.",
"type": "string", "type": "string",
"enum": [ "enum": [
"applications", "applications",
@ -18923,11 +19133,14 @@
"lookup", "lookup",
"members", "members",
"news", "news",
"positions",
"rackets", "rackets",
"rankedwars", "rankedwars",
"rankedwarreport", "rankedwarreport",
"reports",
"revives", "revives",
"revivesfull", "revivesfull",
"search",
"stats", "stats",
"territory", "territory",
"territoryownership", "territoryownership",
@ -18943,19 +19156,13 @@
"crimeexp", "crimeexp",
"drugs", "drugs",
"medical", "medical",
"positions",
"reports",
"temporary", "temporary",
"weapons", "weapons"
"armorynews", ]
"attacknews", },
"crimenews", {
"currency", "type": "string"
"donations", }
"fundsnews",
"mainnews",
"membershipnews",
"territorynews"
] ]
}, },
"FactionLookupResponse": { "FactionLookupResponse": {
@ -19669,6 +19876,8 @@
"type": "object" "type": "object"
}, },
"ForumSelectionName": { "ForumSelectionName": {
"oneOf": [
{
"type": "string", "type": "string",
"enum": [ "enum": [
"categories", "categories",
@ -19679,6 +19888,11 @@
"timestamp" "timestamp"
] ]
}, },
{
"type": "string"
}
]
},
"ForumLookupResponse": { "ForumLookupResponse": {
"required": [ "required": [
"selections" "selections"
@ -19725,6 +19939,9 @@
"type": "integer", "type": "integer",
"format": "int64" "format": "int64"
}, },
{
"type": "string"
},
{ {
"type": "null" "type": "null"
} }
@ -19882,12 +20099,19 @@
"type": "object" "type": "object"
}, },
"KeySelectionName": { "KeySelectionName": {
"oneOf": [
{
"type": "string", "type": "string",
"enum": [ "enum": [
"info", "info",
"log" "log"
] ]
}, },
{
"type": "string"
}
]
},
"ItemMarketListingItemBonus": { "ItemMarketListingItemBonus": {
"required": [ "required": [
"id", "id",
@ -20102,6 +20326,8 @@
"type": "object" "type": "object"
}, },
"MarketSelectionName": { "MarketSelectionName": {
"oneOf": [
{
"description": "The following selections will fallback to API v1 and may change at any time: 'pointsmarket'.", "description": "The following selections will fallback to API v1 and may change at any time: 'pointsmarket'.",
"type": "string", "type": "string",
"enum": [ "enum": [
@ -20112,6 +20338,11 @@
"bazaar" "bazaar"
] ]
}, },
{
"type": "string"
}
]
},
"MarketLookupResponse": { "MarketLookupResponse": {
"required": [ "required": [
"selections" "selections"
@ -20650,6 +20881,8 @@
"type": "object" "type": "object"
}, },
"RacingSelectionName": { "RacingSelectionName": {
"oneOf": [
{
"type": "string", "type": "string",
"enum": [ "enum": [
"cars", "cars",
@ -20662,6 +20895,11 @@
"tracks" "tracks"
] ]
}, },
{
"type": "string"
}
]
},
"RacingLookupResponse": { "RacingLookupResponse": {
"required": [ "required": [
"selections" "selections"
@ -22078,6 +22316,8 @@
"type": "object" "type": "object"
}, },
"TornSelectionName": { "TornSelectionName": {
"oneOf": [
{
"description": "The following selections will fallback to API v1 and may change at any time: 'bank','cards','cityshops','companies','competition','dirtybombs','gyms','honors','itemdetails','itemstats','medals','organisedcrimes','pawnshop','pokertables','properties','raidreport','raids','rockpaperscissors','searchforcash','shoplifting','stats','stocks'.\n * The following selections are not available in API v2: 'chainreport', 'rackets', 'rankedwarreport', 'rankedwars', 'territorynames', 'territorywarreport', 'territorywars'.", "description": "The following selections will fallback to API v1 and may change at any time: 'bank','cards','cityshops','companies','competition','dirtybombs','gyms','honors','itemdetails','itemstats','medals','organisedcrimes','pawnshop','pokertables','properties','raidreport','raids','rockpaperscissors','searchforcash','shoplifting','stats','stocks'.\n * The following selections are not available in API v2: 'chainreport', 'rackets', 'rankedwarreport', 'rankedwars', 'territorynames', 'territorywarreport', 'territorywars'.",
"type": "string", "type": "string",
"enum": [ "enum": [
@ -22119,14 +22359,12 @@
"searchforcash", "searchforcash",
"shoplifting", "shoplifting",
"stats", "stats",
"stocks", "stocks"
"chainreport", ]
"rackets", },
"rankedwarreport", {
"rankedwars", "type": "string"
"territorynames", }
"territorywarreport",
"territorywars"
] ]
}, },
"TornLookupResponse": { "TornLookupResponse": {
@ -22181,6 +22419,36 @@
"type": "string" "type": "string"
} }
}, },
"ApiFactionSearchFilter": {
"name": "filters",
"in": "query",
"description": "A filtering query parameter allowing a comma-separated list of filters. <br>\n * Each filter can be one of the following:\n * Fixed options: 'destroyed', 'notDestroyed', 'recruiting', 'notRecruiting'\n * Dynamic options: `fieldName`+`condition`+`number`, where:\n * * `fieldName` is one of: `id`, `respect`, `members`\n * * `condition` is one of: `Equal`, `NotEqual`, `Less`, `LessOrEqual`, `GreaterOrEqual`, `Greater`\n * * `number`: any integer value\n * Examples: `filters=destroyed`, `filters=notDestroyed,recruiting`, `filters=respectLessOrEqual20000,idGreater100,notRecruiting`",
"required": false,
"style": "form",
"explode": false,
"schema": {
"type": "array",
"items": {
"schema": "Parameters",
"type": "string",
"oneOf": [
{
"type": "string",
"enum": [
"destroyed",
"notDestroyed",
"recruiting",
"notRecruiting"
]
},
{
"type": "string",
"pattern": "^(id|respect|members)(Equal|NotEqual|Less|LessOrEqual|GreaterOrEqual|Greater)\\d+$"
}
]
}
}
},
"ApiComment": { "ApiComment": {
"name": "comment", "name": "comment",
"in": "query", "in": "query",
@ -22190,6 +22458,15 @@
"type": "string" "type": "string"
} }
}, },
"ApiName": {
"name": "name",
"in": "query",
"description": "Name to search for.",
"required": false,
"schema": {
"type": "string"
}
},
"ApiLimit20": { "ApiLimit20": {
"name": "limit", "name": "limit",
"in": "query", "in": "query",

View file

@ -9,7 +9,7 @@ pub(super) mod test {
use crate::{ use crate::{
executor::{ExecutorExt, ReqwestClient}, executor::{ExecutorExt, ReqwestClient},
models::{ models::{
faction_selection_name::FactionSelectionNameVariant, AttackCode, FactionSelectionName, faction_selection_name::FactionSelectionNameVariant, AttackCode,
PersonalStatsCategoryEnum, PersonalStatsStatName, UserListEnum, PersonalStatsCategoryEnum, PersonalStatsStatName, UserListEnum,
}, },
}; };