Skip to content

Commit

Permalink
feat: added lambda processing instead of returning a list
Browse files Browse the repository at this point in the history
  • Loading branch information
fsniper committed Oct 18, 2024
1 parent c62a72f commit 6a1ec88
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 8 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,14 @@ val someApi = TODO("Not a real API object")
// Returns a playlist in the M3U format
val m3uContent: String = someApi.getPlaylist("Best of Willy Astor")
val entries: List<M3uEntry> = M3uParser.parse(m3uContent)


// You can also use a lambda for processing each entry instead of returning a List
val m3uFile = Paths.get("myplaylist.m3u")
M3uParser.parse(m3uFile) { entry ->
println(entry->name)
}

```

### Nested playlists
Expand Down
5 changes: 5 additions & 0 deletions buildConfig/detekt.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,8 @@ comments:
active: true
UndocumentedPublicFunction:
active: true
complexity:
TooManyFunctions:
ignoreOverridden: true
thresholdInClasses: 15
thresholdInObjects: 15
81 changes: 73 additions & 8 deletions src/main/kotlin/net/bjoernpetersen/m3u/M3uParser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,28 @@ object M3uParser {
@JvmOverloads
fun parse(m3uFile: Path, charset: Charset = Charsets.UTF_8): List<M3uEntry> {
require(Files.isRegularFile(m3uFile)) { "$m3uFile is not a file" }
return parse(Files.lines(m3uFile, charset).asSequence(), m3uFile.parent)
val entries = mutableListOf<M3uEntry>()
parse(Files.lines(m3uFile, charset).asSequence(), m3uFile.parent) { entry -> entries.add(entry) }
return entries
}

Check warning on line 61 in src/main/kotlin/net/bjoernpetersen/m3u/M3uParser.kt

View check run for this annotation

Codecov / codecov/patch

src/main/kotlin/net/bjoernpetersen/m3u/M3uParser.kt#L61

Added line #L61 was not covered by tests

/**
* Parses the specified file.
*
* Comment lines and lines which can't be parsed are dropped.
*
* @param m3uFile a path to an .m3u file
* @param charset the file's encoding, defaults to UTF-8
* @param processFun a Unit to process each m3uentry
* @throws IOException if file can't be read
* @throws IllegalArgumentException if file is not a regular file
*/
@Throws(IOException::class)

Check warning on line 74 in src/main/kotlin/net/bjoernpetersen/m3u/M3uParser.kt

View check run for this annotation

Codecov / codecov/patch

src/main/kotlin/net/bjoernpetersen/m3u/M3uParser.kt#L74

Added line #L74 was not covered by tests
@JvmStatic
@JvmOverloads
fun parse(m3uFile: Path, charset: Charset = Charsets.UTF_8, processFun: (M3uEntry) -> Unit) {

Check warning on line 77 in src/main/kotlin/net/bjoernpetersen/m3u/M3uParser.kt

View check run for this annotation

Codecov / codecov/patch

src/main/kotlin/net/bjoernpetersen/m3u/M3uParser.kt#L77

Added line #L77 was not covered by tests
require(Files.isRegularFile(m3uFile)) { "$m3uFile is not a file" }
parse(Files.lines(m3uFile, charset).asSequence(), m3uFile.parent, processFun)

Check warning on line 79 in src/main/kotlin/net/bjoernpetersen/m3u/M3uParser.kt

View check run for this annotation

Codecov / codecov/patch

src/main/kotlin/net/bjoernpetersen/m3u/M3uParser.kt#L79

Added line #L79 was not covered by tests
}

/**
Expand All @@ -70,7 +91,30 @@ object M3uParser {
@JvmStatic
@JvmOverloads
fun parse(m3uContentReader: InputStreamReader, baseDir: Path? = null): List<M3uEntry> {
return m3uContentReader.buffered().useLines { parse(it, baseDir) }
val entries = mutableListOf<M3uEntry>()
m3uContentReader.buffered().useLines {
parse(it, baseDir) {
entry ->
entries.add(entry)
}
}
return entries
}

Check warning on line 102 in src/main/kotlin/net/bjoernpetersen/m3u/M3uParser.kt

View check run for this annotation

Codecov / codecov/patch

src/main/kotlin/net/bjoernpetersen/m3u/M3uParser.kt#L102

Added line #L102 was not covered by tests

/**
* Parses the [InputStream] from the specified reader.
*
* Comment lines and lines which can't be parsed are dropped.
*
* @param m3uContentReader a reader reading the content of an `.m3u` file
* @param baseDir a base dir for resolving relative paths
* @param processFun a Unit to process each m3uentry
*
*/
@JvmStatic
@JvmOverloads
fun parse(m3uContentReader: InputStreamReader, baseDir: Path? = null, processFun: (M3uEntry) -> Unit) {
return m3uContentReader.buffered().useLines { parse(it, baseDir, processFun) }
}

/**
Expand All @@ -85,7 +129,28 @@ object M3uParser {
@JvmStatic
@JvmOverloads
fun parse(m3uContent: String, baseDir: Path? = null): List<M3uEntry> {
return parse(m3uContent.lineSequence(), baseDir)
val entries = mutableListOf<M3uEntry>()
parse(m3uContent, baseDir) {
entry ->
entries.add(entry)
}
return entries
}

Check warning on line 138 in src/main/kotlin/net/bjoernpetersen/m3u/M3uParser.kt

View check run for this annotation

Codecov / codecov/patch

src/main/kotlin/net/bjoernpetersen/m3u/M3uParser.kt#L138

Added line #L138 was not covered by tests

/**
* Parses the specified content of a `.m3u` file.
*
* Comment lines and lines which can't be parsed are dropped.
*
* @param m3uContent the content of a `.m3u` file
* @param baseDir a base dir for resolving relative paths
* @param processFun a Unit to process each m3uentry
*
*/
@JvmStatic

Check warning on line 150 in src/main/kotlin/net/bjoernpetersen/m3u/M3uParser.kt

View check run for this annotation

Codecov / codecov/patch

src/main/kotlin/net/bjoernpetersen/m3u/M3uParser.kt#L150

Added line #L150 was not covered by tests
@JvmOverloads
fun parse(m3uContent: String, baseDir: Path? = null, processFun: (M3uEntry) -> Unit) {

Check warning on line 152 in src/main/kotlin/net/bjoernpetersen/m3u/M3uParser.kt

View check run for this annotation

Codecov / codecov/patch

src/main/kotlin/net/bjoernpetersen/m3u/M3uParser.kt#L152

Added line #L152 was not covered by tests
parse(m3uContent.lineSequence(), baseDir, processFun)
}

/**
Expand All @@ -106,14 +171,14 @@ object M3uParser {
}

@Suppress("NestedBlockDepth", "ReturnCount")
private fun parse(lines: Sequence<String>, baseDir: Path?): List<M3uEntry> {
private fun parse(lines: Sequence<String>, baseDir: Path?, processFun: (M3uEntry) -> Unit) {
val filtered = lines
.filterNot { it.isBlank() }
.map { it.trimEnd() }
.dropWhile { it == EXTENDED_HEADER }
.iterator()

if (!filtered.hasNext()) return emptyList()
if (!filtered.hasNext()) return

val entries = LinkedList<M3uEntry>()

Expand All @@ -134,7 +199,7 @@ object M3uParser {
if (filtered.hasNext()) {
currentLine = filtered.next()
} else {
return entries
return
}
}

Expand All @@ -149,13 +214,13 @@ object M3uParser {
match = null

if (entry != null) {
entries.add(entry)
processFun(entry)
} else {
logger.warn { "Ignored line $currentLine" }
}
}

return entries
return
}

private fun parseSimple(location: String, baseDir: Path?): M3uEntry? {
Expand Down
30 changes: 30 additions & 0 deletions src/test/kotlin/net/bjoernpetersen/m3u/M3uParserExampleTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,36 @@ class M3uParserExampleTest {
assertEquals("https://i.imgur.com/CnIVW9o.jpg", withLogo.metadata.logo)
}

@TestFactory
fun testWikiLambda(): List<DynamicTest> {
return listOf(
"wiki_mixed.m3u",
"wiki_mixed_empty_lines.m3u",
).map { name ->
dynamicTest(name) {
val entries = mutableListOf<M3uEntry>()
M3uParser.parse(javaClass.getResourceAsStream(name).reader()) { entry ->
entries.add(entry)
}
assertEquals(7, entries.size)

val simple = listOf(entries[1], entries[4])
simple.forEach {
assertThat(it)
.returns(null, M3uEntry::duration)
.returns(null, M3uEntry::title)
}

val extended = entries.filterNot { it in simple }
extended.forEach { entry ->
assertThat(entry)
.matches { it.duration != null }
.matches { it.title != null }
}
}
}
}

@Test
fun testRecursiveResolution() {
val files = listOf("rec_1.m3u", "rec_2.m3u", "rec_3.m3u")
Expand Down

0 comments on commit 6a1ec88

Please sign in to comment.