diff options
author | eta <eta@theta.eu.org> | 2020-06-15T20·03+0100 |
---|---|---|
committer | eta <eta@theta.eu.org> | 2020-06-16T13·30+0000 |
commit | c3abbb5e2daeec07d03f1addd4a6c905af7a02ac (patch) | |
tree | 416ba1be719dd3ebbd15ee41b6f8a2c1b944bd43 /fun/tvldb/src/keyword.rs | |
parent | 4c22cf316933613215e83f70a6bb2c556d42e02c (diff) |
feat(tvldb): Import the tvldb/paroxysm source, add a Nix derivation r/993
- This imports the tvldb (actually a thing called 'paroxysm') code from https://git.theta.eu.org/eta/paroxysm into the monorepo. - Additionally, I did a nix thing, yay! \o/ (well, with tazjin's help) - 3p/default.nix needed modifying to whitelist pgsql. Change-Id: Icdf13ca221650dde376f632bd2dd8a087af451bf Reviewed-on: https://cl.tvl.fyi/c/depot/+/389 Reviewed-by: tazjin <mail@tazj.in>
Diffstat (limited to 'fun/tvldb/src/keyword.rs')
-rw-r--r-- | fun/tvldb/src/keyword.rs | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/fun/tvldb/src/keyword.rs b/fun/tvldb/src/keyword.rs new file mode 100644 index 000000000000..78dc36f35f12 --- /dev/null +++ b/fun/tvldb/src/keyword.rs @@ -0,0 +1,182 @@ +use crate::models::{Entry, Keyword, NewEntry, NewKeyword}; +use diesel::pg::PgConnection; +use diesel::prelude::*; +use failure::Error; +use std::borrow::Cow; + +pub struct KeywordDetails { + pub keyword: Keyword, + pub entries: Vec<Entry>, +} +impl KeywordDetails { + pub fn learn(&mut self, nick: &str, text: &str, dbc: &PgConnection) -> Result<usize, Error> { + let now = ::chrono::Utc::now().naive_utc(); + let ins = NewEntry { + keyword_id: self.keyword.id, + idx: (self.entries.len() + 1) as _, + text, + creation_ts: now, + created_by: nick, + }; + let new = { + use crate::schema::entries; + ::diesel::insert_into(entries::table) + .values(ins) + .get_result(dbc)? + }; + self.entries.push(new); + Ok(self.entries.len()) + } + pub fn process_moves(&mut self, moves: &[(i32, i32)], dbc: &PgConnection) -> Result<(), Error> { + for (oid, new_idx) in moves { + { + use crate::schema::entries::dsl::*; + ::diesel::update(entries.filter(id.eq(oid))) + .set(idx.eq(new_idx)) + .execute(dbc)?; + } + } + self.entries = Self::get_entries(self.keyword.id, dbc)?; + Ok(()) + } + pub fn swap(&mut self, idx_a: usize, idx_b: usize, dbc: &PgConnection) -> Result<(), Error> { + let mut moves = vec![]; + for ent in self.entries.iter() { + if ent.idx == idx_a as i32 { + moves.push((ent.id, idx_b as i32)); + } + if ent.idx == idx_b as i32 { + moves.push((ent.id, idx_a as i32)); + } + } + if moves.len() != 2 { + Err(format_err!("Invalid swap operation."))?; + } + self.process_moves(&moves, dbc)?; + Ok(()) + } + pub fn update(&mut self, idx: usize, val: &str, dbc: &PgConnection) -> Result<(), Error> { + let ent = self + .entries + .get_mut(idx.saturating_sub(1)) + .ok_or(format_err!("No such element to update."))?; + { + use crate::schema::entries::dsl::*; + ::diesel::update(entries.filter(id.eq(ent.id))) + .set(text.eq(val)) + .execute(dbc)?; + } + ent.text = val.to_string(); + Ok(()) + } + pub fn delete(&mut self, idx: usize, dbc: &PgConnection) -> Result<(), Error> { + // step 1: delete the element + { + let ent = self + .entries + .get(idx.saturating_sub(1)) + .ok_or(format_err!("No such element to delete."))?; + { + use crate::schema::entries::dsl::*; + ::diesel::delete(entries.filter(id.eq(ent.id))).execute(dbc)?; + } + } + // step 2: move all the elements in front of it back one + let mut moves = vec![]; + for ent in self.entries.iter() { + if idx > ent.idx as _ { + moves.push((ent.id, ent.idx.saturating_sub(1))); + } + } + self.process_moves(&moves, dbc)?; + Ok(()) + } + pub fn add_zwsp_to_name(name: &str) -> Option<String> { + let second_index = name.char_indices().nth(1).map(|(i, _)| i)?; + let (start, end) = name.split_at(second_index); + Some(format!("{}{}", start, end)) + } + pub fn format_entry(&self, idx: usize) -> Option<String> { + if let Some(ent) = self.entries.get(idx.saturating_sub(1)) { + let gen_clr = if self.keyword.chan == "*" { + "\x0307" + } else { + "" + }; + let zwsp_name = Self::add_zwsp_to_name(&self.keyword.name) + .unwrap_or_else(|| self.keyword.name.clone()); + Some(format!( + "\x02{}{}\x0f\x0315[{}/{}]\x0f: {} \x0f\x0314[{}]\x0f", + gen_clr, + zwsp_name, + idx, + self.entries.len(), + ent.text, + ent.creation_ts.date() + )) + } else { + None + } + } + pub fn get_or_create(word: &str, c: &str, dbc: &PgConnection) -> Result<Self, Error> { + if let Some(ret) = Self::get(word, c, dbc)? { + Ok(ret) + } else { + Ok(Self::create(word, c, dbc)?) + } + } + pub fn create(word: &str, c: &str, dbc: &PgConnection) -> Result<Self, Error> { + let val = NewKeyword { + name: word, + chan: c, + }; + let ret: Keyword = { + use crate::schema::keywords; + ::diesel::insert_into(keywords::table) + .values(val) + .get_result(dbc)? + }; + Ok(KeywordDetails { + keyword: ret, + entries: vec![], + }) + } + fn get_entries(kid: i32, dbc: &PgConnection) -> Result<Vec<Entry>, Error> { + let entries: Vec<Entry> = { + use crate::schema::entries::dsl::*; + entries + .filter(keyword_id.eq(kid)) + .order_by(idx.asc()) + .load(dbc)? + }; + Ok(entries) + } + pub fn get<'a, T: Into<Cow<'a, str>>>( + word: T, + c: &str, + dbc: &PgConnection, + ) -> Result<Option<Self>, Error> { + let word = word.into(); + let keyword: Option<Keyword> = { + use crate::schema::keywords::dsl::*; + keywords + .filter(name.ilike(word).and(chan.eq(c).or(chan.eq("*")))) + .first(dbc) + .optional()? + }; + if let Some(k) = keyword { + let entries = Self::get_entries(k.id, dbc)?; + if let Some(e0) = entries.get(0) { + if e0.text.starts_with("see: ") { + return Self::get(e0.text.replace("see: ", ""), c, dbc); + } + } + Ok(Some(KeywordDetails { + keyword: k, + entries, + })) + } else { + Ok(None) + } + } +} |