#![forbid(unsafe_code)]
#![warn(missing_docs)]
extern crate parse_wiki_text;
extern crate serde;
#[macro_use]
extern crate serde_derive;
mod configuration;
mod examples;
mod language;
mod languages;
mod list;
mod overview;
mod pos_section;
mod pos_template;
mod pronunciation;
mod util;
pub use configuration::create_configuration;
pub use languages::Language;
use parse_wiki_text::{DefinitionListItem, DefinitionListItemType::Details, Node, Parameter};
use std::{borrow::Cow, collections::HashMap};
use util::*;
#[derive(Debug, Deserialize, Serialize)]
pub struct Example<'a> {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub example: Vec<Flowing<'a>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub translation: Vec<Flowing<'a>>,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "snake_case", tag = "type")]
pub enum Flowing<'a> {
Audio {
file_name: Cow<'a, str>,
#[serde(skip_serializing_if = "Option::is_none")]
label: Option<Cow<'a, str>>,
#[serde(skip_serializing_if = "Option::is_none")]
language: Option<Cow<'a, str>>,
},
Bold,
Comment,
CommonGender,
Comparative,
EmptyAudio,
FeminineGender,
Genitive,
Ipa {
ipa: Cow<'a, str>,
},
Italic,
Language {
language: Cow<'a, str>,
},
LanguageAdjective {
language: Cow<'a, str>,
},
Link {
target: Cow<'a, str>,
text: Cow<'a, str>,
},
List {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
items: Vec<Vec<Flowing<'a>>>,
},
MasculineGender,
NeuterGender,
NoPlural,
PastParticiple,
Plural,
Plural1,
Plural2,
Plural3,
Plural4,
Pos {
pos: Pos,
},
Preterite,
QualityControl,
Reference,
Rhyme {
rhyme: Cow<'a, str>,
},
Superlative,
SuperscriptEnd,
SuperscriptStart,
Term {
language: Cow<'a, str>,
term: Cow<'a, str>,
#[serde(skip_serializing_if = "Option::is_none")]
transliteration: Option<Cow<'a, str>>,
},
Text {
value: Cow<'a, str>,
},
Unknown {
value: Cow<'a, str>,
},
}
#[derive(Debug, Deserialize, Serialize)]
pub struct LanguageEntry<'a> {
pub language: Language,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub pos_entries: Vec<PosEntry<'a>>,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct Output<'a> {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub language_entries: Vec<LanguageEntry<'a>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub warnings: Vec<Warning>,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct Overview<'a> {
pub name: Cow<'a, str>,
#[serde(default, skip_serializing_if = "HashMap::is_empty")]
pub named_parameters: HashMap<Cow<'a, str>, Cow<'a, str>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub unnamed_parameters: Vec<Vec<::Flowing<'a>>>,
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum Pos {
Abbreviation,
Adjective,
Adverb,
CompoundWord,
ConjugatedForm,
Conjunction,
DeclinedForm,
FirstName,
Idiom,
Interjection,
LastName,
LocalAdverb,
Noun,
Numeral,
PastParticiple,
Postposition,
Preposition,
ProperNoun,
Proverb,
Symbol,
Toponym,
Verb,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct PosEntry<'a> {
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub abbreviations: Vec<Vec<Flowing<'a>>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub affectionate_forms: Vec<Vec<Flowing<'a>>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub antonyms: Vec<Vec<Flowing<'a>>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub audio: Vec<Flowing<'a>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub compound_words: Vec<Vec<Flowing<'a>>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub definitions: Vec<Vec<Flowing<'a>>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub details: Vec<Flowing<'a>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub diminutives: Vec<Vec<Flowing<'a>>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub etymology: Vec<Vec<Flowing<'a>>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub examples: Vec<Example<'a>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub feminine_forms: Vec<Vec<Flowing<'a>>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub hypernyms: Vec<Vec<Flowing<'a>>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub hyphenation: Vec<Vec<Flowing<'a>>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub hyponyms: Vec<Vec<Flowing<'a>>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub idioms: Vec<Vec<Flowing<'a>>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub ipa: Vec<Flowing<'a>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub masculine_forms: Vec<Vec<Flowing<'a>>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub no_longer_valid_spellings: Vec<Vec<Flowing<'a>>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub overview: Option<Overview<'a>>,
pub pos: Pos,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub proverbs: Vec<Vec<Flowing<'a>>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub related_words: Vec<Vec<Flowing<'a>>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub rhymes: Vec<Flowing<'a>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub short_forms: Vec<Vec<Flowing<'a>>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub similar_words: Vec<Vec<Flowing<'a>>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub symbols: Vec<Vec<Flowing<'a>>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub synonyms: Vec<Vec<Flowing<'a>>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub typical_word_combinations: Vec<Vec<Flowing<'a>>>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub variants: Vec<Vec<Flowing<'a>>>,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct Warning {
pub end: usize,
#[serde(skip_serializing_if = "Option::is_none")]
pub language: Option<Language>,
pub message: WarningMessage,
pub start: usize,
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum WarningMessage {
Duplicate,
Empty,
SectionEmpty,
Supplementary,
Unrecognized,
ValueConflicting,
ValueUnrecognized,
}
#[must_use]
pub fn parse<'a>(title: &str, wiki_text: &'a str, nodes: &[Node<'a>]) -> Output<'a> {
let mut context = Context {
language: None,
warnings: vec![],
wiki_text,
};
let mut language_entries = vec![];
let mut node_index = 0;
while let Some(node) = nodes.get(node_index) {
if let Node::Heading {
level,
nodes: heading_child_nodes,
..
} = node
{
if *level < 2 {
add_warning(&mut context, node, WarningMessage::Unrecognized);
break;
}
if *level == 2 {
node_index += 1;
if let [Node::Text { value, .. }, Node::Template {
name, parameters, ..
}, Node::Text { value: ")", .. }] = heading_child_nodes.as_slice()
{
if value.len() == title.len() + 2
&& value.starts_with(title)
&& value.ends_with(" (")
&& text_equals(name, "Sprache")
{
if let [parameter @ Parameter { name: None, .. }] = parameters.as_slice() {
if let Some(language) = parse_text(¶meter.value) {
match Language::from_name(&language) {
None => add_warning(
&mut context,
parameter,
WarningMessage::ValueUnrecognized,
),
Some(language) => {
context.language = Some(language);
node_index += language::parse_language(
&mut context,
node,
&nodes[node_index..],
&mut language_entries,
);
context.language = None;
}
}
continue;
}
}
}
}
add_warning(&mut context, node, WarningMessage::ValueUnrecognized);
continue;
}
}
node_index += 1;
add_warning(&mut context, node, WarningMessage::Unrecognized);
}
Output {
language_entries,
warnings: context.warnings,
}
}