adding super naive html parsing for style options
This commit is contained in:
parent
3517cf492a
commit
e13ce95b83
|
@ -0,0 +1,19 @@
|
|||
package app.dapk.st.matrix.common
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class RichText(@SerialName("parts") val parts: Set<Part>) {
|
||||
@Serializable
|
||||
sealed interface Part {
|
||||
@Serializable
|
||||
data class Normal(@SerialName("content") val content: String) : Part
|
||||
data class Link(@SerialName("url") val url: String, @SerialName("label") val label: String) : Part
|
||||
data class Bold(@SerialName("content") val content: String) : Part
|
||||
data class Italic(@SerialName("content") val content: String) : Part
|
||||
data class BoldItalic(@SerialName("content") val content: String) : Part
|
||||
}
|
||||
}
|
||||
|
||||
fun RichText.asString() = parts.joinToString(separator = "")
|
|
@ -0,0 +1,57 @@
|
|||
package app.dapk.st.matrix.sync.internal.sync
|
||||
|
||||
import app.dapk.st.matrix.common.RichText
|
||||
|
||||
data class Tag(val name: String, val inner: String, val content: String)
|
||||
|
||||
class RichMessageParser {
|
||||
|
||||
fun parse(input: String): RichText {
|
||||
val buffer = mutableSetOf<RichText.Part>()
|
||||
var openIndex = 0
|
||||
var closeIndex = 0
|
||||
while (openIndex != -1) {
|
||||
val foundIndex = input.indexOf('<', startIndex = openIndex)
|
||||
if (foundIndex != -1) {
|
||||
closeIndex = input.indexOf('>', startIndex = openIndex)
|
||||
|
||||
if (closeIndex == -1) {
|
||||
openIndex++
|
||||
} else {
|
||||
val wholeTag = input.substring(foundIndex, closeIndex + 1)
|
||||
val tagName = wholeTag.substring(1, wholeTag.indexOfFirst { it == '>' || it == ' ' })
|
||||
val exitTag = "<$tagName/>"
|
||||
val exitIndex = input.indexOf(exitTag, startIndex = closeIndex + 1)
|
||||
val tagContent = input.substring(closeIndex + 1, exitIndex)
|
||||
|
||||
val tag = Tag(name = tagName, wholeTag, tagContent)
|
||||
|
||||
println("found $tag")
|
||||
if (openIndex != foundIndex) {
|
||||
buffer.add(RichText.Part.Normal(input.substring(openIndex, foundIndex)))
|
||||
}
|
||||
openIndex = exitIndex + exitTag.length
|
||||
|
||||
when (tagName) {
|
||||
"a" -> {
|
||||
val findHrefUrl = wholeTag.substringAfter("href=").replace("\"", "").removeSuffix(">")
|
||||
buffer.add(RichText.Part.Link(url = findHrefUrl, label = tag.content))
|
||||
}
|
||||
|
||||
"b" -> buffer.add(RichText.Part.Bold(tagContent))
|
||||
"i" -> buffer.add(RichText.Part.Italic(tagContent))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// exit
|
||||
if (openIndex < input.length) {
|
||||
buffer.add(RichText.Part.Normal(input.substring(openIndex)))
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return RichText(buffer)
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
package app.dapk.st.matrix.sync.internal.sync
|
||||
|
||||
import app.dapk.st.matrix.common.RichText
|
||||
import org.amshove.kluent.shouldBeEqualTo
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
|
||||
class RichMessageParserTest {
|
||||
|
||||
private val parser = RichMessageParser()
|
||||
|
||||
@Test
|
||||
fun `parses plain text`() = runParserTest(
|
||||
input = "Hello world!",
|
||||
expected = RichText(setOf(RichText.Part.Normal("Hello world!")))
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `parses nested b tags`() = runParserTest(
|
||||
Case(
|
||||
input = """hello <b>wor<b/>ld""",
|
||||
expected = RichText(
|
||||
setOf(
|
||||
RichText.Part.Normal("hello "),
|
||||
RichText.Part.Bold("wor"),
|
||||
RichText.Part.Normal("ld"),
|
||||
)
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `parses nested i tags`() = runParserTest(
|
||||
Case(
|
||||
input = """hello <i>wor<i/>ld""",
|
||||
expected = RichText(
|
||||
setOf(
|
||||
RichText.Part.Normal("hello "),
|
||||
RichText.Part.Italic("wor"),
|
||||
RichText.Part.Normal("ld"),
|
||||
)
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
@Ignore // TODO
|
||||
@Test
|
||||
fun `parses nested tags`() = runParserTest(
|
||||
Case(
|
||||
input = """hello <b><i>wor<i/><b/>ld""",
|
||||
expected = RichText(
|
||||
setOf(
|
||||
RichText.Part.Normal("hello "),
|
||||
RichText.Part.BoldItalic("wor"),
|
||||
RichText.Part.Normal("ld"),
|
||||
)
|
||||
)
|
||||
),
|
||||
Case(
|
||||
input = """<a href="www.google.com"><a href="www.google.com">www.google.com<a/><a/>""",
|
||||
expected = RichText(
|
||||
setOf(
|
||||
RichText.Part.Link(url = "www.google.com", label = "www.google.com"),
|
||||
RichText.Part.Link(url = "www.bing.com", label = "www.bing.com"),
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `parses 'a' tags`() = runParserTest(
|
||||
Case(
|
||||
input = """hello world <a href="www.google.com">a link!<a/> more content.""",
|
||||
expected = RichText(
|
||||
setOf(
|
||||
RichText.Part.Normal("hello world "),
|
||||
RichText.Part.Link(url = "www.google.com", label = "a link!"),
|
||||
RichText.Part.Normal(" more content."),
|
||||
)
|
||||
)
|
||||
),
|
||||
Case(
|
||||
input = """<a href="www.google.com">www.google.com<a/><a href="www.bing.com">www.bing.com<a/>""",
|
||||
expected = RichText(
|
||||
setOf(
|
||||
RichText.Part.Link(url = "www.google.com", label = "www.google.com"),
|
||||
RichText.Part.Link(url = "www.bing.com", label = "www.bing.com"),
|
||||
)
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
private fun runParserTest(vararg cases: Case) {
|
||||
val errors = mutableListOf<Throwable>()
|
||||
cases.forEach {
|
||||
runCatching { runParserTest(it.input, it.expected) }.onFailure { errors.add(it) }
|
||||
}
|
||||
if (errors.isNotEmpty()) {
|
||||
throw CompositeThrowable(errors)
|
||||
}
|
||||
}
|
||||
|
||||
private fun runParserTest(input: String, expected: RichText) {
|
||||
val result = parser.parse(input)
|
||||
|
||||
result shouldBeEqualTo expected
|
||||
}
|
||||
}
|
||||
|
||||
private data class Case(val input: String, val expected: RichText)
|
||||
|
||||
class CompositeThrowable(inner: List<Throwable>) : Throwable() {
|
||||
init {
|
||||
inner.forEach { addSuppressed(it) }
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue