logmeow/filters.cpp

292 lines
8.4 KiB
C++

#include <string>
#include <vector>
#include <stdexcept>
#include "log.h"
#include "filters.h"
Filter::~Filter() {
}
IntegerFilter::IntegerFilter(FilterKey key_, size_t other_, bool inverted_, bool disabled)
: key(key_), other(other_), inverted(inverted_), _disabled(disabled) {
if (key_ != FilterKey::PID && key_ != FilterKey::TID) {
throw std::runtime_error("IntegerFilter is passed a non-integer key");
}
}
void IntegerFilter::updated() {
}
bool IntegerFilter::disabled() const {
return this->_disabled;
}
void IntegerFilter::disabled(bool disabled) {
this->_disabled = disabled;
}
std::unique_ptr<Filter> IntegerFilter::clone() const {
return std::make_unique<IntegerFilter>(this->key, this->other, this->inverted, this->_disabled);
}
const std::string* IntegerFilter::error() const {
return nullptr;
}
bool IntegerFilter::match(const LogcatEntry& entry) const {
bool matched;
switch (this->key) {
case FilterKey::PID: matched = entry.pid == this->other; break;
case FilterKey::TID: matched = entry.tid == this->other; break;
default: throw std::runtime_error("IntegerFilter::match received unhandled key");
};
return !this->inverted ? matched : !matched;
}
StringFilter::StringFilter(FilterKey key_, std::string other_, bool inverted_, bool exact_match_, bool disabled)
: key(key_), other(std::move(other_)), inverted(inverted_), exact_match(exact_match_), _disabled(disabled) {
if (key_ != FilterKey::User && key_ != FilterKey::Tag && key_ != FilterKey::Message) {
throw std::runtime_error("StringFilter is passed a non-string key");
}
this->updated();
}
void StringFilter::updated() {
this->_regex.reset();
this->_error.clear();
if (this->other.empty()) {
this->_error = "String must not be empty";
return;
}
if (!this->other.starts_with("regex:")) {
return;
}
if (this->other.size() == 6) {
this->_error = "Regex must not be empty";
return;
}
try {
this->_regex.emplace(&this->other.c_str()[6]);
} catch (const std::exception& e) {
this->_error = e.what();
}
}
bool StringFilter::disabled() const {
return this->_disabled;
}
void StringFilter::disabled(bool disabled) {
this->_disabled = disabled;
}
std::unique_ptr<Filter> StringFilter::clone() const {
return std::make_unique<StringFilter>(this->key, this->other, this->inverted, this->exact_match, this->_disabled);
}
const std::string* StringFilter::error() const {
return !this->_error.empty() ? &this->_error : nullptr;
}
bool StringFilter::_match_no_regex(const LogcatEntry& entry) const {
bool matched;
if (this->exact_match) {
switch (this->key) {
case FilterKey::User: matched = entry.user.value_or("") == this->other; break;
case FilterKey::Tag: matched = entry.tag == this->other; break;
case FilterKey::Message: matched = entry.message == this->other; break;
default: throw std::runtime_error("StringFilter::match received unhandled key");
}
} else {
switch (this->key) {
case FilterKey::User: matched = entry.user.value_or("").find(this->other) != std::string::npos; break;
case FilterKey::Tag: matched = entry.tag.find(this->other) != std::string::npos; break;
case FilterKey::Message: matched = entry.message.find(this->other) != std::string::npos; break;
default: throw std::runtime_error("StringFilter::match received unhandled key");
}
}
return !this->inverted ? matched : !matched;
}
bool StringFilter::match(const LogcatEntry& entry) const {
bool matched;
if (this->error()) {
log(std::string("StringFilter::match called despite there being an error: ") + *this->error());
return true;
}
if (!this->other.starts_with("regex:")) {
return this->_match_no_regex(entry);
}
if (!this->_regex) {
log("StringFilter::match called with a regex despite there being no regex");
return true;
}
std::string str;
regmatch_t pmatch[1];
switch (this->key) {
case FilterKey::User: str = entry.user.value_or(""); break;
case FilterKey::Tag: str = entry.tag; break;
case FilterKey::Message: str = entry.message; break;
default: throw std::runtime_error("StringFilter::match received unhandled key");
}
pmatch[0].rm_so = 0;
pmatch[0].rm_eo = static_cast<regoff_t>(str.size());
matched = this->_regex->match(str.data(), 1, pmatch, REG_STARTEND);
if (this->exact_match) {
matched = matched && pmatch[0].rm_so == 0 && static_cast<size_t>(pmatch[0].rm_eo) == str.size();
}
return !this->inverted ? matched : !matched;
}
BufferFilter::BufferFilter(unsigned int wanted_, bool disabled) : wanted(wanted_), _disabled(disabled) {
}
void BufferFilter::updated() {
}
bool BufferFilter::disabled() const {
return this->_disabled;
}
void BufferFilter::disabled(bool disabled) {
this->_disabled = disabled;
}
std::unique_ptr<Filter> BufferFilter::clone() const {
return std::make_unique<BufferFilter>(this->wanted, this->_disabled);
}
const std::string* BufferFilter::error() const {
return nullptr;
}
bool BufferFilter::match(const LogcatEntry& entry) const {
return this->wanted & static_cast<unsigned int>(entry.buffer) || this->wanted == 0;
}
PriorityFilter::PriorityFilter(unsigned int wanted_, bool disabled) : wanted(wanted_), _disabled(disabled) {
}
void PriorityFilter::updated() {
}
bool PriorityFilter::disabled() const {
return this->_disabled;
}
void PriorityFilter::disabled(bool disabled) {
this->_disabled = disabled;
}
std::unique_ptr<Filter> PriorityFilter::clone() const {
return std::make_unique<PriorityFilter>(this->wanted, this->_disabled);
}
const std::string* PriorityFilter::error() const {
return nullptr;
}
bool PriorityFilter::match(const LogcatEntry& entry) const {
return this->wanted & static_cast<unsigned int>(entry.priority) || this->wanted == 0;
}
GroupFilter::GroupFilter(std::vector<std::unique_ptr<Filter>> filters_, GroupFilter::Type type_, bool disabled)
: filters(std::move(filters_)), type(type_), _disabled(disabled) {
}
void GroupFilter::updated() {
}
bool GroupFilter::disabled() const {
return this->_disabled;
}
void GroupFilter::disabled(bool disabled) {
this->_disabled = disabled;
}
std::unique_ptr<Filter> GroupFilter::clone() const {
std::vector<std::unique_ptr<Filter>> new_filters;
new_filters.reserve(this->filters.size());
for (const std::unique_ptr<Filter>& filter : this->filters) {
new_filters.push_back(filter->clone());
}
return std::make_unique<GroupFilter>(std::move(new_filters), this->type, this->_disabled);
}
const std::string* GroupFilter::error() const {
return nullptr;
}
bool GroupFilter::match(const LogcatEntry& entry) const {
bool had_matched = false;
for (const std::unique_ptr<Filter>& filter : this->filters) {
if (filter->disabled() || filter->error()) {
continue;
}
bool matched = filter->match(entry);
if (this->type == GroupFilter::Type::Any && matched) {
return true;
} else if (this->type == GroupFilter::Type::None && matched) {
return false;
} else if (this->type == GroupFilter::Type::All && !matched) {
return false;
} else if (this->type == GroupFilter::Type::One) {
if (had_matched && matched) {
return false;
}
if (matched) {
had_matched = true;
}
}
}
if (this->type == GroupFilter::Type::Any) {
return false;
} else if (this->type == GroupFilter::Type::One && !had_matched) {
return false;
}
return true;
}
void copy_filters(Filters& filters, const Filters& other) {
filters.clear();
filters.reserve(other.size());
for (const auto &[title, filter] : other) {
filters.push_back(std::make_pair(title, filter->clone()));
}
}
bool matches(const Filters& filters, const LogcatEntry& entry) {
bool ok_filter_exists = false;
for (const auto &[title, filter] : filters) {
if (filter->disabled() || filter->error()) {
continue;
}
ok_filter_exists = true;
if (filter->match(entry)) {
return true;
}
}
return !ok_filter_exists;
}