new filter abstraction

This commit is contained in:
mrbesen 2021-04-11 01:47:55 +02:00
parent 797eecc487
commit d46579b73f
Signed by: MrBesen
GPG Key ID: 596B2350DCD67504
9 changed files with 183 additions and 131 deletions

9
src/filter.cpp Normal file
View File

@ -0,0 +1,9 @@
#include "filter.h"
void Filter::setup(std::ostream& o, std::istream& str) {
o << "ignoreCase? [y/n]";
char c;
str >> c;
ignoreCase = (c == 'y' || c == 'Y');
}

14
src/filter.h Normal file
View File

@ -0,0 +1,14 @@
#pragma once
#include <iostream>
#include "message.h"
//ideen: nach sender filtern, nur nachrichten mit medien, nur nachrichten ohne medien, medien Dateinamen, nach datum filtern, service messages? (joined, kicked, invited,...), inverted (not conatain)
struct Filter {
virtual ~Filter() {}
virtual void setup(std::ostream& o, std::istream& str);
virtual bool filter(const Message& m) const = 0;
protected:
bool ignoreCase;
};

View File

@ -2,11 +2,13 @@
#include <Log.h>
#include <iostream>
#include <iomanip>
#include <signal.h> //signal handler
#include <unistd.h>
#include <ctime>
#include "search.h"
#include "textfilter.h"
static bool run = true;
@ -17,6 +19,45 @@ void sig_handler(int sig_num) {
(void) sig_num;
}
std::map<std::string, Filter*> filterlist;
void loadFilter() {
filterlist.insert({"TextSearch", new TextFilter()});
filterlist.insert({"RegexSearch", new RegexFilter()});
}
void removeFilter() {
for(auto it : filterlist) {
delete it.second;
}
filterlist.clear();
}
inline void resetStream(std::istream& i = std::cin) {
i.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
Filter* chooseFilter(std::ostream& o, std::istream& istr) {
//print filter
uint32_t i = 0;
std::vector<Filter*> templookup;
templookup.reserve(filterlist.size());
for(auto it : filterlist) {
o << "[" << std::setw(2) << std::setfill('0') << i << "] " << it.first << std::endl;
templookup.push_back(it.second);
++i;
}
uint32_t choise = 0;
do {
o << "Pick one" << std::endl;
istr >> choise;
} while(choise > templookup.size());
resetStream(istr);
return templookup.at(choise);
}
int main(int argc, const char** argv) {
Log::init();
Log::setConsoleLogLevel(Log::Level::TRACE);
@ -41,6 +82,8 @@ int main(int argc, const char** argv) {
time_t end = time(nullptr);
Log::info << rootsearch.getChatCount() << " Chats with " << rootsearch.getMessageCount() << " Messages loaded in " << end-start << "s";
loadFilter();
signal(SIGINT, sig_handler);
Search* search = nullptr;
@ -49,22 +92,16 @@ int main(int argc, const char** argv) {
if(!search)
search = &rootsearch;
Log::info << search->getMessageCount() << " Messages. Enter searchterm: ";
std::string searchterm;
std::getline(std::cin, searchterm);
Log::trace << "searchterm: \"" << searchterm << "\"";
Log::info << search->getMessageCount() << " Messages. Configure Search: ";
if(!run) break;
Filter* filter = chooseFilter(std::cout, std::cin);
Log::info << "Enter Flags: [RI]";
std::string flags;
std::cin >> flags;
Searchflags parsedflags = Search::fromString(flags);
filter->setup(std::cout, std::cin);
if(!run) break;
time_t start = time(nullptr);
std::list<const Message*> results = search->search(searchterm, parsedflags);
std::list<const Message*> results = search->search(*filter);
time_t end = time(nullptr);
Log::info << results.size() << " results found in " << end-start << "s";
if(results.size()) {
@ -105,7 +142,7 @@ int main(int argc, const char** argv) {
}
}
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
resetStream(std::cin);
}
if(search != &rootsearch)

20
src/message.cpp Normal file
View File

@ -0,0 +1,20 @@
#include "message.h"
bool Message::operator==(const Message& m) const {
return (m.chatid == chatid) && (m.messageid == messageid);
}
bool Message::operator!=(const Message& m) const {
return (m.chatid != chatid) || (m.messageid != messageid);
}
bool Message::operator<(const Message& m) const {
if (chatid < m.chatid) return true;
if (chatid > m.chatid) return false;
// chatid == m.chatid
return (messageid < m.messageid);
}
bool Message::hasFile() const {
return !filename.empty();
}

17
src/message.h Normal file
View File

@ -0,0 +1,17 @@
#pragma once
#include <cstdint>
#include <string>
struct Message {
int64_t chatid;
int64_t messageid;
int64_t senderid;
std::string text;
std::string filename = ""; //empty filename = no file
bool isreply = false;
bool operator==(const Message& m) const;
bool operator!=(const Message& m) const;
bool operator<(const Message& m) const;
bool hasFile() const;
};

View File

@ -8,51 +8,6 @@
#include <nlohmann/json.hpp>
using json = nlohmann::json;
bool Message::operator==(const Message& m) const {
return (m.chatid == chatid) && (m.messageid == messageid);
}
bool Message::operator!=(const Message& m) const {
return (m.chatid != chatid) || (m.messageid != messageid);
}
bool Message::operator<(const Message& m) const {
if (chatid < m.chatid) return true;
if (chatid > m.chatid) return false;
// chatid == m.chatid
return (messageid < m.messageid);
}
bool Message::hasFile() const {
return !filename.empty();
}
Searchflags operator|=(Searchflags& lhs, const Searchflags sf) {
lhs = (Searchflags) ((uint32_t) lhs | (uint32_t) sf);
return lhs;
}
bool operator&(Searchflags& lhs, const Searchflags sf) {
return (bool) ((uint32_t) lhs & (uint32_t) sf);
}
static bool matches(const std::string& msg, const std::string& text) {
//simpler contains check
return (msg.find(text) != std::string::npos);
}
static bool matchesIC(const std::string& msg, const std::string& text) {
//turn compare string to lower
std::string lower;
mrbesen::util::toLower(msg, lower);
return (lower.find(text) != std::string::npos);
}
// matches reges is inline, so i need a wrapper
static bool matchesRegex(const std::string& msg, const std::regex& reg) {
return std::regex_search(msg, reg);
}
Search::Search() {}
Search::Search(const Search& orig, std::list<const Message*>* list) {
//copy messages
@ -69,27 +24,6 @@ Search::Search(const Search& orig, std::list<const Message*>* list) {
}
Search::~Search() {}
Searchflags Search::fromString(const std::string& str) {
Searchflags f = Searchflags::NONE;
for(char c : str) {
switch(c) {
case 'R':
case 'r':
f |= Searchflags::REGEX;
break;
case 'i':
case 'I':
f |= Searchflags::IGNORECASE;
break;
default:
break;
}
}
return f;
}
void Search::addFile(const std::string& file) {
//laden den datei
try {
@ -151,22 +85,10 @@ void Search::finalize() {
deduplicate.clear();
}
std::list<const Message*> Search::search(std::string text, Searchflags flags) const {
std::list<const Message*> Search::search(const Filter& filter) const {
std::list<const Message*> out;
if(flags & Searchflags::REGEX) {
searchRegex(text, flags & Searchflags::IGNORECASE, out);
return out;
}
if(flags & Searchflags::IGNORECASE) {
//turn search to lower
mrbesen::util::toLower(text);
runsearch<const std::string&>(text, matchesIC, out);
} else {
runsearch<const std::string&>(text, matches, out);
}
runsearch(filter, out);
return out;
}
@ -195,17 +117,9 @@ uint64_t Search::getMessageCount() const {
return msgs.size();
}
void Search::searchRegex(const std::string& text, bool ignoreCase, std::list<const Message*>& out) const {
//build regex pattern
const std::regex pattern(text, (ignoreCase ? std::regex::icase : (std::regex::flag_type) 0));
runsearch<const std::regex&>(pattern, matchesRegex, out);
}
template<typename T>
void Search::runsearch(T st, bool (*checker)(const std::string& msg, T text), std::list<const Message*>& out) const {
void Search::runsearch(const Filter& filter, std::list<const Message*>& out) const {
for(const Message& m : msgs) {
if(checker(m.text, st)) {
if(filter.filter(m)) {
out.push_back(&m);
}
}

View File

@ -10,31 +10,8 @@
#include <nlohmann/json_fwd.hpp>
using json = nlohmann::json;
struct Message {
int64_t chatid;
int64_t messageid;
int64_t senderid;
std::string text;
std::string filename = ""; //empty filename = no file
bool isreply = false;
bool operator==(const Message& m) const;
bool operator!=(const Message& m) const;
bool operator<(const Message& m) const;
bool hasFile() const;
};
enum class Searchflags {
NONE = 0,
IGNORECASE = 1,
REGEX = 2,
//ideen: nach sender filtern, nur nachrichten mit medien, nur nachrichten ohne medien, medien Dateinamen, nach datum filtern, service messages? (joined, kicked, invited,...), inverted (not conatain)
};
Searchflags operator|=(Searchflags& lhs, const Searchflags sf);
bool operator&(Searchflags& lhs, const Searchflags sf);
#include "filter.h"
#include "message.h"
class Search {
public:
@ -43,12 +20,10 @@ public:
Search(const Search& orig, std::list<const Message*>* list = nullptr);
~Search();
static Searchflags fromString(const std::string&);
void addFile(const std::string& file);
void finalize(); //stop adding files and finalize deduplication, could be called twice, but then a deduplication is not guaranteed
std::list<const Message*> search(std::string text, Searchflags flags = Searchflags::NONE) const;
std::list<const Message*> search(const Filter& filter) const;
const std::string& getChatname(int64_t id) const;
std::string getShortChatname(int64_t id) const;
@ -57,8 +32,7 @@ public:
private:
void searchRegex(const std::string& text, bool ignoreCase, std::list<const Message*>& out) const;
template<typename T>
void runsearch(T st, bool (*checker)(const std::string& msg, T text), std::list<const Message*>& out) const;
void runsearch(const Filter& filter, std::list<const Message*>& out) const;
void loadMessages(const json& j, int64_t chatid);

46
src/textfilter.cpp Normal file
View File

@ -0,0 +1,46 @@
#include "textfilter.h"
#include <util.h> // mrbesen/util.h
static bool matches(const std::string& msg, const std::string& text) {
//simpler contains check
return (msg.find(text) != std::string::npos);
}
static bool matchesIC(const std::string& msg, const std::string& text) {
//turn compare string to lower
std::string lower;
mrbesen::util::toLower(msg, lower);
return (lower.find(text) != std::string::npos);
}
void TextFilter::setup(std::ostream& o, std::istream& str) {
o << "Such text:";
std::getline(str, text);
Filter::setup(o, str);
if(ignoreCase) {
mrbesen::util::toLower(text);
}
}
bool TextFilter::filter(const Message& m) const {
if(ignoreCase)
return matchesIC(m.text, text);
else
return matches(m.text, text);
}
void RegexFilter::setup(std::ostream& o, std::istream& str) {
//build regex pattern
std::string text = ""; //TODO
pattern = std::regex(text, (ignoreCase ? std::regex::icase : (std::regex::flag_type) 0));
Filter::setup(o, str);
}
bool RegexFilter::filter(const Message& m) const {
return std::regex_search(m.text, pattern);
}

21
src/textfilter.h Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#include <string>
#include <regex>
#include "filter.h"
struct TextFilter : public Filter {
virtual void setup(std::ostream& o, std::istream& str) override;
virtual bool filter(const Message& m) const override;
protected:
std::string text;
};
struct RegexFilter : public Filter {
virtual void setup(std::ostream& o, std::istream& str) override;
virtual bool filter(const Message& m) const override;
protected:
std::regex pattern;
};