gallery.accords-library.com/server/szurubooru/search/parser.py

111 lines
3.4 KiB
Python

import re
from szurubooru import errors
from szurubooru.search import criteria, tokens
def _create_criterion(original_value, value):
if ',' in value:
return criteria.ArrayCriterion(
original_value, value.split(','))
if '..' in value:
low, high = value.split('..', 1)
if not low and not high:
raise errors.SearchError('Empty ranged value')
return criteria.RangedCriterion(original_value, low, high)
return criteria.PlainCriterion(original_value, value)
def _parse_anonymous(value, negated):
criterion = _create_criterion(value, value)
return tokens.AnonymousToken(criterion, negated)
def _parse_named(key, value, negated):
original_value = value
if key.endswith('-min'):
key = key[:-4]
value += '..'
elif key.endswith('-max'):
key = key[:-4]
value = '..' + value
criterion = _create_criterion(original_value, value)
return tokens.NamedToken(key, criterion, negated)
def _parse_special(value, negated):
return tokens.SpecialToken(value, negated)
def _parse_sort(value, negated):
if value.count(',') == 0:
order_str = None
elif value.count(',') == 1:
value, order_str = value.split(',')
else:
raise errors.SearchError('Too many commas in sort style token.')
try:
order = {
'asc': tokens.SortToken.SORT_ASC,
'desc': tokens.SortToken.SORT_DESC,
'': tokens.SortToken.SORT_DEFAULT,
None: tokens.SortToken.SORT_DEFAULT,
}[order_str]
except KeyError:
raise errors.SearchError(
'Unknown search direction: %r.' % order_str)
if negated:
order = {
tokens.SortToken.SORT_ASC:
tokens.SortToken.SORT_DESC,
tokens.SortToken.SORT_DESC:
tokens.SortToken.SORT_ASC,
tokens.SortToken.SORT_DEFAULT:
tokens.SortToken.SORT_NEGATED_DEFAULT,
tokens.SortToken.SORT_NEGATED_DEFAULT:
tokens.SortToken.SORT_DEFAULT,
}[order]
return tokens.SortToken(value, order)
class SearchQuery:
def __init__(self):
self.anonymous_tokens = []
self.named_tokens = []
self.special_tokens = []
self.sort_tokens = []
def __hash__(self):
return hash((
tuple(self.anonymous_tokens),
tuple(self.named_tokens),
tuple(self.special_tokens),
tuple(self.sort_tokens)))
class Parser:
def parse(self, query_text):
query = SearchQuery()
for chunk in re.split(r'\s+', (query_text or '').lower()):
if not chunk:
continue
negated = False
while chunk[0] == '-':
chunk = chunk[1:]
negated = not negated
match = re.match('([a-z_-]+):(.*)', chunk)
if match:
key, value = list(match.groups())
if key == 'sort':
query.sort_tokens.append(
_parse_sort(value, negated))
elif key == 'special':
query.special_tokens.append(
_parse_special(value, negated))
else:
query.named_tokens.append(
_parse_named(key, value, negated))
else:
query.anonymous_tokens.append(
_parse_anonymous(chunk, negated))
return query