Skip to content

Commit

Permalink
add rss serialization
Browse files Browse the repository at this point in the history
  • Loading branch information
Tlaster committed Dec 26, 2024
1 parent c6ea5f1 commit db22ea6
Show file tree
Hide file tree
Showing 4 changed files with 384 additions and 0 deletions.
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ precompose-molecule = { module = "moe.tlaster:precompose-molecule", version = "1

compose-cupertino = { module = "io.github.alexzhirkevich:cupertino-core", version = "0.1.0-alpha04" }

xmlUtil = { module = "io.github.pdvrieze.xmlutil:serialization", version = "0.90.3" }

[bundles]
compose = ["ui", "ui-util", "ui-graphics", "ui-tooling", "ui-tooling-preview", "material3", "material3WindowSizeClass", "material3-adaptive-navigation-suite", "material3-adaptive", "material3-adaptive-navigation", "material3-adaptive-layout"]
navigation = ["navigation-compose"]
Expand Down
1 change: 1 addition & 0 deletions shared/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ kotlin {
implementation(libs.sqlite.bundled)
implementation(libs.datastore)
implementation(libs.kotlinx.serialization.protobuf)
implementation(libs.xmlUtil)
}
}
val commonTest by getting {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package dev.dimension.flare.data.network.rss

import dev.dimension.flare.data.network.ktorClient
import dev.dimension.flare.data.network.rss.model.Feed
import io.ktor.client.request.get
import io.ktor.client.statement.bodyAsText
import nl.adaptivity.xmlutil.serialization.XML

internal object Rss {
private val xml by lazy {
XML {
defaultPolicy {
autoPolymorphic = true
ignoreNamespaces()
ignoreUnknownChildren()
}
defaultToGenericParser = true
}
}

suspend fun fetch(url: String): Feed = xml.decodeFromString(Feed.serializer(), ktorClient().get(url).bodyAsText())
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,359 @@
package dev.dimension.flare.data.network.rss.model

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import nl.adaptivity.xmlutil.serialization.XmlElement
import nl.adaptivity.xmlutil.serialization.XmlSerialName
import nl.adaptivity.xmlutil.serialization.XmlValue

@Serializable
internal sealed interface Feed {
@Serializable
@SerialName("feed")
data class Atom(
@XmlSerialName("id")
@XmlElement(true)
val id: String,
@XmlSerialName("title")
@XmlElement(true)
val title: Text,
@XmlSerialName("updated")
@XmlElement(true)
val updated: String, // ISO-8601 datetime string
@XmlSerialName("author")
@XmlElement(true)
val authors: List<Person> = emptyList(),
@XmlSerialName("contributor")
@XmlElement(true)
val contributors: List<Person> = emptyList(),
@XmlSerialName("category")
@XmlElement(true)
val categories: List<Category> = emptyList(),
@XmlSerialName("generator")
@XmlElement(true)
val generator: Generator? = null,
@XmlSerialName("icon")
@XmlElement(true)
val icon: String? = null, // URI
@XmlSerialName("logo")
@XmlElement(true)
val logo: String? = null, // URI
@XmlSerialName("rights")
@XmlElement(true)
val rights: Text? = null,
@XmlSerialName("subtitle")
@XmlElement(true)
val subtitle: Text? = null,
@XmlSerialName("link")
@XmlElement(true)
val links: List<Link> = emptyList(),
@XmlSerialName("entry")
@XmlElement(true)
val entries: List<Entry> = emptyList(),
) : Feed {
@Serializable
data class Entry(
@XmlSerialName("id")
@XmlElement(true)
val id: String,
@XmlSerialName("title")
@XmlElement(true)
val title: Text,
@XmlSerialName("updated")
@XmlElement(true)
val updated: String, // ISO-8601 datetime string
@XmlSerialName("author")
@XmlElement(true)
val authors: List<Person> = emptyList(),
@XmlSerialName("content")
@XmlElement(true)
val content: Content? = null,
@XmlSerialName("contributor")
@XmlElement(true)
val contributors: List<Person> = emptyList(),
@XmlSerialName("published")
@XmlElement(true)
val published: String? = null, // ISO-8601 datetime string
@XmlSerialName("rights")
@XmlElement(true)
val rights: Text? = null,
@XmlSerialName("source")
@XmlElement(true)
val source: Source? = null,
@XmlSerialName("summary")
@XmlElement(true)
val summary: Text? = null,
@XmlSerialName("link")
@XmlElement(true)
val links: List<Link> = emptyList(),
@XmlSerialName("category")
@XmlElement(true)
val categories: List<Category> = emptyList(),
)

@Serializable
data class Person(
@XmlSerialName("name")
@XmlElement(true)
val name: String,
@XmlSerialName("uri")
@XmlElement(true)
val uri: String? = null,
@XmlSerialName("email")
@XmlElement(true)
val email: String? = null,
)

@Serializable
data class Text(
@XmlValue
val value: String,
@XmlElement(false)
val type: TextType = TextType.TEXT,
)

@Serializable
enum class TextType {
@SerialName("text")
TEXT,

@SerialName("html")
HTML,

@SerialName("xhtml")
XHTML,
}

@Serializable
data class Content(
@XmlValue
val value: String? = null,
@XmlElement(false)
val src: String? = null, // URI
@XmlElement(false)
val type: String? = null,
)

@Serializable
data class Category(
@XmlElement(false)
val term: String,
@XmlElement(false)
val scheme: String? = null, // URI
@XmlElement(false)
val label: String? = null,
)

@Serializable
data class Generator(
@XmlValue
val value: String,
@XmlElement(false)
val uri: String? = null,
@XmlElement(false)
val version: String? = null,
)

@Serializable
data class Link(
@XmlElement(false)
val href: String, // URI
@XmlElement(false)
val rel: String? = null,
@XmlElement(false)
val type: String? = null,
@XmlElement(false)
val hreflang: String? = null,
@XmlElement(false)
val title: String? = null,
@XmlElement(false)
val length: Int? = null,
)

@Serializable
data class Source(
@XmlSerialName("id")
@XmlElement(true)
val id: String? = null,
@XmlSerialName("title")
@XmlElement(true)
val title: Text? = null,
@XmlSerialName("updated")
@XmlElement(true)
val updated: String? = null, // ISO-8601 datetime string
@XmlSerialName("author")
@XmlElement(true)
val authors: List<Person> = emptyList(),
@XmlSerialName("contributor")
@XmlElement(true)
val contributors: List<Person> = emptyList(),
@XmlSerialName("category")
@XmlElement(true)
val categories: List<Category> = emptyList(),
@XmlSerialName("generator")
@XmlElement(true)
val generator: Generator? = null,
@XmlSerialName("icon")
@XmlElement(true)
val icon: String? = null, // URI
@XmlSerialName("logo")
@XmlElement(true)
val logo: String? = null, // URI
@XmlSerialName("rights")
@XmlElement(true)
val rights: Text? = null,
@XmlSerialName("subtitle")
@XmlElement(true)
val subtitle: Text? = null,
@XmlSerialName("link")
@XmlElement(true)
val links: List<Link> = emptyList(),
)
}

@Serializable
@SerialName("rss")
data class Rss20(
val version: String,
@XmlElement(true)
val channel: Channel,
) : Feed {
@Serializable
@SerialName("channel")
data class Channel(
@XmlElement(true)
val title: String,
@XmlElement(true)
val link: String,
@XmlElement(true)
val description: String,
@XmlElement(true)
val language: String? = null,
@XmlElement(true)
val copyright: String? = null,
@XmlElement(true)
val managingEditor: String? = null,
@XmlElement(true)
val webMaster: String? = null,
@XmlElement(true)
val pubDate: String? = null,
@XmlElement(true)
val lastBuildDate: String? = null,
@XmlElement(true)
val category: String? = null,
@XmlElement(true)
val generator: String? = null,
@XmlElement(true)
val docs: String? = null,
@XmlElement(true)
val cloud: Cloud? = null,
@XmlElement(true)
val ttl: Int? = null,
@XmlElement(true)
val image: Image? = null,
@XmlElement(true)
val rating: String? = null,
@XmlElement(true)
val textInput: TextInput? = null,
@XmlElement(true)
val skipHours: List<Int> = emptyList(),
@XmlElement(true)
val skipDays: List<String> = emptyList(),
@XmlSerialName(value = "item")
val items: List<Item> = emptyList(),
)

@Serializable
data class Cloud(
@XmlElement(true)
val domain: String,
@XmlElement(true)
val port: Int,
@XmlElement(true)
val path: String,
@XmlElement(true)
val registerProcedure: String,
@XmlElement(true)
val protocol: String,
)

@Serializable
data class Image(
@XmlElement(true)
val url: String,
@XmlElement(true)
val title: String? = null,
@XmlElement(true)
val link: String? = null,
@XmlElement(true)
val width: Int? = null,
@XmlElement(true)
val height: Int? = null,
@XmlElement(true)
val description: String? = null,
)

@Serializable
data class TextInput(
@XmlElement(true)
val title: String,
@XmlElement(true)
val description: String? = null,
@XmlElement(true)
val name: String,
@XmlElement(true)
val link: String? = null,
)

@Serializable
data class Item(
@XmlElement(true)
val title: String,
@XmlElement(true)
val link: String,
@XmlElement(true)
val description: String? = null,
@XmlElement(true)
val author: String? = null,
@XmlElement(true)
val category: String? = null,
@XmlElement(true)
val comments: String? = null,
@XmlElement(true)
val enclosure: Enclosure? = null,
@XmlElement(true)
val guid: Guid? = null,
@XmlElement(true)
val pubDate: String? = null,
@XmlElement(true)
val source: Source? = null,
)

@Serializable
data class Enclosure(
@XmlElement(true)
val url: String,
@XmlElement(true)
val length: Int,
@XmlElement(true)
val type: String,
)

@Serializable
@SerialName("guid")
data class Guid(
@XmlElement(false)
val isPermaLink: Boolean = false,
@XmlValue
val value: String,
)

@Serializable
data class Source(
@XmlElement(true)
val url: String,
@XmlElement(true)
val title: String? = null,
)
}
}

0 comments on commit db22ea6

Please sign in to comment.