{{ const dotUtils = require('./dotUtils.js'); const champions = require('./.champion.json') .filter(({ id }) => id > 0) .sortBy(({ name }) => name); const constName = name => dotUtils.changeCase.constantCase(name).replace(/[^_A-Z0-9]+/g, ''); const constNamePad = 12; const hashFactor = 256; const strHash = (str) => { let h = 0; for (const c of str) h = hashFactor * h + c.charCodeAt(0); return h; }; }}{{= dotUtils.preamble() }} use serde::{ Serialize, Deserialize }; /// League of Legends champions. /// /// The documentation of each const field specifies:
/// NAME (`IDENTIFIER`, ID). /// /// Implements [IntoEnumIterator](super::IntoEnumIterator). #[derive(Serialize, Deserialize)] #[derive(Copy, Clone)] #[derive(PartialEq, Eq, PartialOrd, Ord)] #[serde(transparent)] #[repr(transparent)] pub struct Champion(pub i16); impl Champion { {{ for (const { id, alias, name } of champions) { }} /** {{= `${name} (\`${alias}\`, ${id})`.padEnd(33) }} */ pub const {{= `${constName(name)}:`.padEnd(1 + constNamePad) }} Self = Self({{= id }}); {{ } }} } impl Champion { /// Array containing all Champion variants, ordered by their id value. pub const ALL: [Self; {{= champions.length }}] = [ {{ { const champNames = require('./.champion.json').slice(1).map(({ name }) => `Self::${constName(name)},`.padEnd(8 + constNamePad)); const step = 8; for (let i = 0; i < champNames.length; i += step) { }} {{= champNames.slice(i, i + step).join('').trim() }} {{ } } }} ]; pub const fn is_known(self) -> bool { match self { {{ for (const { name, alias } of champions) { }} Self::{{= constName(name).padEnd(constNamePad) }} => true, {{ } }} _ => false, } } /// The champion's name (`en_US` localization). pub const fn name(self) -> Option<&'static str> { match self { {{ for (const { name } of champions) { }} Self::{{= constName(name).padEnd(constNamePad) }} => Some("{{= name }}"), {{ } }} _ => None, } } /// The champion's identifier key. Somtimes called "key", "identifier", or "alias". /// This is mainly used in DDragon paths. /// /// This is generally the `en_US` name with spaces and punctuation removed, /// capitalization preserved, however the follow are exceptions: /// /// Field | Name | Identifier /// --------|------|----------- {{ for (const { name, alias } of champions) { if (name.replace(/[^a-zA-Z0-9]+/, '') !== alias) { }} /// `{{= constName(name) }}` | "{{= name }}" | "{{= alias }}" {{ } } }} pub const fn identifier(self) -> Option<&'static str> { match self { {{ for (const { name, alias } of champions) { }} Self::{{= constName(name).padEnd(constNamePad) }} => Some("{{= alias }}"), {{ } }} _ => None, } } } impl std::str::FromStr for Champion { type Err = (); fn from_str(s: &str) -> Result { match s.chars() .take(4) .filter(|c| c.is_ascii_alphanumeric()) .fold(0_u32, |hash, next| hash * {{= hashFactor }} + u32::from(next)) { {{ const keyStrings = (name, alias) => new Set([].concat(...[ name, alias ].map(s => s.toUpperCase()) .map(s => [ s.replace(/[^A-Z0-9]+/, '').substring(0, 4), s.split(/[^A-Z0-9]/, 1)[0].substring(0, 4), s.split(/[^A-Z]/, 1)[0].substring(0, 4), ]))); for (const { id, alias, name } of champions) { for (const prefix of keyStrings(name, alias)) { }} 0x{{= strHash(prefix).toString(16).padEnd(8) }} /* {{= prefix.padEnd(4) }} */ => Ok(Champion::{{= constName(name) }}), {{ } } }} _ => Err(()), } } } impl std::convert::TryFrom<&str> for Champion { type Error = ::Err; fn try_from(value: &str) -> Result { ::from_str(value) } } impl std::convert::From for Champion { fn from(value: i16) -> Self { Self(value) } } impl std::convert::From for i16 { fn from(value: Champion) -> Self { value.0 } } impl std::fmt::Display for Champion { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.0.fmt(f) } } impl std::fmt::Debug for Champion { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "Champion({} {})", self.0, self.identifier().unwrap_or("UNKNOWN")) } }