diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/exception/ContentUnavailableException.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/exception/ContentUnavailableException.kt index fba33577..65f47f69 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/exception/ContentUnavailableException.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/exception/ContentUnavailableException.kt @@ -1,3 +1,3 @@ package org.koitharu.kotatsu.parsers.exception -class ContentUnavailableException(message: String) : RuntimeException(message) \ No newline at end of file +public class ContentUnavailableException(message: String) : RuntimeException(message) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/exception/GraphQLException.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/exception/GraphQLException.kt index b89ab439..e8bb16a2 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/exception/GraphQLException.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/exception/GraphQLException.kt @@ -2,14 +2,15 @@ package org.koitharu.kotatsu.parsers.exception import okio.IOException import org.json.JSONArray +import org.koitharu.kotatsu.parsers.InternalParsersApi import org.koitharu.kotatsu.parsers.util.json.mapJSON -class GraphQLException(private val errors: JSONArray) : IOException() { +public class GraphQLException @InternalParsersApi constructor(private val errors: JSONArray) : IOException() { - val messages = errors.mapJSON { + public val messages = errors.mapJSON { it.getString("message") } override val message: String get() = messages.joinToString("\n") -} \ No newline at end of file +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/exception/NotFoundException.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/exception/NotFoundException.kt index b9271577..fb1a03e2 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/exception/NotFoundException.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/exception/NotFoundException.kt @@ -3,7 +3,7 @@ package org.koitharu.kotatsu.parsers.exception import org.jsoup.HttpStatusException import java.net.HttpURLConnection -class NotFoundException( +public class NotFoundException( message: String, url: String, -) : HttpStatusException(message, HttpURLConnection.HTTP_NOT_FOUND, url) \ No newline at end of file +) : HttpStatusException(message, HttpURLConnection.HTTP_NOT_FOUND, url) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/exception/ParseException.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/exception/ParseException.kt index b2ad8a43..4ba7c53f 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/exception/ParseException.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/exception/ParseException.kt @@ -2,8 +2,8 @@ package org.koitharu.kotatsu.parsers.exception import org.koitharu.kotatsu.parsers.InternalParsersApi -class ParseException @InternalParsersApi @JvmOverloads constructor( - val shortMessage: String?, - val url: String, +public class ParseException @InternalParsersApi @JvmOverloads constructor( + public val shortMessage: String?, + public val url: String, cause: Throwable? = null, -) : RuntimeException("$shortMessage at $url", cause) \ No newline at end of file +) : RuntimeException("$shortMessage at $url", cause) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/exception/TooManyRequestExceptions.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/exception/TooManyRequestExceptions.kt index 1e576928..cf35fe0a 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/exception/TooManyRequestExceptions.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/exception/TooManyRequestExceptions.kt @@ -4,18 +4,18 @@ import okio.IOException import java.time.Instant import java.time.temporal.ChronoUnit -class TooManyRequestExceptions( - val url: String, +public class TooManyRequestExceptions( + public val url: String, retryAfter: Long, ) : IOException("Too man requests") { - val retryAt: Instant? = if (retryAfter > 0 && retryAfter < Long.MAX_VALUE) { + public val retryAt: Instant? = if (retryAfter > 0 && retryAfter < Long.MAX_VALUE) { Instant.now().plusMillis(retryAfter) } else { null } - fun getRetryDelay(): Long { + public fun getRetryDelay(): Long { if (retryAt == null) { return -1L } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/ContentRating.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/ContentRating.kt index eb5c8614..6658f1e8 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/ContentRating.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/ContentRating.kt @@ -1,6 +1,6 @@ package org.koitharu.kotatsu.parsers.model -enum class ContentRating { +public enum class ContentRating { SAFE, SUGGESTIVE, ADULT diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/ContentType.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/ContentType.kt index 695440fb..d1045dcb 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/ContentType.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/ContentType.kt @@ -1,6 +1,6 @@ package org.koitharu.kotatsu.parsers.model -enum class ContentType { +public enum class ContentType { /** * Standard manga, manhua, webtoons, etc diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/Demographic.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/Demographic.kt index d94bda67..51b52f80 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/Demographic.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/Demographic.kt @@ -1,6 +1,6 @@ package org.koitharu.kotatsu.parsers.model -enum class Demographic { +public enum class Demographic { SHOUNEN, SHOUJO, SEINEN, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/Favicon.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/Favicon.kt index 41e092ff..5b110162 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/Favicon.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/Favicon.kt @@ -2,14 +2,14 @@ package org.koitharu.kotatsu.parsers.model import okhttp3.HttpUrl.Companion.toHttpUrl -class Favicon internal constructor( - @JvmField val url: String, - @JvmField val size: Int, +public class Favicon( + @JvmField public val url: String, + @JvmField public val size: Int, @JvmField internal val rel: String?, ) : Comparable { @JvmField - val type: String = url.toHttpUrl().pathSegments.last() + public val type: String = url.toHttpUrl().pathSegments.last() .substringAfterLast('.', "").lowercase() override fun compareTo(other: Favicon): Int { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/Favicons.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/Favicons.kt index 37c2d1d4..b11add35 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/Favicons.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/Favicons.kt @@ -1,8 +1,8 @@ package org.koitharu.kotatsu.parsers.model -class Favicons internal constructor( +public class Favicons( favicons: Collection, - @JvmField val referer: String?, + @JvmField public val referer: String?, ) : Collection { private val icons = favicons.sortedDescending() @@ -18,7 +18,7 @@ class Favicons internal constructor( override fun iterator(): Iterator = icons.iterator() - operator fun minus(victim: Favicon): Favicons = Favicons( + public operator fun minus(victim: Favicon): Favicons = Favicons( favicons = icons.filterNot { it == victim }, referer = referer, ) @@ -30,7 +30,7 @@ class Favicons internal constructor( * @param types supported file types, e.g. png, svg, ico. May be null but not empty */ @JvmOverloads - fun find(size: Int, types: Set? = null): Favicon? { + public fun find(size: Int, types: Set? = null): Favicon? { if (icons.isEmpty()) { return null } @@ -48,11 +48,12 @@ class Favicons internal constructor( return result } - companion object { + public companion object { @JvmStatic - fun empty() = Favicons(emptySet(), null) + public val EMPTY: Favicons = Favicons(emptySet(), null) - fun single(url: String) = Favicons(setOf(Favicon(url, 0, null)), null) + @JvmStatic + public fun single(url: String): Favicons = Favicons(setOf(Favicon(url, 0, null)), null) } } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaListFilter.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaListFilter.kt index c6c7d2d1..b357edd9 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaListFilter.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaListFilter.kt @@ -17,19 +17,24 @@ public data class MangaListFilter( @JvmField val yearTo: Int = YEAR_UNKNOWN, ) { - public fun isEmpty(): Boolean = tags.isEmpty() && + private fun isNonSearchOptionsEmpty(): Boolean = tags.isEmpty() && tagsExclude.isEmpty() && locale == null && originalLocale == null && states.isEmpty() && contentRating.isEmpty() && - query == null && year == YEAR_UNKNOWN && yearFrom == YEAR_UNKNOWN && yearTo == YEAR_UNKNOWN && types.isEmpty() && demographics.isEmpty() + public fun isEmpty(): Boolean = isNonSearchOptionsEmpty() && query.isNullOrEmpty() + + public fun isNotEmpty(): Boolean = !isEmpty() + + public fun hasNonSearchOptions(): Boolean = !isNonSearchOptionsEmpty() + public companion object { @JvmStatic diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaPage.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaPage.kt index 2c463761..384692ad 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaPage.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/MangaPage.kt @@ -2,23 +2,23 @@ package org.koitharu.kotatsu.parsers.model import org.koitharu.kotatsu.parsers.MangaParser -class MangaPage( +public class MangaPage( /** * Unique identifier for manga */ - @JvmField val id: Long, + @JvmField public val id: Long, /** * Relative url to page (**without** a domain) or any other uri. * Used principally in parsers. * May contain link to image or html page. * @see MangaParser.getPageUrl */ - @JvmField val url: String, + @JvmField public val url: String, /** * Absolute url of the small page image if exists, null otherwise */ - @JvmField val preview: String?, - @JvmField val source: MangaSource, + @JvmField public val preview: String?, + @JvmField public val source: MangaSource, ) { override fun equals(other: Any?): Boolean { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/SortOrder.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/SortOrder.kt index 0b60e306..6754405f 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/model/SortOrder.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/model/SortOrder.kt @@ -1,6 +1,6 @@ package org.koitharu.kotatsu.parsers.model -enum class SortOrder { +public enum class SortOrder { UPDATED, UPDATED_ASC, POPULARITY, diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/network/OkHttpWebClient.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/network/OkHttpWebClient.kt index 31a8c637..c641b247 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/network/OkHttpWebClient.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/network/OkHttpWebClient.kt @@ -13,7 +13,7 @@ import org.koitharu.kotatsu.parsers.util.await import org.koitharu.kotatsu.parsers.util.parseJson import java.net.HttpURLConnection -class OkHttpWebClient( +public class OkHttpWebClient( private val httpClient: OkHttpClient, private val mangaSource: MangaSource, ) : WebClient { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/network/UserAgents.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/network/UserAgents.kt index 5a4b500e..636fe9c9 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/network/UserAgents.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/network/UserAgents.kt @@ -1,16 +1,17 @@ package org.koitharu.kotatsu.parsers.network -object UserAgents { +public object UserAgents { - const val CHROME_MOBILE = + public const val CHROME_MOBILE: String = "Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.5735.196 Mobile Safari/537.36" - const val FIREFOX_MOBILE = "Mozilla/5.0 (Android 14; Mobile; LG-M255; rv:123.0) Gecko/123.0 Firefox/123.0" + public const val FIREFOX_MOBILE: String = + "Mozilla/5.0 (Android 14; Mobile; LG-M255; rv:123.0) Gecko/123.0 Firefox/123.0" - const val CHROME_DESKTOP = + public const val CHROME_DESKTOP: String = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36" - const val FIREFOX_DESKTOP = "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/116.0" + public const val FIREFOX_DESKTOP: String = "Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/116.0" - const val KOTATSU = "Kotatsu/6.8 (Android 13;;; en)" + public const val KOTATSU: String = "Kotatsu/6.8 (Android 13;;; en)" } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/network/WebClient.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/network/WebClient.kt index bddbb270..8b243714 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/network/WebClient.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/network/WebClient.kt @@ -6,54 +6,55 @@ import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.Response import org.json.JSONObject -interface WebClient { +public interface WebClient { /** * Do a GET http request to specific url * @param url */ - suspend fun httpGet(url: String): Response = httpGet(url.toHttpUrl()) + public suspend fun httpGet(url: String): Response = httpGet(url.toHttpUrl()) - suspend fun httpGet(url: String, extraHeaders: Headers?): Response = httpGet(url.toHttpUrl(), extraHeaders) + public suspend fun httpGet(url: String, extraHeaders: Headers?): Response = httpGet(url.toHttpUrl(), extraHeaders) /** * Do a GET http request to specific url * @param url */ - suspend fun httpGet(url: HttpUrl): Response = httpGet(url, null) + public suspend fun httpGet(url: HttpUrl): Response = httpGet(url, null) /** * Do a GET http request to specific url * @param url * @param extraHeaders additional HTTP headers for request */ - suspend fun httpGet(url: HttpUrl, extraHeaders: Headers?): Response + public suspend fun httpGet(url: HttpUrl, extraHeaders: Headers?): Response /** * Do a HEAD http request to specific url * @param url */ - suspend fun httpHead(url: String): Response = httpHead(url.toHttpUrl()) + public suspend fun httpHead(url: String): Response = httpHead(url.toHttpUrl()) /** * Do a HEAD http request to specific url * @param url */ - suspend fun httpHead(url: HttpUrl): Response + public suspend fun httpHead(url: HttpUrl): Response /** * Do a POST http request to specific url with `multipart/form-data` payload * @param url * @param form payload as key=>value map */ - suspend fun httpPost(url: String, form: Map): Response = httpPost(url.toHttpUrl(), form, null) + public suspend fun httpPost(url: String, form: Map): Response = + httpPost(url.toHttpUrl(), form, null) /** * Do a POST http request to specific url with `multipart/form-data` payload * @param url * @param form payload as key=>value map */ - suspend fun httpPost(url: HttpUrl, form: Map): Response = httpPost(url, form, null) + public suspend fun httpPost(url: HttpUrl, form: Map): Response = httpPost(url, form, null) /** * Do a POST http request to specific url with `multipart/form-data` payload @@ -61,21 +62,21 @@ interface WebClient { * @param form payload as key=>value map * @param extraHeaders additional HTTP headers for request */ - suspend fun httpPost(url: HttpUrl, form: Map, extraHeaders: Headers?): Response + public suspend fun httpPost(url: HttpUrl, form: Map, extraHeaders: Headers?): Response /** * Do a POST http request to specific url with `multipart/form-data` payload * @param url * @param payload payload as `key=value` string with `&` separator */ - suspend fun httpPost(url: String, payload: String): Response = httpPost(url.toHttpUrl(), payload, null) + public suspend fun httpPost(url: String, payload: String): Response = httpPost(url.toHttpUrl(), payload, null) /** * Do a POST http request to specific url with `multipart/form-data` payload * @param url * @param payload payload as `key=value` string with `&` separator */ - suspend fun httpPost(url: HttpUrl, payload: String): Response = httpPost(url, payload, null) + public suspend fun httpPost(url: HttpUrl, payload: String): Response = httpPost(url, payload, null) /** * Do a POST http request to specific url with `multipart/form-data` payload @@ -83,21 +84,21 @@ interface WebClient { * @param payload payload as `key=value` string with `&` separator * @param extraHeaders additional HTTP headers for request */ - suspend fun httpPost(url: HttpUrl, payload: String, extraHeaders: Headers?): Response + public suspend fun httpPost(url: HttpUrl, payload: String, extraHeaders: Headers?): Response /** * Do a POST http request to specific url with json payload * @param url * @param body */ - suspend fun httpPost(url: String, body: JSONObject): Response = httpPost(url.toHttpUrl(), body, null) + public suspend fun httpPost(url: String, body: JSONObject): Response = httpPost(url.toHttpUrl(), body, null) /** * Do a POST http request to specific url with json payload * @param url * @param body */ - suspend fun httpPost(url: HttpUrl, body: JSONObject): Response = httpPost(url, body, null) + public suspend fun httpPost(url: HttpUrl, body: JSONObject): Response = httpPost(url, body, null) /** * Do a POST http request to specific url with json payload @@ -105,12 +106,12 @@ interface WebClient { * @param body * @param extraHeaders additional HTTP headers for request */ - suspend fun httpPost(url: HttpUrl, body: JSONObject, extraHeaders: Headers?): Response + public suspend fun httpPost(url: HttpUrl, body: JSONObject, extraHeaders: Headers?): Response /** * Do a GraphQL request to specific url * @param endpoint an url * @param query GraphQL request payload */ - suspend fun graphQLQuery(endpoint: String, query: String): JSONObject + public suspend fun graphQLQuery(endpoint: String, query: String): JSONObject } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/BatoToParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/BatoToParser.kt index 76170f9c..c6fbce98 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/BatoToParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/BatoToParser.kt @@ -177,12 +177,12 @@ internal class BatoToParser(context: MangaLoaderContext) : PagedMangaParser( append("&genres=") if (filter.tags.isNotEmpty()) { - appendAll(filter.tags, ",") { it.key } + filter.tags.joinTo(this, ",") { it.key } } append("|") if (filter.tagsExclude.isNotEmpty()) { - appendAll(filter.tagsExclude, ",") { it.key } + filter.tagsExclude.joinTo(this, ",") { it.key } } if (filter.contentRating.isNotEmpty()) { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaPark.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaPark.kt index 3c75e13e..07b7775e 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaPark.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/all/MangaPark.kt @@ -79,14 +79,10 @@ internal class MangaPark(context: MangaLoaderContext) : } append("&genres=") - if (filter.tags.isNotEmpty()) { - appendAll(filter.tags, ",") { it.key } - } + filter.tags.joinTo(this, ",") { it.key } append("|") - if (filter.tagsExclude.isNotEmpty()) { - appendAll(filter.tagsExclude, ",") { it.key } - } + filter.tagsExclude.joinTo(this, ",") { it.key } if (filter.contentRating.isNotEmpty()) { filter.contentRating.oneOrThrowIfMany()?.let { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/iken/IkenParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/iken/IkenParser.kt index 0e6c19fc..5f3998d7 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/iken/IkenParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/iken/IkenParser.kt @@ -61,7 +61,7 @@ internal abstract class IkenParser( if (filter.tags.isNotEmpty()) { append("&genreIds=") - appendAll(filter.tags, ",") { it.key } + filter.tags.joinTo(this, ",") { it.key } } append("&seriesType=&seriesStatus=") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/DesuMeParser.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/DesuMeParser.kt index d8a946c5..151f6548 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/DesuMeParser.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/site/ru/DesuMeParser.kt @@ -56,7 +56,7 @@ internal class DesuMeParser(context: MangaLoaderContext) : PagedMangaParser(cont append(page) if (filter.tags.isNotEmpty()) { append("&genres=") - appendAll(filter.tags, ",") { it.key } + filter.tags.joinTo(this, ",") { it.key } } if (!filter.query.isNullOrEmpty()) { append("&search=") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Collection.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Collection.kt index 45cdc948..f16691d7 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Collection.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Collection.kt @@ -6,7 +6,7 @@ import androidx.collection.ArrayMap import androidx.collection.ArraySet import java.util.* -fun MutableCollection.replaceWith(subject: Iterable) { +public fun MutableCollection.replaceWith(subject: Iterable) { clear() addAll(subject) } @@ -23,7 +23,7 @@ fun List.medianOrNull(): T? = when { else -> get((size / 2).coerceIn(indices)) } -inline fun Collection.mapToSet(transform: (T) -> R): Set { +public inline fun Collection.mapToSet(transform: (T) -> R): Set { return mapTo(ArraySet(size), transform) } @@ -35,13 +35,13 @@ inline fun Collection.mapNotNullToSet(transform: (T) -> R?): Set { return destination } -inline fun Array.mapToArray(transform: (T) -> R): Array = Array(size) { i -> +public inline fun Array.mapToArray(transform: (T) -> R): Array = Array(size) { i -> transform(get(i)) } fun List>.toMutableMap(): MutableMap = toMap(ArrayMap(size)) -fun MutableList.move(sourceIndex: Int, targetIndex: Int) { +public fun MutableList.move(sourceIndex: Int, targetIndex: Int) { if (sourceIndex <= targetIndex) { Collections.rotate(subList(sourceIndex, targetIndex + 1), -1) } else { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/CookieJar.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/CookieJar.kt index 8e9ce7c3..79258841 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/CookieJar.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/CookieJar.kt @@ -8,7 +8,7 @@ import okhttp3.HttpUrl private const val SCHEME_HTTPS = "https" -fun CookieJar.insertCookies(domain: String, vararg cookies: String) { +public fun CookieJar.insertCookies(domain: String, vararg cookies: String) { val url = safeUrlOf(domain) ?: return saveFromResponse( url, @@ -18,17 +18,17 @@ fun CookieJar.insertCookies(domain: String, vararg cookies: String) { ) } -fun CookieJar.insertCookie(domain: String, cookie: Cookie) { +public fun CookieJar.insertCookie(domain: String, cookie: Cookie) { val url = safeUrlOf(domain) ?: return saveFromResponse(url, listOf(cookie)) } -fun CookieJar.getCookies(domain: String): List { +public fun CookieJar.getCookies(domain: String): List { val url = safeUrlOf(domain) ?: return emptyList() return loadForRequest(url) } -fun CookieJar.copyCookies(oldDomain: String, newDomain: String, names: Array? = null) { +public fun CookieJar.copyCookies(oldDomain: String, newDomain: String, names: Array? = null) { val url = HttpUrl.Builder() .scheme(SCHEME_HTTPS) .host(oldDomain) @@ -47,4 +47,4 @@ private fun safeUrlOf(domain: String): HttpUrl? = try { .build() } catch (_: IllegalArgumentException) { null -} \ No newline at end of file +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/CryptoAES.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/CryptoAES.kt index 6f4cd1d9..4a9e2946 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/CryptoAES.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/CryptoAES.kt @@ -1,11 +1,13 @@ package org.koitharu.kotatsu.parsers.util +import org.koitharu.kotatsu.parsers.InternalParsersApi import org.koitharu.kotatsu.parsers.MangaLoaderContext import java.security.MessageDigest import java.util.* import javax.crypto.Cipher import javax.crypto.spec.IvParameterSpec import javax.crypto.spec.SecretKeySpec +import kotlin.Throws private const val HASH_CIPHER = "AES/CBC/PKCS7PADDING" private const val AES = "AES" @@ -14,7 +16,8 @@ private const val KDF_DIGEST = "MD5" /** * Conforming with CryptoJS AES method */ -class CryptoAES( +@InternalParsersApi +public class CryptoAES( private val context: MangaLoaderContext, ) { @@ -27,7 +30,7 @@ class CryptoAES( * @param password passphrase */ @Throws(Exception::class) - fun decrypt(cipherText: String, password: String): String { + public fun decrypt(cipherText: String, password: String): String { val ctBytes = context.decodeBase64(cipherText) val saltBytes = Arrays.copyOfRange(ctBytes, 8, 16) val cipherTextBytes = Arrays.copyOfRange(ctBytes, 16, ctBytes.size) @@ -48,7 +51,7 @@ class CryptoAES( * @param ivBytes iv as a bytearray */ @Throws(Exception::class) - fun decrypt(cipherText: String, keyBytes: ByteArray, ivBytes: ByteArray): String { + public fun decrypt(cipherText: String, keyBytes: ByteArray, ivBytes: ByteArray): String { val cipherTextBytes = context.decodeBase64(cipherText) return decryptAES(cipherTextBytes, keyBytes, ivBytes) } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Jsoup.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Jsoup.kt index 9d2e0e59..1a431000 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Jsoup.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Jsoup.kt @@ -7,6 +7,7 @@ import org.jsoup.nodes.Element import org.jsoup.select.Elements import org.jsoup.select.QueryParser import org.jsoup.select.Selector +import org.koitharu.kotatsu.parsers.InternalParsersApi import org.koitharu.kotatsu.parsers.exception.ParseException val Element.host: String? @@ -154,8 +155,22 @@ fun Element.attrOrNull(vararg names: String): String? { return null } +@InternalParsersApi @JvmOverloads -fun Element.src(names: Array = arrayOf("data-src", "data-cfsrc", "data-original", "data-cdn", "data-sizes", "data-lazy-src", "data-srcset", "original-src", "data-wpfc-original-src", "src")): String? { +public fun Element.src( + names: Array = arrayOf( + "data-src", + "data-cfsrc", + "data-original", + "data-cdn", + "data-sizes", + "data-lazy-src", + "data-srcset", + "original-src", + "data-wpfc-original-src", + "src", + ), +): String? { for (name in names) { val value = attrAsAbsoluteUrlOrNull(name) if (value != null) { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/MangaParserEnv.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/MangaParserEnv.kt index 845c5e29..8f1e4155 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/MangaParserEnv.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/MangaParserEnv.kt @@ -17,7 +17,7 @@ import org.koitharu.kotatsu.parsers.model.* * @see [MangaPage.id] */ @InternalParsersApi -fun MangaParser.generateUid(url: String): Long { +public fun MangaParser.generateUid(url: String): Long { var h = 1125899906842597L source.name.forEach { c -> h = 31 * h + c.code @@ -36,7 +36,7 @@ fun MangaParser.generateUid(url: String): Long { * @see [MangaPage.id] */ @InternalParsersApi -fun MangaParser.generateUid(id: Long): Long { +public fun MangaParser.generateUid(id: Long): Long { var h = 1125899906842597L source.name.forEach { c -> h = 31 * h + c.code @@ -46,32 +46,32 @@ fun MangaParser.generateUid(id: Long): Long { } @InternalParsersApi -fun Element.parseFailed(message: String? = null): Nothing { +public fun Element.parseFailed(message: String? = null): Nothing { throw ParseException(message, ownerDocument()?.location() ?: baseUri(), null) } @InternalParsersApi -fun Set?.oneOrThrowIfMany(): MangaTag? = oneOrThrowIfMany( +public fun Set?.oneOrThrowIfMany(): MangaTag? = oneOrThrowIfMany( ErrorMessages.FILTER_MULTIPLE_GENRES_NOT_SUPPORTED, ) @InternalParsersApi -fun Set?.oneOrThrowIfMany(): MangaState? = oneOrThrowIfMany( +public fun Set?.oneOrThrowIfMany(): MangaState? = oneOrThrowIfMany( ErrorMessages.FILTER_MULTIPLE_STATES_NOT_SUPPORTED, ) @InternalParsersApi -fun Set?.oneOrThrowIfMany(): ContentType? = oneOrThrowIfMany( +public fun Set?.oneOrThrowIfMany(): ContentType? = oneOrThrowIfMany( ErrorMessages.FILTER_MULTIPLE_CONTENT_TYPES_NOT_SUPPORTED, ) @InternalParsersApi -fun Set?.oneOrThrowIfMany(): Demographic? = oneOrThrowIfMany( +public fun Set?.oneOrThrowIfMany(): Demographic? = oneOrThrowIfMany( ErrorMessages.FILTER_MULTIPLE_DEMOGRAPHICS_NOT_SUPPORTED, ) @InternalParsersApi -fun Set?.oneOrThrowIfMany(): ContentRating? = oneOrThrowIfMany( +public fun Set?.oneOrThrowIfMany(): ContentRating? = oneOrThrowIfMany( ErrorMessages.FILTER_MULTIPLE_CONTENT_RATING_NOT_SUPPORTED, ) @@ -81,17 +81,19 @@ private fun Set?.oneOrThrowIfMany(msg: String): T? = when { else -> throw IllegalArgumentException(msg) } -val MangaParser.domain: String +public val MangaParser.domain: String get() { return config[configKeyDomain] } -fun MangaParser.getDomain(subdomain: String): String { +@InternalParsersApi +public fun MangaParser.getDomain(subdomain: String): String { val domain = domain return subdomain + "." + domain.removePrefix("www.") } -fun MangaParser.urlBuilder(subdomain: String? = null): HttpUrl.Builder { +@InternalParsersApi +public fun MangaParser.urlBuilder(subdomain: String? = null): HttpUrl.Builder { return HttpUrl.Builder() .scheme("https") .host(if (subdomain == null) domain else "$subdomain.$domain") diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/MangaParsersUtils.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/MangaParsersUtils.kt new file mode 100644 index 00000000..4b5aa04f --- /dev/null +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/MangaParsersUtils.kt @@ -0,0 +1,13 @@ +@file:JvmName("MangaParsersUtils") + +package org.koitharu.kotatsu.parsers.util + +import org.koitharu.kotatsu.parsers.model.MangaListFilter +import kotlin.contracts.contract + +public fun MangaListFilter?.isNullOrEmpty(): Boolean { + contract { + returns(false) implies (this@isNullOrEmpty != null) + } + return this == null || this.isEmpty() +} diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Number.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Number.kt index 464ce78c..390b3109 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Number.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Number.kt @@ -7,7 +7,7 @@ import java.text.NumberFormat import java.util.* import kotlin.math.absoluteValue -fun Number.format(decimals: Int = 0, decPoint: Char = '.', thousandsSep: Char? = ' '): String { +public fun Number.format(decimals: Int = 0, decPoint: Char = '.', thousandsSep: Char? = ' '): String { val formatter = NumberFormat.getInstance(Locale.US) as DecimalFormat val symbols = formatter.decimalFormatSymbols if (thousandsSep != null) { @@ -29,7 +29,7 @@ fun Number.format(decimals: Int = 0, decPoint: Char = '.', thousandsSep: Char? = } } -fun Float.toIntUp(): Int { +public fun Float.toIntUp(): Int { val intValue = toInt() return if ((this - intValue.toFloat()).absoluteValue <= 0.00001) { intValue @@ -38,7 +38,7 @@ fun Float.toIntUp(): Int { } } -infix fun Int.upBy(step: Int): Int { +public infix fun Int.upBy(step: Int): Int { val mod = this % step return if (mod == 0) { this @@ -47,7 +47,7 @@ infix fun Int.upBy(step: Int): Int { } } -fun Number.formatSimple(): String { +public fun Number.formatSimple(): String { val raw = toString() return if (raw.endsWith(".0") || raw.endsWith(",0")) { raw.dropLast(2) @@ -56,7 +56,7 @@ fun Number.formatSimple(): String { } } -inline fun Int.ifZero(defaultVale: () -> Int): Int = if (this == 0) { +public inline fun Int.ifZero(defaultVale: () -> Int): Int = if (this == 0) { defaultVale() } else { this diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/OkHttp.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/OkHttp.kt index 4728cdad..8411dd32 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/OkHttp.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/OkHttp.kt @@ -7,19 +7,19 @@ import okhttp3.Call import okhttp3.Headers import okhttp3.Response -suspend fun Call.await(): Response = suspendCancellableCoroutine { continuation -> +public suspend fun Call.await(): Response = suspendCancellableCoroutine { continuation -> val callback = ContinuationCallCallback(this, continuation) enqueue(callback) continuation.invokeOnCancellation(callback) } -val Response.mimeType: String? +public val Response.mimeType: String? get() = header("content-type")?.takeUnless { it.isEmpty() } -val Response.contentDisposition: String? +public val Response.contentDisposition: String? get() = header("Content-Disposition") -fun Headers.Builder.mergeWith(other: Headers, replaceExisting: Boolean): Headers.Builder { +public fun Headers.Builder.mergeWith(other: Headers, replaceExisting: Boolean): Headers.Builder { for ((name, value) in other) { if (replaceExisting || this[name] == null) { this[name] = value @@ -28,11 +28,11 @@ fun Headers.Builder.mergeWith(other: Headers, replaceExisting: Boolean): Headers return this } -fun Response.copy() = newBuilder() +public fun Response.copy(): Response = newBuilder() .body(peekBody(Long.MAX_VALUE)) .build() -fun Response.Builder.setHeader(name: String, value: String?) = if (value == null) { +public fun Response.Builder.setHeader(name: String, value: String?): Response.Builder = if (value == null) { removeHeader(name) } else { header(name, value) diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Paginator.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Paginator.kt index ab313c53..45e15ca6 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Paginator.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Paginator.kt @@ -3,12 +3,12 @@ package org.koitharu.kotatsu.parsers.util import androidx.collection.SparseArrayCompat import androidx.collection.set -class Paginator(private val initialPageSize: Int) { +public class Paginator internal constructor(private val initialPageSize: Int) { - var firstPage = 1 + public var firstPage: Int = 1 private var pages = SparseArrayCompat() - fun getPage(offset: Int): Int { + internal fun getPage(offset: Int): Int { if (offset == 0) { // just an optimization return firstPage } @@ -19,7 +19,7 @@ class Paginator(private val initialPageSize: Int) { return intPage + firstPage + if (tail == 0) 0 else 1 } - fun onListReceived(offset: Int, page: Int, count: Int) { + internal fun onListReceived(offset: Int, page: Int, count: Int) { pages[offset + count] = if (count > 0) page + 1 else page } } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Parse.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Parse.kt index 33a8164b..b38be2da 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Parse.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Parse.kt @@ -5,10 +5,12 @@ package org.koitharu.kotatsu.parsers.util import okhttp3.Response import okhttp3.ResponseBody import okhttp3.internal.closeQuietly +import org.jetbrains.annotations.Blocking import org.json.JSONArray import org.json.JSONObject import org.jsoup.Jsoup import org.jsoup.nodes.Document +import org.koitharu.kotatsu.parsers.InternalParsersApi import java.text.DateFormat /** @@ -16,7 +18,8 @@ import java.text.DateFormat * @see [parseJson] * @see [parseJsonArray] */ -fun Response.parseHtml(): Document = try { +@Blocking +public fun Response.parseHtml(): Document = try { val body = requireBody() val charset = body.contentType()?.charset()?.name() Jsoup.parse(body.byteStream(), charset, request.url.toString()) @@ -29,7 +32,7 @@ fun Response.parseHtml(): Document = try { * @see [parseJsonArray] * @see [parseHtml] */ -fun Response.parseJson(): JSONObject = try { +public fun Response.parseJson(): JSONObject = try { JSONObject(requireBody().string()) } finally { closeQuietly() @@ -40,19 +43,19 @@ fun Response.parseJson(): JSONObject = try { * @see [parseJson] * @see [parseHtml] */ -fun Response.parseJsonArray(): JSONArray = try { +public fun Response.parseJsonArray(): JSONArray = try { JSONArray(requireBody().string()) } finally { closeQuietly() } -fun Response.parseRaw(): String = try { +public fun Response.parseRaw(): String = try { requireBody().string() } finally { closeQuietly() } -fun Response.parseBytes(): ByteArray = try { +public fun Response.parseBytes(): ByteArray = try { requireBody().bytes() } finally { closeQuietly() @@ -62,7 +65,7 @@ fun Response.parseBytes(): ByteArray = try { * Convert url to relative if it is on [domain] * @return an url relative to the [domain] or absolute, if domain is mismatching */ -fun String.toRelativeUrl(domain: String): String { +public fun String.toRelativeUrl(domain: String): String { if (isEmpty() || startsWith("/")) { return this } @@ -73,13 +76,13 @@ fun String.toRelativeUrl(domain: String): String { * Convert url to absolute with specified domain * @return an absolute url with [domain] if this is relative */ -fun String.toAbsoluteUrl(domain: String): String = when { +public fun String.toAbsoluteUrl(domain: String): String = when { this.startsWith("//") -> "https:$this" this.startsWith('/') -> "https://$domain$this" else -> this } -fun concatUrl(host: String, path: String): String { +public fun concatUrl(host: String, path: String): String { val hostWithSlash = host.endsWith('/') val pathWithSlash = path.startsWith('/') val hostWithScheme = if (host.startsWith("//")) "https:$host" else host @@ -90,7 +93,8 @@ fun concatUrl(host: String, path: String): String { } } -fun DateFormat.tryParse(str: String?): Long = if (str.isNullOrEmpty()) { +@InternalParsersApi +public fun DateFormat.tryParse(str: String?): Long = if (str.isNullOrEmpty()) { // assert(false) { "Date string is null or empty" } 0L } else { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/RelatedMangaFinder.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/RelatedMangaFinder.kt index c118a298..27aa4d33 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/RelatedMangaFinder.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/RelatedMangaFinder.kt @@ -9,13 +9,13 @@ import org.koitharu.kotatsu.parsers.model.Manga import org.koitharu.kotatsu.parsers.model.MangaListFilter import org.koitharu.kotatsu.parsers.model.SortOrder -class RelatedMangaFinder( +public class RelatedMangaFinder( private val parsers: Collection, ) { private val regexWhitespace = Regex("\\s+") - suspend operator fun invoke(seed: Manga): List = coroutineScope { + public suspend operator fun invoke(seed: Manga): List = coroutineScope { parsers.singleOrNull()?.let { parser -> findRelatedImpl(this, parser, seed) } ?: parsers.map { parser -> diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Result.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Result.kt index e5be1293..369bb018 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Result.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/Result.kt @@ -2,7 +2,7 @@ package org.koitharu.kotatsu.parsers.util import kotlinx.coroutines.CancellationException -inline fun T.runCatchingCancellable(block: T.() -> R): Result { +public inline fun T.runCatchingCancellable(block: T.() -> R): Result { return try { Result.success(block()) } catch (e: InterruptedException) { @@ -14,14 +14,14 @@ inline fun T.runCatchingCancellable(block: T.() -> R): Result { } } -inline fun Result.recoverCatchingCancellable(transform: (exception: Throwable) -> R): Result { +public inline fun Result.recoverCatchingCancellable(transform: (exception: Throwable) -> R): Result { return when (val exception = exceptionOrNull()) { null -> this else -> runCatchingCancellable { transform(exception) } } } -inline fun Result.recoverNotNull(transform: (exception: Throwable) -> R?): Result { +public inline fun Result.recoverNotNull(transform: (exception: Throwable) -> R?): Result { return when (val exception = exceptionOrNull()) { null -> this else -> transform(exception)?.let(Result.Companion::success) ?: this diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/SoftSuspendLazy.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/SoftSuspendLazy.kt index 91dcd4cc..6e392905 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/SoftSuspendLazy.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/SoftSuspendLazy.kt @@ -7,14 +7,14 @@ import java.lang.ref.SoftReference /** * Like a [SuspendLazy] but with [SoftReference] under the hood */ -class SoftSuspendLazy( +public class SoftSuspendLazy( private val initializer: suspend () -> T, ) { private val mutex = Mutex() private var cachedValue: SoftReference? = null - suspend fun get(): T { + public suspend fun get(): T { // fast way cachedValue?.get()?.let { return it @@ -29,9 +29,9 @@ class SoftSuspendLazy( } } - suspend fun tryGet() = runCatchingCancellable { get() } + public suspend fun tryGet(): Result = runCatchingCancellable { get() } - fun peek(): T? { + public fun peek(): T? { return cachedValue?.get() } } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/String.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/String.kt index 9f3cbb8d..6d08e974 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/String.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/String.kt @@ -12,7 +12,7 @@ import java.security.MessageDigest import java.util.* import kotlin.math.min -fun String.removeSurrounding(vararg chars: Char): String { +public fun String.removeSurrounding(vararg chars: Char): String { if (isEmpty()) { return this } @@ -24,7 +24,7 @@ fun String.removeSurrounding(vararg chars: Char): String { return this } -fun String.toCamelCase(): String { +public fun String.toCamelCase(): String { if (isEmpty()) { return this } @@ -43,15 +43,15 @@ fun String.toCamelCase(): String { return result.toString() } -fun String.toTitleCase(): String { +public fun String.toTitleCase(): String { return replaceFirstChar { x -> x.uppercase() } } -fun String.toTitleCase(locale: Locale): String { +public fun String.toTitleCase(locale: Locale): String { return replaceFirstChar { x -> x.uppercase(locale) } } -fun String.transliterate(skipMissing: Boolean): String { +public fun String.transliterate(skipMissing: Boolean): String { val cyr = charArrayOf( 'а', 'б', 'в', 'г', 'д', 'е', 'ж', 'з', 'и', 'й', 'к', 'л', 'м', 'н', 'о', 'п', 'р', 'с', 'т', 'у', 'ф', 'х', 'ц', 'ч', 'ш', 'щ', 'ъ', 'ы', 'ь', 'э', 'ю', 'я', 'ё', 'ў', @@ -76,15 +76,15 @@ fun String.transliterate(skipMissing: Boolean): String { } } -fun String.toFileNameSafe() = this.transliterate(false) +public fun String.toFileNameSafe() = this.transliterate(false) .replace(Regex("[^a-z0-9_\\-]", arraySetOf(RegexOption.IGNORE_CASE)), " ") .replace(Regex("\\s+"), "_") -fun String.ellipsize(maxLength: Int) = if (this.length > maxLength) { +public fun String.ellipsize(maxLength: Int) = if (this.length > maxLength) { this.take(maxLength - 1) + Typography.ellipsis } else this -fun String.splitTwoParts(delimiter: Char): Pair? { +public fun String.splitTwoParts(delimiter: Char): Pair? { val indices = MutableIntList(4) for ((i, c) in this.withIndex()) { if (c == delimiter) { @@ -98,13 +98,13 @@ fun String.splitTwoParts(delimiter: Char): Pair? { return substring(0, index) to substring(index + 1) } -fun String.urlEncoded(): String = URLEncoder.encode(this, Charsets.UTF_8.name()) +public fun String.urlEncoded(): String = URLEncoder.encode(this, Charsets.UTF_8.name()) -fun String.urlDecode(): String = URLDecoder.decode(this, Charsets.UTF_8.name()) +public fun String.urlDecode(): String = URLDecoder.decode(this, Charsets.UTF_8.name()) -fun String.nl2br() = replace("\n", "
") +public fun String.nl2br() = replace("\n", "
") -fun ByteArray.byte2HexFormatted(): String { +public fun ByteArray.byte2HexFormatted(): String { val str = StringBuilder(size * 2) for (i in indices) { var h = Integer.toHexString(this[i].toInt()) @@ -123,14 +123,14 @@ fun ByteArray.byte2HexFormatted(): String { return str.toString() } -fun String.md5(): String { +public fun String.md5(): String { val md = MessageDigest.getInstance("MD5") return BigInteger(1, md.digest(toByteArray())) .toString(16) .padStart(32, '0') } -fun String.substringBetween(from: String, to: String, fallbackValue: String = this): String { +public fun String.substringBetween(from: String, to: String, fallbackValue: String = this): String { val fromIndex = indexOf(from) if (fromIndex == -1) { return fallbackValue @@ -143,7 +143,7 @@ fun String.substringBetween(from: String, to: String, fallbackValue: String = th } } -fun String.substringBetweenFirst(from: String, to: String): String? { +public fun String.substringBetweenFirst(from: String, to: String): String? { val fromIndex = indexOf(from) if (fromIndex == -1) { return null @@ -156,7 +156,7 @@ fun String.substringBetweenFirst(from: String, to: String): String? { } } -fun String.substringBetweenLast(from: String, to: String, fallbackValue: String = this): String { +public fun String.substringBetweenLast(from: String, to: String, fallbackValue: String = this): String { val fromIndex = lastIndexOf(from) if (fromIndex == -1) { return fallbackValue @@ -169,16 +169,16 @@ fun String.substringBetweenLast(from: String, to: String, fallbackValue: String } } -fun String.find(regex: Regex) = regex.find(this)?.value +public fun String.find(regex: Regex) = regex.find(this)?.value -fun String.removeSuffix(suffix: Char): String { +public fun String.removeSuffix(suffix: Char): String { if (lastOrNull() == suffix) { return substring(0, length - 1) } return this } -fun String.levenshteinDistance(other: String): Int { +public fun String.levenshteinDistance(other: String): Int { if (this == other) { return 0 } @@ -219,7 +219,7 @@ fun String.levenshteinDistance(other: String): Int { /** * @param threshold 0 = exact match */ -fun String.almostEquals(other: String, @FloatRange(from = 0.0) threshold: Float): Boolean { +public fun String.almostEquals(other: String, @FloatRange(from = 0.0) threshold: Float): Boolean { if (threshold <= 0f) { return equals(other, ignoreCase = true) } @@ -227,24 +227,7 @@ fun String.almostEquals(other: String, @FloatRange(from = 0.0) threshold: Float) return diff < threshold } - -inline fun Appendable.appendAll( - items: Iterable, - separator: CharSequence, - transform: (T) -> CharSequence = { it.toString() }, -) { - var isFirst = true - for (item in items) { - if (isFirst) { - isFirst = false - } else { - append(separator) - } - append(transform(item)) - } -} - -fun String.isNumeric() = all { c -> c.isDigit() } +public fun String.isNumeric() = all { c -> c.isDigit() } internal fun StringBuilder.removeTrailingZero() { if (length > 2 && get(length - 1) == '0') { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/SuspendLazy.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/SuspendLazy.kt index 6a37a1c3..ea588f3b 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/SuspendLazy.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/SuspendLazy.kt @@ -3,7 +3,7 @@ package org.koitharu.kotatsu.parsers.util import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock -class SuspendLazy( +public class SuspendLazy( private val initializer: suspend () -> T, ) { @@ -11,7 +11,7 @@ class SuspendLazy( private var cachedValue: Any? = Uninitialized @Suppress("UNCHECKED_CAST") - suspend fun get(): T { + public suspend fun get(): T { // fast way cachedValue.let { if (it !== Uninitialized) { @@ -30,10 +30,10 @@ class SuspendLazy( } } - suspend fun tryGet() = runCatchingCancellable { get() } + public suspend fun tryGet(): Result = runCatchingCancellable { get() } @Suppress("UNCHECKED_CAST") - fun peek(): T? { + public fun peek(): T? { return cachedValue?.takeUnless { it === Uninitialized } as T? } diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/json/EscapeUtils.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/json/EscapeUtils.kt index 0cb24050..0c5ac547 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/json/EscapeUtils.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/json/EscapeUtils.kt @@ -1,6 +1,6 @@ package org.koitharu.kotatsu.parsers.util.json -fun String.unescapeJson(): String { +public fun String.unescapeJson(): String { val builder = StringBuilder() var i = 0 while (i < length) { diff --git a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/json/JsonExt.kt b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/json/JsonExt.kt index ecf29d9b..396c158a 100644 --- a/src/main/kotlin/org/koitharu/kotatsu/parsers/util/json/JsonExt.kt +++ b/src/main/kotlin/org/koitharu/kotatsu/parsers/util/json/JsonExt.kt @@ -130,7 +130,7 @@ fun JSONArray.associateByKey(key: String): Map { return destination } -fun JSONArray?.isNullOrEmpty(): Boolean { +public fun JSONArray?.isNullOrEmpty(): Boolean { contract { returns(false) implies (this@isNullOrEmpty != null) }