ordabok/src/db/models/words.rs

221 lines
6.3 KiB
Rust

use super::super::schema;
use crate::{
db::{Database, DatabaseError},
graphql::Context,
};
use diesel::prelude::*;
use juniper::{FieldResult, GraphQLEnum};
use schema::{wordrelation, words};
use tracing::info;
use std::convert::Into;
use super::languages::Language;
#[derive(diesel_derive_enum::DbEnum, Debug, Clone, PartialEq, Eq)]
#[DieselTypePath = "crate::db::schema::sql_types::Wordrelationship"]
pub enum WordRelationship {
Definition,
Related,
}
#[derive(
diesel_derive_enum::DbEnum, Debug, Clone, PartialEq, Eq, GraphQLEnum,
)]
#[DieselTypePath = "crate::db::schema::sql_types::Partofspeech"]
pub enum PartOfSpeech {
Adjective,
Adposition,
Adverb,
Auxilliary,
#[graphql(name = "COORDINATING_CONJUNCTION")]
CoordConj,
Determiner,
Interjection,
Noun,
Numeral,
Particle,
Pronoun,
#[graphql(name = "PROPER_NOUN")]
ProperNoun,
Punctuation,
#[graphql(name = "SUBORDINATING_CONJUNCTION")]
SubjConj,
Symbol,
Verb,
Other,
}
#[derive(Queryable, Insertable, Debug, Clone, PartialEq, Eq)]
pub struct Word {
norm: String,
native: Option<String>,
lemma: Option<String>,
language: uuid::Uuid,
partofspeech: PartOfSpeech,
audio: Option<String>,
video: Option<String>,
image: Option<String>,
description: Option<String>,
etymology: Option<String>,
lusage: Option<String>,
morphology: Option<String>,
}
impl Word {
fn relationship(
&self,
db: &Database,
relationship: WordRelationship,
) -> Result<Vec<Word>, DatabaseError> {
use schema::wordrelation::dsl;
match &mut db.conn() {
Ok(conn) => Ok(dsl::wordrelation
.filter(dsl::wordsource.eq(self.norm.clone()))
.filter(dsl::relationship.eq(relationship))
.load::<WordRelation>(conn)
.map_err(|e| {
DatabaseError::new(
format!("Failed to retrieve word relations: {e:?}"),
"Database reading failed",
)
})?
.into_iter()
.flat_map(|w| {
use schema::words::dsl;
dsl::words.find(w.wordtarget).first::<Word>(conn)
})
.collect::<Vec<Word>>()),
Err(e) => Err(DatabaseError::new(
format!("Failed to connect to the database: {e:?}"),
"Database connection error",
)),
}
}
}
#[juniper::graphql_object(Context = Context)]
impl Word {
#[graphql(description = "Normal form of the word")]
fn norm(&self) -> String {
self.norm.clone()
}
#[graphql(description = "Native representation of the word")]
fn native(&self) -> Option<String> {
self.native.clone()
}
#[graphql(description = "Base form of the current word")]
fn lemma(&self, context: &Context) -> Option<Word> {
use schema::words::dsl;
match self.lemma.clone() {
Some(lemma) => match &mut context.db.conn() {
Ok(conn) => {
match dsl::words.find(lemma.clone()).first::<Word>(conn) {
Ok(word) => Some(word),
Err(e) => {
info!(
"Failed to retrieve lemma {} of word {}: {:?}",
lemma, self.norm, e
);
None
}
}
}
Err(e) => {
info!("Could not connect to the database: {:?}", e);
None
}
},
None => None,
}
}
#[graphql(description = "Language to which the word belongs")]
fn language(&self, context: &Context) -> Language {
use schema::languages::dsl;
match &mut context.db.conn() {
Ok(conn) => {
match dsl::languages.find(self.language).first::<Language>(conn)
{
Ok(lang) => lang,
Err(e) => {
panic!("Failed to retrieve language {} of word {} from database: {:?}",
self.language, self.norm, e
)
}
}
}
Err(e) => panic!("Failed to connect to database: {:?}", e),
}
}
#[graphql(
name = "partOfSpeech",
description = "Part of speech the word belongs to"
)]
fn part_of_speech(&self) -> PartOfSpeech {
self.partofspeech.clone()
}
#[graphql(description = "Link to an audio file related to the word")]
fn audio(&self) -> Option<String> {
self.audio.clone()
}
#[graphql(description = "Link to an video file related to the word")]
fn video(&self) -> Option<String> {
self.video.clone()
}
#[graphql(description = "Link to an image file related to the word")]
fn image(&self) -> Option<String> {
self.image.clone()
}
#[graphql(description = "Etymology of the word, can be in Markdown format")]
fn etymology(&self) -> Option<String> {
self.etymology.clone()
}
#[graphql(description = "Usage of the word, can be in Markdown format")]
fn usage(&self) -> Option<String> {
self.lusage.clone()
}
#[graphql(
description = "Morphology of the word, can be in Markdown format"
)]
fn morphology(&self) -> Option<String> {
self.morphology.clone()
}
#[graphql(
name = "related",
description = "Words related to the current word"
)]
fn related_words(&self, context: &Context) -> FieldResult<Vec<Word>> {
self.relationship(&context.db, WordRelationship::Related)
.map_err(Into::into)
}
#[graphql(
name = "definitions",
description = "Words that define the current word"
)]
fn definitions(&self, context: &Context) -> FieldResult<Vec<Word>> {
self.relationship(&context.db, WordRelationship::Definition)
.map_err(Into::into)
}
}
#[derive(Queryable, Insertable, Debug, Clone, PartialEq, Eq)]
#[diesel(table_name = wordrelation)]
pub struct WordRelation {
id: i32,
wordsource: String,
wordtarget: String,
relationship: WordRelationship,
}