Want to contribute? Fork me on Codeberg.org!
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

523 lines
20 KiB

/*******************************************************************************
* Copyright 2021 Stefan Majewsky <majewsky@gmx.net>
* SPDX-License-Identifier: Apache-2.0
* Refer to the file "LICENSE" for details.
*******************************************************************************/
use json::JsonValue;
struct EnumVariant {
code: &'static str,
name: &'static str,
enabled: bool,
}
fn v(code: &'static str, name: &'static str) -> EnumVariant {
EnumVariant {
code,
name,
enabled: true,
}
}
impl EnumVariant {
fn when(self, enabled: bool) -> Self {
Self { enabled, ..self }
}
}
struct Enum<'a> {
name: &'static str,
all_name: Option<&'static str>,
doc: String,
entities: Option<&'a JsonValue>,
variants: Vec<EnumVariant>,
}
fn main() {
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=data/entities.json");
let entities_str = std::fs::read_to_string("data/entities.json").unwrap();
let entities = json::parse(&entities_str).unwrap();
let mut content = String::new();
content.push_str(&process(Enum {
name: "Dialect",
all_name: None,
doc: "Dialect of Japanese in which a certain vocabulary occurs.".into(),
entities: Some(&entities["dial"]),
variants: vec![
v("bra", "Brazilian"),
v("hob", "Hokkaido"),
v("ksb", "Kansai"),
v("ktb", "Kantou"),
v("kyb", "Kyoto"),
v("kyu", "Kyuushuu"),
v("nab", "Nagano"),
v("osb", "Osaka"),
v("rkb", "Ryuukyuu"),
v("thb", "Touhoku"),
v("tsb", "Tosa"),
v("tsug", "Tsugaru"),
],
}));
content.push_str(&process(Enum {
name: "GlossLanguage",
all_name: Some("AllGlossLanguage"),
doc: "The language of a particular Gloss.".into(),
entities: None,
variants: vec![
v("eng", "English").when(cfg!(feature = "translations-eng")),
v("dut", "Dutch").when(cfg!(feature = "translations-dut")),
v("fre", "French").when(cfg!(feature = "translations-fre")),
v("ger", "German").when(cfg!(feature = "translations-ger")),
v("hun", "Hungarian").when(cfg!(feature = "translations-hun")),
v("rus", "Russian").when(cfg!(feature = "translations-rus")),
v("slv", "Slovenian").when(cfg!(feature = "translations-slv")),
v("spa", "Spanish").when(cfg!(feature = "translations-spa")),
v("swe", "Swedish").when(cfg!(feature = "translations-swe")),
],
}));
content.push_str(&process(Enum {
name: "GlossType",
all_name: None,
doc: "Type of gloss.".into(),
entities: None,
variants: vec![
v("", "RegularTranslation"),
v("expl", "Explanation"),
v("fig", "FigurativeSpeech"),
v("lit", "LiteralTranslation"),
v("tm", "Trademark"),
],
}));
content.push_str(&process(Enum {
name: "KanjiInfo",
all_name: None,
doc: "Information regarding a certain KanjiElement.".into(),
entities: Some(&entities["ke_inf"]),
variants: vec![
v("ateji", "Ateji"),
v("iK", "IrregularKanjiUsage"),
v("ik", "IrregularKanaUsage"),
v("io", "IrregularOkuriganaUsage"),
v("oK", "OutdatedKanji"),
v("rK", "RareKanjiForm"),
],
}));
content.push_str(&process(Enum {
name: "PartOfSpeech",
all_name: Some("AllPartOfSpeech"),
doc: "Where a word can appear in a sentence for a particular Sense of the word.".into(),
entities: Some(&entities["pos"]),
variants: vec![
v("adj-f", "NounOrVerbActingPrenominally"),
v("adj-i", "Adjective"),
v("adj-ix", "YoiAdjective"),
v("adj-kari", "KariAdjective").when(cfg!(feature = "scope-archaic")),
v("adj-ku", "KuAdjective").when(cfg!(feature = "scope-archaic")),
v("adj-na", "AdjectivalNoun"),
v("adj-nari", "NariAdjective").when(cfg!(feature = "scope-archaic")),
v("adj-no", "NoAdjective"),
v("adj-pn", "PreNounAdjectival"),
v("adj-shiku", "ShikuAdjective").when(cfg!(feature = "scope-archaic")),
v("adj-t", "TaruAdjective"),
v("adv", "Adverb"),
v("adv-to", "AdverbTakingToParticle"),
v("aux", "Auxiliary"),
v("aux-adj", "AuxiliaryAdjective"),
v("aux-v", "AuxiliaryVerb"),
v("conj", "Conjunction"),
v("cop", "Copula"),
v("ctr", "Counter"),
v("exp", "Expression"),
v("int", "Interjection"),
v("n", "CommonNoun"),
v("n-adv", "AdverbialNoun"),
v("n-pr", "ProperNoun"),
v("n-pref", "NounPrefix"),
v("n-suf", "NounSuffix"),
v("n-t", "TemporalNoun"),
v("num", "Numeric"),
v("pn", "Pronoun"),
v("pref", "Prefix"),
v("prt", "Particle"),
v("suf", "Suffix"),
v("unc", "Unclassified"),
v("v-unspec", "UnspecifiedVerb"),
v("v1", "IchidanVerb"),
v("v1-s", "IchidanKureruVerb"),
v("v2a-s", "NidanUVerb").when(cfg!(feature = "scope-archaic")),
v("v2b-k", "UpperNidanBuVerb").when(cfg!(feature = "scope-archaic")),
v("v2b-s", "LowerNidanBuVerb").when(cfg!(feature = "scope-archaic")),
v("v2d-k", "UpperNidanDzuVerb").when(cfg!(feature = "scope-archaic")),
v("v2d-s", "LowerNidanDzuVerb").when(cfg!(feature = "scope-archaic")),
v("v2g-k", "UpperNidanGuVerb").when(cfg!(feature = "scope-archaic")),
v("v2g-s", "LowerNidanGuVerb").when(cfg!(feature = "scope-archaic")),
v("v2h-k", "UpperNidanFuVerb").when(cfg!(feature = "scope-archaic")),
v("v2h-s", "LowerNidanFuVerb").when(cfg!(feature = "scope-archaic")),
v("v2k-k", "UpperNidanKuVerb").when(cfg!(feature = "scope-archaic")),
v("v2k-s", "LowerNidanKuVerb").when(cfg!(feature = "scope-archaic")),
v("v2m-k", "UpperNidanMuVerb").when(cfg!(feature = "scope-archaic")),
v("v2m-s", "LowerNidanMuVerb").when(cfg!(feature = "scope-archaic")),
v("v2n-s", "LowerNidanNuVerb").when(cfg!(feature = "scope-archaic")),
v("v2r-k", "UpperNidanRuVerb").when(cfg!(feature = "scope-archaic")),
v("v2r-s", "LowerNidanRuVerb").when(cfg!(feature = "scope-archaic")),
v("v2s-s", "LowerNidanSuVerb").when(cfg!(feature = "scope-archaic")),
v("v2t-k", "UpperNidanTsuVerb").when(cfg!(feature = "scope-archaic")),
v("v2t-s", "LowerNidanTsuVerb").when(cfg!(feature = "scope-archaic")),
v("v2w-s", "LowerNidanUWeVerb").when(cfg!(feature = "scope-archaic")),
v("v2y-k", "UpperNidanYuVerb").when(cfg!(feature = "scope-archaic")),
v("v2y-s", "LowerNidanYuVerb").when(cfg!(feature = "scope-archaic")),
v("v2z-s", "LowerNidanZuVerb").when(cfg!(feature = "scope-archaic")),
v("v4b", "YodanBuVerb").when(cfg!(feature = "scope-archaic")),
v("v4g", "YodanGuVerb").when(cfg!(feature = "scope-archaic")),
v("v4h", "YodanFuVerb").when(cfg!(feature = "scope-archaic")),
v("v4k", "YodanKuVerb").when(cfg!(feature = "scope-archaic")),
v("v4m", "YodanMuVerb").when(cfg!(feature = "scope-archaic")),
v("v4n", "YodanNuVerb").when(cfg!(feature = "scope-archaic")),
v("v4r", "YodanRuVerb").when(cfg!(feature = "scope-archaic")),
v("v4s", "YodanSuVerb").when(cfg!(feature = "scope-archaic")),
v("v4t", "YodanTsuVerb").when(cfg!(feature = "scope-archaic")),
v("v5aru", "GodanAruVerb"),
v("v5b", "GodanBuVerb"),
v("v5g", "GodanGuVerb"),
v("v5k", "GodanKuVerb"),
v("v5k-s", "GodanIkuVerb"),
v("v5m", "GodanMuVerb"),
v("v5n", "GodanNuVerb"),
v("v5r", "GodanRuVerb"),
v("v5r-i", "IrregularGodanRuVerb"),
v("v5s", "GodanSuVerb"),
v("v5t", "GodanTsuVerb"),
v("v5u", "GodanUVerb"),
v("v5u-s", "IrregularGodanUVerb"),
v("vi", "IntransitiveVerb"),
v("vk", "KuruVerb"),
v("vn", "IrregularGodanNuVerb"),
v("vr", "IrregularGodanRuVerbWithPlainRiForm"),
v("vs", "SuruVerb"),
v("vs-c", "SuruPrecursorVerb"),
v("vs-i", "IncludedSuruVerb"),
v("vs-s", "SpecialSuruVerb"),
v("vt", "TransitiveVerb"),
v("vz", "IchidanZuruVerb"),
],
}));
content.push_str(&process(Enum {
name: "ReadingInfo",
all_name: None,
doc: "Information regarding a certain ReadingElement.".into(),
entities: Some(&entities["re_inf"]),
variants: vec![
v("gikun", "GikunOrJukujikun"),
v("ik", "IrregularKanaUsage"),
v("ok", "OutdatedKanaUsage"),
v("uK", "UsuallyWrittenUsingKanjiAlone"),
],
}));
content.push_str(&process(Enum {
name: "SenseInfo",
all_name: None,
doc: "Information regarding a certain Sense.".into(),
entities: Some(&entities["misc"]),
variants: vec![
v("X", "XRated"),
v("abbr", "Abbreviation"),
v("arch", "Archaism"),
v("char", "Character"),
v("chn", "ChildrensLanguage"),
v("col", "Colloquialism"),
v("company", "CompanyName"),
v("creat", "Creature"),
v("dated", "DatedTerm"),
v("dei", "Deity"),
v("derog", "Derogatory"),
v("doc", "Document"),
v("ev", "Event"),
v("fam", "FamiliarLanguage"),
v("fem", "FemaleTermOrLanguage"),
v("fict", "Fiction"),
v("form", "FormalOrLiteraryTerm"),
v("given", "GivenName"),
v("group", "Group"),
v("hist", "HistoricalTerm"),
v("hon", "HonorificLanguage"),
v("hum", "HumbleLanguage"),
v("id", "IdiomaticExpression"),
v("joc", "JocularTerm"),
v("leg", "Legend"),
v("m-sl", "MangaSlang"),
v("male", "MaleTermOrLanguage"),
v("myth", "Mythology"),
v("net-sl", "InternetSlang"),
v("obj", "Object"),
v("obs", "ObsoleteTerm"),
v("obsc", "ObscureTerm"),
v("on-mim", "Onomatopoeia"),
v("organization", "OrganizationName"),
v("oth", "Other"),
v("person", "PersonName"),
v("place", "PlaceName"),
v("poet", "PoeticalTerm"),
v("pol", "PoliteLanguage"),
v("product", "ProductName"),
v("proverb", "Proverb"),
v("quote", "Quotation"),
v("rare", "Rare"),
v("relig", "Religion"),
v("sens", "Sensitive"),
v("serv", "Service"),
v("sl", "Slang"),
v("station", "RailwayStation"),
v("surname", "Surname"),
v("uk", "UsuallyWrittenUsingKanaAlone"),
v("unclass", "UnclassifiedName"),
v("vulg", "VulgarTerm"),
v("work", "WorkOfArt"),
v("yoji", "Yojijukugo"),
],
}));
content.push_str(&process(Enum {
name: "SenseTopic",
all_name: None,
doc: "Field of study where a certain Sense originates.".into(),
entities: Some(&entities["field"]),
variants: vec![
v("Buddh", "Buddhism"),
v("Christn", "Christianity"),
v("MA", "MartialArts"),
v("Shinto", "Shinto"),
v("agric", "Agriculture"),
v("anat", "Anatomy"),
v("archeol", "Archeology"),
v("archit", "Architecture"),
v("art", "Art"),
v("astron", "Astronomy"),
v("audvid", "AudioVisual"),
v("aviat", "Aviation"),
v("baseb", "Baseball"),
v("biochem", "Biochemistry"),
v("biol", "Biology"),
v("bot", "Botany"),
v("bus", "Business"),
v("chem", "Chemistry"),
v("cloth", "Clothing"),
v("comp", "Computing"),
v("cryst", "Crystallography"),
v("ecol", "Ecology"),
v("econ", "Economics"),
v("elec", "ElectricalEngineering"),
v("electr", "Electronics"),
v("embryo", "Embryology"),
v("engr", "Engineering"),
v("ent", "Entomology"),
v("finc", "Finance"),
v("fish", "Fishing"),
v("food", "Food"),
v("gardn", "Gardening"),
v("genet", "Genetics"),
v("geogr", "Geography"),
v("geol", "Geology"),
v("geom", "Geometry"),
v("go", "Go"),
v("golf", "Golf"),
v("gramm", "Grammar"),
v("grmyth", "GreekMythology"),
v("hanaf", "Hanafuda"),
v("horse", "Horseracing"),
v("law", "Law"),
v("ling", "Linguistics"),
v("logic", "Logic"),
v("mahj", "Mahjong"),
v("math", "Mathematics"),
v("mech", "MechanicalEngineering"),
v("med", "Medicine"),
v("met", "Meteorology"),
v("mil", "Military"),
v("music", "Music"),
v("ornith", "Ornithology"),
v("paleo", "Paleontology"),
v("pathol", "Pathology"),
v("pharm", "Pharmacy"),
v("phil", "Philosophy"),
v("photo", "Photography"),
v("physics", "Physics"),
v("physiol", "Physiology"),
v("print", "Printing"),
v("psy", "Psychiatry"),
v("psych", "Psychology"),
v("rail", "Railway"),
v("shogi", "Shogi"),
v("sports", "Sports"),
v("stat", "Statistics"),
v("sumo", "Sumo"),
v("telec", "Telecommunications"),
v("tradem", "Trademark"),
v("vidg", "VideoGame"),
v("zool", "Zoology"),
],
}));
let out_dir = std::env::var_os("OUT_DIR").unwrap();
let dest_path = std::path::Path::new(&out_dir).join("generated.rs");
std::fs::write(&dest_path, content).unwrap();
}
fn process(e: Enum) -> String {
let mut lines = vec![];
//render the corresponding fully-populated enum, if requested
if let Some(all_name) = e.all_name {
lines.push(process(Enum {
name: all_name,
all_name: None,
doc: format!("{} This enum contains all possible variants, including those that have been disabled by compile-time flags in `enum {}`.", e.doc, e.name),
entities: e.entities,
variants: e.variants.iter().map(|v| EnumVariant{enabled: true, ..*v}).collect(),
}));
}
//enum declaration
lines.push(format!("/// {}", e.doc));
lines.push("#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]".into());
lines.push(format!("pub enum {} {{", e.name));
for v in e.variants.iter().filter(|v| v.enabled) {
if let Some(ref entities) = e.entities {
lines.push(format!(" ///{}", entities[v.code].as_str().unwrap()));
}
lines.push(format!(" {},", v.name));
}
lines.push("}\n".into());
//start impl Enum
lines.push(format!("impl Enum for {} {{", e.name));
//fn code(&self) -> &str
lines.push(" fn code(&self) -> &'static str {".into());
lines.push(" match *self {".into());
for v in e.variants.iter().filter(|v| v.enabled) {
lines.push(format!(
" {}::{} => \"{}\",",
e.name, v.name, v.code
));
}
lines.push(" }".into());
lines.push(" }\n".into());
//fn constant_name(&self) -> &str
lines.push(" fn constant_name(&self) -> &'static str {".into());
lines.push(" match *self {".into());
for v in e.variants.iter().filter(|v| v.enabled) {
lines.push(format!(
" {}::{} => \"{}\",",
e.name, v.name, v.name
));
}
lines.push(" }".into());
lines.push(" }\n".into());
//fn from_code(&str) -> Self
lines.push(" fn from_code(text: &str) -> Option<Self> {".into());
lines.push(" match text {".into());
for v in e.variants.iter().filter(|v| v.enabled) {
lines.push(format!(
" \"{}\" => Some({}::{}),",
v.code, e.name, v.name
));
}
lines.push(" _ => None,".into());
lines.push(" }".into());
lines.push(" }\n".into());
//end impl Enum
lines.push("}\n".into());
//impl Display
lines.push(format!("impl std::fmt::Display for {} {{", e.name));
lines.push(" fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {".into());
lines.push(" write!(f, \"{}\", self.constant_name())".into());
lines.push(" }".into());
lines.push("}\n".into());
//impl EnumPayload
lines.push(format!("impl EnumPayload for {} {{", e.name));
lines.push(" fn to_u32(&self) -> u32 {".into());
lines.push(" match *self {".into());
for (idx, v) in e.variants.iter().filter(|v| v.enabled).enumerate() {
lines.push(format!(" {}::{} => {},", e.name, v.name, idx));
}
lines.push(" }".into());
lines.push(" }\n".into());
lines.push(" fn from_u32(code: u32) -> Self {".into());
lines.push(" match code {".into());
for (idx, v) in e.variants.iter().filter(|v| v.enabled).enumerate() {
lines.push(format!(" {} => {}::{},", idx, e.name, v.name));
}
lines.push(format!(
" _ => panic!(\"unknown {} code: {{}}\", code),",
e.name
));
lines.push(" }".into());
lines.push(" }".into());
lines.push("}\n".into());
if let Some(all_name) = e.all_name {
//impl TryFrom
lines.push(format!(
"impl std::convert::TryFrom<{}> for {} {{",
all_name, e.name
));
lines.push(" type Error = DisabledVariant;".into());
lines.push(format!(
" fn try_from(value: {}) -> Result<{}, DisabledVariant> {{",
all_name, e.name,
));
lines.push(" match value {".into());
for v in e.variants.iter() {
if v.enabled {
lines.push(format!(
" {}::{} => Ok({}::{}),",
all_name, v.name, e.name, v.name
));
} else {
lines.push(format!(
" {}::{} => Err(DisabledVariant),",
all_name, v.name
));
}
}
lines.push(" }".into());
lines.push(" }".into());
lines.push("}\n".into());
//impl From
lines.push(format!(
"impl std::convert::From<{}> for {} {{",
e.name, all_name
));
lines.push(format!(" fn from(value: {}) -> {} {{", e.name, all_name));
lines.push(" match value {".into());
for v in e.variants.iter().filter(|v| v.enabled) {
lines.push(format!(
" {}::{} => {}::{},",
e.name, v.name, all_name, v.name
));
}
lines.push(" }".into());
lines.push(" }".into());
lines.push("}\n".into());
}
lines.join("\n")
}