Add missing changes from #1496
This commit is contained in:
@@ -14,7 +14,7 @@ public data class MangaSearchQueryCapabilities internal constructor(
|
||||
|
||||
@InternalParsersApi
|
||||
public fun validate(query: MangaSearchQuery) {
|
||||
val strictFields = capabilities.filter { !it.otherCriteria }.mapToSet { it.field }
|
||||
val strictFields = capabilities.filter { it.isExclusive }.mapToSet { it.field }
|
||||
val usedStrictFields = query.criteria.mapToSet { it.field }.intersect(strictFields)
|
||||
|
||||
if (usedStrictFields.isNotEmpty() && query.criteria.size > 1) {
|
||||
@@ -34,7 +34,7 @@ public data class MangaSearchQueryCapabilities internal constructor(
|
||||
}
|
||||
|
||||
// Ensure single value per criterion if supportMultiValue is false
|
||||
if (!capability.multiValue) {
|
||||
if (!capability.isMultiple) {
|
||||
when (criterion) {
|
||||
is Include<*> -> if (criterion.values.size > 1)
|
||||
throw IllegalArgumentException("Multiple values are not allowed for field ${criterion.field}")
|
||||
|
||||
@@ -15,6 +15,17 @@ public sealed interface QueryCriteria<T> {
|
||||
|
||||
override fun hashCode(): Int
|
||||
|
||||
/**
|
||||
* Represents an inclusion criterion that allows search results based on a set of allowed values.
|
||||
*
|
||||
* @param T The type of value being included in the search.
|
||||
* @property values The set of values that should be included in the search results.
|
||||
*
|
||||
* ### Example Usage:
|
||||
* ```kotlin
|
||||
* val genreFilter = QueryCriteria.Include(SearchableField.STATE, setOf(MangaState.ONGOING, MangaState.FINISHED))
|
||||
* ```
|
||||
*/
|
||||
public data class Include<T : Any>(
|
||||
public override val field: SearchableField,
|
||||
@JvmField public val values: Set<T>,
|
||||
@@ -25,6 +36,17 @@ public sealed interface QueryCriteria<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an exclusion criterion that exclude results containing certain values.
|
||||
*
|
||||
* @param T The type of value being excluded from the search.
|
||||
* @property values The set of values that should be excluded from the search results.
|
||||
*
|
||||
* ### Example Usage:
|
||||
* ```kotlin
|
||||
* val excludeTag = QueryCriteria.Exclude(SearchableField.TAG, setOf(MangaTag(key, title, source)))
|
||||
* ```
|
||||
*/
|
||||
public data class Exclude<T : Any>(
|
||||
public override val field: SearchableField,
|
||||
@JvmField public val values: Set<T>,
|
||||
@@ -35,6 +57,18 @@ public sealed interface QueryCriteria<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a range criterion that allows search based on a range of values.
|
||||
*
|
||||
* @param T The type of value used in the range (must be comparable).
|
||||
* @property from The starting value of the range (inclusive).
|
||||
* @property to The ending value of the range (inclusive).
|
||||
*
|
||||
* ### Example Usage:
|
||||
* ```kotlin
|
||||
* val yearRange = QueryCriteria.Range(SearchableField.PUBLICATION_YEAR, 2000, 2020)
|
||||
* ```
|
||||
*/
|
||||
public data class Range<T : Comparable<T>>(
|
||||
public override val field: SearchableField,
|
||||
@JvmField public val from: T,
|
||||
@@ -47,6 +81,18 @@ public sealed interface QueryCriteria<T> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Represents a match criterion that search results based on an exact match of a value.
|
||||
*
|
||||
* @param T The type of value being matched.
|
||||
* @property value The exact value that must be matched.
|
||||
*
|
||||
* ### Example Usage:
|
||||
* ```kotlin
|
||||
* val titleMatch = QueryCriteria.Match(SearchableField.TITLE, "manga title")
|
||||
* ```
|
||||
*/
|
||||
public data class Match<T : Any>(
|
||||
public override val field: SearchableField,
|
||||
@JvmField public val value: T,
|
||||
|
||||
@@ -2,9 +2,32 @@ package org.koitharu.kotatsu.parsers.model.search
|
||||
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
public data class SearchCapability (
|
||||
/**
|
||||
* Defines the search capabilities of a given field in the manga search query.
|
||||
*
|
||||
* @property field The searchable field that this capability applies to.
|
||||
* Example values:
|
||||
* - `SearchableField.TITLE_NAME` for searching by title.
|
||||
* - `SearchableField.AUTHOR` for searching by author names.
|
||||
* - `SearchableField.TAG` for filtering by tags.
|
||||
* @property criteriaTypes The set of supported criteria types for the field.
|
||||
* Example values:
|
||||
* - `setOf(Include::class, Exclude::class)` selected field supports inclusion/exclusion criteria.
|
||||
* - `setOf(Range::class)` selected field support numerical range criteria.
|
||||
* @property isMultiValue Indicates whether the field supports multiple values.
|
||||
* - `true` if multiple values can be provided (e.g., multiple tags or authors).
|
||||
* - `false` if only a single value is allowed (e.g., only one tag or author).
|
||||
* @property isExclusive Specifies whether the field can be used alongside other criteria.
|
||||
* - `true` if this field can be used with other search criteria.
|
||||
* - `false` if using this field requires it to be the only criterion in query.
|
||||
*/
|
||||
public data class SearchCapability(
|
||||
/** The searchable field that this capability applies to. */
|
||||
@JvmField public val field: SearchableField,
|
||||
/** The set of supported criteria types for this field. */
|
||||
@JvmField public val criteriaTypes: Set<KClass<out QueryCriteria<*>>>,
|
||||
@JvmField public val multiValue: Boolean,
|
||||
@JvmField public val otherCriteria: Boolean,
|
||||
/** Indicates whether the field supports multiple values. */
|
||||
@JvmField public val isMultiple: Boolean,
|
||||
/** Specifies whether the field can be used alongside other criteria. */
|
||||
@JvmField public val isExclusive: Boolean = false,
|
||||
)
|
||||
|
||||
@@ -73,62 +73,52 @@ internal class MangaDexParser(context: MangaLoaderContext) : AbstractMangaParser
|
||||
SearchCapability(
|
||||
field = TAG,
|
||||
criteriaTypes = setOf(Include::class, Exclude::class),
|
||||
multiValue = true,
|
||||
otherCriteria = true,
|
||||
isMultiple = true,
|
||||
),
|
||||
SearchCapability(
|
||||
field = TITLE_NAME,
|
||||
criteriaTypes = setOf(Match::class),
|
||||
multiValue = false,
|
||||
otherCriteria = true,
|
||||
isMultiple = false,
|
||||
),
|
||||
SearchCapability(
|
||||
field = STATE,
|
||||
criteriaTypes = setOf(Include::class),
|
||||
multiValue = true,
|
||||
otherCriteria = true,
|
||||
isMultiple = true,
|
||||
),
|
||||
SearchCapability(
|
||||
field = AUTHOR,
|
||||
criteriaTypes = setOf(Include::class),
|
||||
multiValue = true,
|
||||
otherCriteria = true,
|
||||
isMultiple = true,
|
||||
),
|
||||
SearchCapability(
|
||||
field = CONTENT_TYPE,
|
||||
criteriaTypes = setOf(Include::class),
|
||||
multiValue = true,
|
||||
otherCriteria = true,
|
||||
isMultiple = true,
|
||||
),
|
||||
SearchCapability(
|
||||
field = CONTENT_RATING,
|
||||
criteriaTypes = setOf(Include::class),
|
||||
multiValue = true,
|
||||
otherCriteria = true,
|
||||
isMultiple = true,
|
||||
),
|
||||
SearchCapability(
|
||||
field = DEMOGRAPHIC,
|
||||
criteriaTypes = setOf(Include::class),
|
||||
multiValue = true,
|
||||
otherCriteria = true,
|
||||
isMultiple = true,
|
||||
),
|
||||
SearchCapability(
|
||||
field = ORIGINAL_LANGUAGE,
|
||||
criteriaTypes = setOf(Include::class),
|
||||
multiValue = true,
|
||||
otherCriteria = true,
|
||||
isMultiple = true,
|
||||
),
|
||||
SearchCapability(
|
||||
field = LANGUAGE,
|
||||
criteriaTypes = setOf(Include::class),
|
||||
multiValue = true,
|
||||
otherCriteria = true,
|
||||
isMultiple = true,
|
||||
),
|
||||
SearchCapability(
|
||||
field = PUBLICATION_YEAR,
|
||||
criteriaTypes = setOf(Match::class),
|
||||
multiValue = false,
|
||||
otherCriteria = true,
|
||||
isMultiple = false,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -41,26 +41,23 @@ internal abstract class MangaboxParser(
|
||||
SearchCapability(
|
||||
field = TAG,
|
||||
criteriaTypes = setOf(Include::class, Exclude::class),
|
||||
multiValue = true,
|
||||
otherCriteria = true,
|
||||
isMultiple = true,
|
||||
),
|
||||
SearchCapability(
|
||||
field = TITLE_NAME,
|
||||
criteriaTypes = setOf(Match::class),
|
||||
multiValue = false,
|
||||
otherCriteria = true,
|
||||
isMultiple = false,
|
||||
),
|
||||
SearchCapability(
|
||||
field = STATE,
|
||||
criteriaTypes = setOf(Include::class),
|
||||
multiValue = true,
|
||||
otherCriteria = true,
|
||||
isMultiple = true,
|
||||
),
|
||||
SearchCapability(
|
||||
field = AUTHOR,
|
||||
criteriaTypes = setOf(Include::class),
|
||||
multiValue = false,
|
||||
otherCriteria = false,
|
||||
isMultiple = false,
|
||||
isExclusive = true,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -43,20 +43,18 @@ internal class Mangairo(context: MangaLoaderContext) :
|
||||
SearchCapability(
|
||||
field = TAG,
|
||||
criteriaTypes = setOf(Include::class),
|
||||
multiValue = false,
|
||||
otherCriteria = true,
|
||||
isMultiple = false,
|
||||
),
|
||||
SearchCapability(
|
||||
field = TITLE_NAME,
|
||||
criteriaTypes = setOf(Match::class),
|
||||
multiValue = false,
|
||||
otherCriteria = false,
|
||||
isMultiple = false,
|
||||
isExclusive = true,
|
||||
),
|
||||
SearchCapability(
|
||||
field = STATE,
|
||||
criteriaTypes = setOf(Include::class),
|
||||
multiValue = false,
|
||||
otherCriteria = true,
|
||||
isMultiple = false,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -32,20 +32,18 @@ internal class Mangakakalot(context: MangaLoaderContext) :
|
||||
SearchCapability(
|
||||
field = TAG,
|
||||
criteriaTypes = setOf(Include::class),
|
||||
multiValue = false,
|
||||
otherCriteria = true,
|
||||
isMultiple = false,
|
||||
),
|
||||
SearchCapability(
|
||||
field = TITLE_NAME,
|
||||
criteriaTypes = setOf(Match::class),
|
||||
multiValue = false,
|
||||
otherCriteria = false,
|
||||
isMultiple = false,
|
||||
isExclusive = true,
|
||||
),
|
||||
SearchCapability(
|
||||
field = STATE,
|
||||
criteriaTypes = setOf(Include::class),
|
||||
multiValue = false,
|
||||
otherCriteria = true,
|
||||
isMultiple = false,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -35,20 +35,18 @@ internal class MangakakalotTv(context: MangaLoaderContext) :
|
||||
SearchCapability(
|
||||
field = TAG,
|
||||
criteriaTypes = setOf(Include::class),
|
||||
multiValue = false,
|
||||
otherCriteria = true,
|
||||
isMultiple = false,
|
||||
),
|
||||
SearchCapability(
|
||||
field = TITLE_NAME,
|
||||
criteriaTypes = setOf(Match::class),
|
||||
multiValue = false,
|
||||
otherCriteria = false,
|
||||
isMultiple = false,
|
||||
isExclusive = true,
|
||||
),
|
||||
SearchCapability(
|
||||
field = STATE,
|
||||
criteriaTypes = setOf(Include::class),
|
||||
multiValue = false,
|
||||
otherCriteria = true,
|
||||
isMultiple = false,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -87,10 +87,10 @@ internal fun convertToMangaListFilter(searchQuery: MangaSearchQuery): MangaListF
|
||||
}
|
||||
|
||||
internal fun MangaSearchQueryCapabilities.toMangaListFilterCapabilities() = MangaListFilterCapabilities(
|
||||
isMultipleTagsSupported = capabilities.any { x -> x.field == TAG && x.multiValue },
|
||||
isMultipleTagsSupported = capabilities.any { x -> x.field == TAG && x.isMultiple },
|
||||
isTagsExclusionSupported = capabilities.any { x -> x.field == TAG && x.criteriaTypes.contains(Exclude::class) },
|
||||
isSearchSupported = capabilities.any { x -> x.field == TITLE_NAME },
|
||||
isSearchWithFiltersSupported = capabilities.any { x -> x.field == TITLE_NAME && x.otherCriteria },
|
||||
isSearchWithFiltersSupported = capabilities.any { x -> x.field == TITLE_NAME && !x.isExclusive },
|
||||
isYearSupported = capabilities.any { x -> x.field == PUBLICATION_YEAR && x.criteriaTypes.contains(Match::class) },
|
||||
isYearRangeSupported = capabilities.any { x -> x.field == PUBLICATION_YEAR && x.criteriaTypes.contains(Range::class) },
|
||||
isOriginalLocaleSupported = capabilities.any { x -> x.field == ORIGINAL_LANGUAGE },
|
||||
@@ -102,80 +102,76 @@ internal fun MangaListFilterCapabilities.toMangaSearchQueryCapabilities(): Manga
|
||||
capabilities = setOfNotNull(
|
||||
isMultipleTagsSupported.takeIf { it }?.let {
|
||||
SearchCapability(
|
||||
field = TAG, criteriaTypes = setOf(Include::class), multiValue = true, otherCriteria = true,
|
||||
field = TAG,
|
||||
criteriaTypes = setOf(Include::class),
|
||||
isMultiple = true,
|
||||
)
|
||||
},
|
||||
isTagsExclusionSupported.takeIf { it }?.let {
|
||||
SearchCapability(
|
||||
field = TAG, criteriaTypes = setOf(Exclude::class), multiValue = true, otherCriteria = true,
|
||||
field = TAG,
|
||||
criteriaTypes = setOf(Exclude::class),
|
||||
isMultiple = true,
|
||||
)
|
||||
},
|
||||
isSearchSupported.takeIf { it }?.let {
|
||||
SearchCapability(
|
||||
field = TITLE_NAME,
|
||||
criteriaTypes = setOf(Match::class),
|
||||
multiValue = false,
|
||||
otherCriteria = false,
|
||||
isMultiple = false,
|
||||
isExclusive = true,
|
||||
)
|
||||
},
|
||||
isSearchWithFiltersSupported.takeIf { it }?.let {
|
||||
SearchCapability(
|
||||
field = TITLE_NAME,
|
||||
criteriaTypes = setOf(Match::class),
|
||||
multiValue = false,
|
||||
otherCriteria = true,
|
||||
isMultiple = false,
|
||||
)
|
||||
},
|
||||
isYearSupported.takeIf { it }?.let {
|
||||
SearchCapability(
|
||||
field = PUBLICATION_YEAR,
|
||||
criteriaTypes = setOf(Match::class),
|
||||
multiValue = false,
|
||||
otherCriteria = true,
|
||||
isMultiple = false,
|
||||
)
|
||||
},
|
||||
isYearRangeSupported.takeIf { it }?.let {
|
||||
SearchCapability(
|
||||
field = PUBLICATION_YEAR,
|
||||
criteriaTypes = setOf(Range::class),
|
||||
multiValue = false,
|
||||
otherCriteria = true,
|
||||
isMultiple = false,
|
||||
)
|
||||
},
|
||||
isOriginalLocaleSupported.takeIf { it }?.let {
|
||||
SearchCapability(
|
||||
field = ORIGINAL_LANGUAGE,
|
||||
criteriaTypes = setOf(Include::class),
|
||||
multiValue = true,
|
||||
otherCriteria = true,
|
||||
isMultiple = true,
|
||||
)
|
||||
},
|
||||
SearchCapability(
|
||||
field = LANGUAGE,
|
||||
criteriaTypes = setOf(Include::class),
|
||||
multiValue = true,
|
||||
otherCriteria = true,
|
||||
isMultiple = true,
|
||||
),
|
||||
SearchCapability(
|
||||
field = STATE, criteriaTypes = setOf(Include::class), multiValue = true, otherCriteria = true,
|
||||
field = STATE, criteriaTypes = setOf(Include::class), isMultiple = true,
|
||||
),
|
||||
SearchCapability(
|
||||
field = CONTENT_TYPE,
|
||||
criteriaTypes = setOf(Include::class),
|
||||
multiValue = true,
|
||||
otherCriteria = true,
|
||||
isMultiple = true,
|
||||
),
|
||||
SearchCapability(
|
||||
field = CONTENT_RATING,
|
||||
criteriaTypes = setOf(Include::class),
|
||||
multiValue = true,
|
||||
otherCriteria = true,
|
||||
isMultiple = true,
|
||||
),
|
||||
SearchCapability(
|
||||
field = DEMOGRAPHIC,
|
||||
criteriaTypes = setOf(Include::class),
|
||||
multiValue = true,
|
||||
otherCriteria = true,
|
||||
isMultiple = true,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -14,10 +14,10 @@ class MangaSearchQueryCapabilitiesTest {
|
||||
|
||||
private val capabilities = MangaSearchQueryCapabilities(
|
||||
capabilities = setOf(
|
||||
SearchCapability(TITLE_NAME, setOf(Match::class), multiValue = false, otherCriteria = false),
|
||||
SearchCapability(TAG, setOf(Include::class, Exclude::class), multiValue = true, otherCriteria = true),
|
||||
SearchCapability(PUBLICATION_YEAR, setOf(Range::class), multiValue = false, otherCriteria = true),
|
||||
SearchCapability(STATE, setOf(Include::class), multiValue = false, otherCriteria = true),
|
||||
SearchCapability(TITLE_NAME, setOf(Match::class), isMultiple = false, isExclusive = true),
|
||||
SearchCapability(TAG, setOf(Include::class, Exclude::class), isMultiple = true, isExclusive = false),
|
||||
SearchCapability(PUBLICATION_YEAR, setOf(Range::class), isMultiple = false, isExclusive = false),
|
||||
SearchCapability(STATE, setOf(Include::class), isMultiple = false, isExclusive = false),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user