Skip to content

Commit

Permalink
Add General support
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcinMoskala committed Mar 15, 2023
1 parent fa913d3 commit b52dd2b
Show file tree
Hide file tree
Showing 9 changed files with 390 additions and 62 deletions.
17 changes: 16 additions & 1 deletion src/commonMain/kotlin/Utils.kt
@@ -1,3 +1,18 @@
package deckmarkdown

inline fun hashCodeOf(vararg values: Any?) = values
.fold(0) { acc, v -> (acc + v.hashCode()) * 31 }
.fold(0) { acc, v -> (acc + v.hashCode()) * 31 }

fun String.recognizeKeyValueLines(): Map<String, String>? = this
.split("\n")
.fold(emptyList<Pair<String, String>>()) { acc, line ->
val matches = KEY_REGEX.matchEntire(line)
if (matches == null) {
val (key, value) = acc.lastOrNull() ?: return@recognizeKeyValueLines null
return@fold acc.dropLast(1) + (key to value + "\n" + line)
}
val (_, key, value) = matches.groupValues
acc + (key to value)
}.toMap()

private val KEY_REGEX = Regex("^([\\w-]+): ([\\W\\w]*)$")
2 changes: 1 addition & 1 deletion src/commonMain/kotlin/api/AnkiApi.kt
@@ -1,6 +1,6 @@
package deckmarkdown.api

import hashCodeOf
import deckmarkdown.hashCodeOf
import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.plugins.contentnegotiation.*
Expand Down
1 change: 1 addition & 0 deletions src/commonMain/kotlin/note/DeckParser.kt
Expand Up @@ -10,6 +10,7 @@ val DefaultParser = DeckParser(
ReminderParser,
ListDeletionParser,
ClozeParser,
GeneralParser,
TextParser
)
)
Expand Down
34 changes: 25 additions & 9 deletions src/commonMain/kotlin/note/GeneralParser.kt
@@ -1,23 +1,35 @@
package deckmarkdown.note

import deckmarkdown.api.ApiNote
import deckmarkdown.Note
import deckmarkdown.Note.General
import note.MarkdownParser
import deckmarkdown.api.ApiNote
import deckmarkdown.recognizeKeyValueLines

// TODO
/*
* Consumes all API notes (see recognizeApiNote), so should be after all other
* Consumes all API notes (see recognizeApiNote below), so should be after all other
* notes but before TextParser.
*/
object GeneralParser : FullNoteProcessor<General> {
val MODEL_NAME_FIELD = "modelName"

override fun handlesNote(note: Note): Boolean = note is General

override fun recognize(text: String): Boolean = TODO()
override fun recognize(text: String): Boolean =
text.recognizeKeyValueLines()?.let { it[MODEL_NAME_FIELD] } != null

override fun parse(id: Long?, noteText: String): General = TODO()
override fun parse(id: Long?, noteText: String): General =
noteText.recognizeKeyValueLines()!!
.let { fields ->
General(
id = id,
modelName = fields[MODEL_NAME_FIELD]!!.trim(),
fields = fields - MODEL_NAME_FIELD
)
}

override fun render(note: General): String = TODO()
override fun render(note: General): String =
"modelName: ${note.modelName}\n" +
note.fields.toList().joinToString(separator = "\n") { (field, value) -> "$field: $value" }

/*
* Every unrecognized API note should be treated as general.
Expand All @@ -28,10 +40,14 @@ object GeneralParser : FullNoteProcessor<General> {
noteId = note.id ?: ApiNote.NO_ID,
deckName = deckName,
modelName = note.modelName,
fields = note.fields
fields = note.fields,
)

override fun ankiNoteToCard(apiNote: ApiNote): General = TODO()
override fun ankiNoteToCard(apiNote: ApiNote): General = General(
id = apiNote.noteId,
modelName = apiNote.modelName,
fields = apiNote.fields,
)

override fun toHtml(note: General): String = TODO()

Expand Down
66 changes: 66 additions & 0 deletions src/commonTest/kotlin/E2ETest.kt
@@ -0,0 +1,66 @@
package ankimarkdown

import ankimarkdown.fakes.FakeAnkiApi
import deckmarkdown.AnkiConnector
import deckmarkdown.Note
import deckmarkdown.api.ApiNote
import deckmarkdown.note.BasicParser
import deckmarkdown.note.DefaultParser
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
import kotlin.test.assertEquals

@OptIn(ExperimentalCoroutinesApi::class)
abstract class E2ETest {
protected val fakeApi = FakeAnkiApi()
protected val connector = AnkiConnector(api = fakeApi)
protected val deckName = "SOME_DECK_NAME"

protected fun basicApi(
front: String,
back: String,
extra: String? = null,
noteId: Long = 0
) = ApiNote(
noteId, deckName, "Basic", mapOf(
BasicParser.FRONT_FIELD to front,
BasicParser.BACK_FIELD to back,
BasicParser.EXTRA_FIELD to extra.orEmpty(),
)
)

protected fun basicAndReversedApi(
front: String,
back: String,
extra: String? = null,
noteId: Long = 0
) = ApiNote(
noteId, deckName, "Basic (and reversed card)", mapOf(
BasicParser.FRONT_FIELD to front,
BasicParser.BACK_FIELD to back,
BasicParser.EXTRA_FIELD to extra.orEmpty(),
)
)

protected fun listApi(
title: String,
items: Map<String, String>,
generalComment: String = "",
extra: String = "",
noteId: Long = 0
) = ApiNote(
noteId, deckName, "ListDeletion", mapOf(
"Title" to title,
"General Comment" to generalComment,
"Extra" to extra,
*items.toList().flatMapIndexed { index: Int, (text, comment) ->
val num = index + 1
listOf(
"$num" to text,
"$num comment" to comment
)
}.toTypedArray()
)
)
}
59 changes: 59 additions & 0 deletions src/commonTest/kotlin/PullingTest.kt
@@ -0,0 +1,59 @@
package ankimarkdown

import ankimarkdown.fakes.FakeAnkiApi
import deckmarkdown.AnkiConnector
import deckmarkdown.Note
import deckmarkdown.api.ApiNote
import deckmarkdown.note.BasicParser
import deckmarkdown.note.DefaultParser
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
import kotlin.test.assertEquals

@OptIn(ExperimentalCoroutinesApi::class)
class PullingTest: E2ETest() {

@Test
fun general() = testPull(
startingMarkdown = """
Some text 1
Some text 2
""".trimIndent(),
notesOnDeck = listOf(
ApiNote(
noteId = 123,
deckName = deckName,
modelName = "Special model name (for test)",
fields = mapOf(
"field_a" to "This is text a",
"field_b" to "This is text b",
)
)
),
expectedMarkdown = """
Some text 1
Some text 2
@123
modelName: Special model name (for test)
field_a: This is text a
field_b: This is text b
""".trimIndent(),
)

private fun testPull(
startingMarkdown: String = "",
notesOnDeck: List<ApiNote>,
expectedMarkdown: String? = null,
) = runTest {
fakeApi.hasNotes(notesOnDeck)
val res = connector.pullDeck(deckName, startingMarkdown)
if (expectedMarkdown != null) {
assertEquals(expectedMarkdown, res.markdown)
}
fakeApi.clean()
}
}

0 comments on commit b52dd2b

Please sign in to comment.