diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 3220cdc..9a7b1af 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -14,11 +14,11 @@ jobs: - name: Checkout code uses: actions/checkout@v4 - - name: Set up JDK 8 + - name: Set up JDK 11 uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: '8' + java-version: '11' - name: Setup Gradle uses: gradle/actions/setup-gradle@v4 @@ -29,7 +29,7 @@ jobs: path: | ~/.gradle/caches ~/.gradle/wrapper - key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties', '**/libs.versions.toml') }} restore-keys: | ${{ runner.os }}-gradle- @@ -38,8 +38,7 @@ jobs: - name: Run checks and generate report run: | - ./gradlew clean check - ./gradlew jacocoTestReport + ./gradlew clean check koverXmlReport - name: Upload coverage report to Codecov uses: codecov/codecov-action@v4 diff --git a/.gitignore b/.gitignore index cefcb35..bab889f 100644 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,7 @@ test.csv .idea/* # Ignore yarn.lcok -kotlin-js-store/yarn.lock \ No newline at end of file +kotlin-js-store/yarn.lock + +# Kotlin 2.0 +.kotlin/ \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index e2e0a4f..e54e852 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,68 +1,77 @@ +import com.vanniktech.maven.publish.JavadocJar +import com.vanniktech.maven.publish.KotlinMultiplatform +import com.vanniktech.maven.publish.SonatypeHost +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + plugins { - java - kotlin("multiplatform") version "1.7.21" - id("org.jetbrains.dokka").version("1.7.20") - `maven-publish` - signing - jacoco + alias(libs.plugins.kotlinMultiplatform) + alias(libs.plugins.dokka) + alias(libs.plugins.kover) + alias(libs.plugins.mavenPublish) + alias(libs.plugins.kotest) } group = "com.jsoizo" -version = "1.10.0" +version = "2.0.0-dev1" +val projectName = "kotlin-csv" buildscript { repositories { mavenCentral() } - dependencies { - classpath("org.jetbrains.dokka:dokka-gradle-plugin:1.7.20") - } } repositories { mavenCentral() } -val dokkaJar = task("dokkaJar") { - group = JavaBasePlugin.DOCUMENTATION_GROUP - archiveClassifier.set("javadoc") -} - kotlin { jvm { - compilations.forEach { - it.kotlinOptions.jvmTarget = "1.8" - } - //https://docs.gradle.org/current/userguide/publishing_maven.html - mavenPublication { - artifact(dokkaJar) + compilerOptions { + jvmTarget.set(JvmTarget.JVM_1_8) } } - js(BOTH) { + js { browser { } nodejs { } } + @OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class) + wasmJs { + browser() + nodejs() + } sourceSets { - commonMain {} + commonMain { + dependencies { + implementation(libs.kotlinx.coroutines.core) + api(libs.kotlinx.io) + } + } commonTest { dependencies { implementation(kotlin("test-common")) implementation(kotlin("test-annotations-common")) + implementation(libs.kotest.framework.engine) + implementation(libs.kotlinx.datetime) + } + } + + wasmJsTest { + dependencies { + implementation(kotlin("test-wasm-js")) } } jvm().compilations["main"].defaultSourceSet { dependencies { - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2") + implementation(libs.kotlinx.coroutines.core) } } jvm().compilations["test"].defaultSourceSet { dependencies { - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2") - implementation("io.kotest:kotest-runner-junit5:4.6.3") - implementation("io.kotest:kotest-assertions-core:4.6.3") + implementation(libs.bundles.kotest) } } js().compilations["main"].defaultSourceSet { @@ -81,79 +90,51 @@ tasks.withType() { useJUnitPlatform() } +mavenPublishing { + publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL) -publishing { - publications.all { - (this as MavenPublication).pom { - name.set("kotlin-csv") - description.set("Kotlin CSV Reader/Writer") - url.set("https://github.com/jsoizo/kotlin-csv") - - organization { - name.set("com.jsoizo") - url.set("https://github.com/jsoizo") - } - licenses { - license { - name.set("Apache License 2.0") - url.set("https://github.com/jsoizo/kotlin-csv/blob/master/LICENSE") - } - } - scm { - url.set("https://github.com/jsoizo/kotlin-csv") - connection.set("scm:git:git://github.com/jsoizo/kotlin-csv.git") - developerConnection.set("https://github.com/jsoizo/kotlin-csv") - } - developers { - developer { - name.set("jsoizo") - } - } - } - } - repositories { - maven { - credentials { - val nexusUsername: String? by project - val nexusPassword: String? by project - username = nexusUsername - password = nexusPassword - } - - val releasesRepoUrl = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2/") - val snapshotsRepoUrl = uri("https://oss.sonatype.org/content/repositories/snapshots/") - url = if (version.toString().endsWith("SNAPSHOT")) snapshotsRepoUrl else releasesRepoUrl - } + if (project.hasProperty("signing.keyId")) { + signAllPublications() } -} -signing { - sign(publishing.publications) -} + coordinates(group.toString(), projectName, version.toString()) -///////////////////////////////////////// -// Jacoco setting // -///////////////////////////////////////// -jacoco { - toolVersion = "0.8.8" -} -tasks.jacocoTestReport { - val coverageSourceDirs = arrayOf( - "commonMain/src", - "jvmMain/src" + configure( + KotlinMultiplatform( + javadocJar = JavadocJar.Dokka("dokkaHtml"), + sourcesJar = true, + androidVariantsToPublish = listOf("debug", "release"), + ) ) - val classFiles = File("${buildDir}/classes/kotlin/jvm/") - .walkBottomUp() - .toSet() - classDirectories.setFrom(classFiles) - sourceDirectories.setFrom(files(coverageSourceDirs)) - additionalSourceDirs.setFrom(files(coverageSourceDirs)) - executionData - .setFrom(files("${buildDir}/jacoco/jvmTest.exec")) + val repo = "github.com/jsoizo/${projectName}" + val repoHttpUrl = "https://${repo}" + val repoGitUrl = "git://${repo}.git" - reports { - xml.required.set(true) - html.required.set(false) + pom { + name = projectName + description = "Pure Kotlin CSV reader and writer" + inceptionYear = "2019" + url = repoHttpUrl + organization { + name.set("com.jsoizo") + url.set("https://github.com/jsoizo") + } + licenses { + license { + name.set("Apache License 2.0") + url.set("${repoHttpUrl}/blob/master/LICENSE") + } + } + scm { + url.set(repoHttpUrl) + connection.set("scm:git:${repoGitUrl}") + developerConnection.set(repoHttpUrl) + } + developers { + developer { + name.set("jsoizo") + } + } } } diff --git a/gradle.properties b/gradle.properties index 29e08e8..ff4f21a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1,2 @@ -kotlin.code.style=official \ No newline at end of file +kotlin.code.style=official +org.gradle.jvmargs=-Xmx4096m diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 0000000..5acdf5f --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,27 @@ +[versions] +kotlin = "2.1.0" +kotlinx-io = "0.6.0" +coroutines = "1.10.1" +maven-publish = "0.30.0" +kover = "0.8.2" +dokka = "2.0.0" +kotest = "5.9.1" + +[libraries] +#kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } +kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" } +kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version = "0.6.1" } +kotlinx-io = { module = "org.jetbrains.kotlinx:kotlinx-io-core", version.ref = "kotlinx-io" } +kotest-runner-junit5 = { module = "io.kotest:kotest-runner-junit5", version.ref = "kotest" } +kotest-assertions-core = { module = "io.kotest:kotest-assertions-core", version.ref = "kotest" } +kotest-framework-engine = { module = "io.kotest:kotest-framework-engine", version.ref = "kotest" } + +[bundles] +kotest = ["kotest-runner-junit5", "kotest-assertions-core"] + +[plugins] +kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } +kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" } +dokka = { id = "org.jetbrains.dokka", version.ref = "dokka" } +mavenPublish = { id = "com.vanniktech.maven.publish", version.ref = "maven-publish" } +kotest = { id = "io.kotest.multiplatform", version.ref = "kotest" } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 249e583..c1962a7 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 59bc51a..2617362 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip +networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index a69d9cb..aeb74cb 100755 --- a/gradlew +++ b/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,13 +80,10 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -143,12 +140,16 @@ fi if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -193,6 +194,10 @@ if "$cygwin" || "$msys" ; then done fi + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + # Collect all arguments for the java command; # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of # shell script including quotes and variable substitutions, so put them in diff --git a/gradlew.bat b/gradlew.bat index 53a6b23..6689b85 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -26,6 +26,7 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% diff --git a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvReader.kt b/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvReader.kt deleted file mode 100644 index 5e31eb5..0000000 --- a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvReader.kt +++ /dev/null @@ -1,22 +0,0 @@ -package com.github.doyaaaaaken.kotlincsv.client - -import com.github.doyaaaaaken.kotlincsv.dsl.context.CsvReaderContext - -/** - * CSV Reader class - * - * @author doyaaaaaken - */ -expect class CsvReader( - ctx: CsvReaderContext = CsvReaderContext() -) { - /** - * read csv data as String, and convert into List> - */ - fun readAll(data: String): List> - - /** - * read csv data with header, and convert into List> - */ - fun readAllWithHeader(data: String): List> -} diff --git a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvWriter.kt b/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvWriter.kt deleted file mode 100644 index 69d8148..0000000 --- a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvWriter.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.github.doyaaaaaken.kotlincsv.client - -import com.github.doyaaaaaken.kotlincsv.dsl.context.CsvWriterContext - -/** - * CSV Writer class - * - * @author doyaaaaaken - */ -expect class CsvWriter(ctx: CsvWriterContext = CsvWriterContext()) { - - fun open(targetFileName: String, append: Boolean = false, write: ICsvFileWriter.() -> Unit) - - fun writeAll(rows: List>, targetFileName: String, append: Boolean = false) - - suspend fun writeAllAsync(rows: List>, targetFileName: String, append: Boolean = false) - - suspend fun openAsync(targetFileName: String, append: Boolean = false, write: suspend ICsvFileWriter.() -> Unit) -} diff --git a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/Reader.kt b/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/Reader.kt deleted file mode 100644 index c689056..0000000 --- a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/Reader.kt +++ /dev/null @@ -1,62 +0,0 @@ -package com.github.doyaaaaaken.kotlincsv.client - -internal interface Reader { - /** - * Reads a single character. - * Returns: The character read, as an integer in the range 0 to 65535 (0x00-0xffff), or -1 if the end of the stream has been reached - */ - fun read(): Int - - /** - * Marks the present position in the stream. Subsequent calls to reset() - * will attempt to reposition the stream to this point. - * - * @param readAheadLimit Limit on the number of characters that may be - * read while still preserving the mark. An attempt - * to reset the stream after reading characters - * up to this limit or beyond may fail. - * A limit value larger than the size of the input - * buffer will cause a new buffer to be allocated - * whose size is no smaller than limit. - * Therefore large values should be used with care. - * - * @exception IllegalArgumentException If {@code readAheadLimit < 0} - */ - fun mark(readAheadLimit: Int): Unit - - /** - * Resets the stream to the most recent mark. - */ - fun reset(): Unit - - - fun close() -} - -/** - * Multiplatform implementation of the Reader interface that uses a String as the backing - * data source. - */ -internal class StringReaderImpl(private val data: String) : Reader { - private var nextChar = 0 - private var mark = -1 - - override fun read(): Int { - return if (nextChar == data.length) { - -1 - } else { - data[nextChar++].code - } - } - - override fun mark(readAheadLimit: Int) { - mark = nextChar - } - - override fun reset() { - nextChar = mark - } - - override fun close() { - } -} \ No newline at end of file diff --git a/src/jvmMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/Annotation.kt b/src/commonMain/kotlin/com/jsoizo/kotlincsv/client/Annotation.kt similarity index 83% rename from src/jvmMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/Annotation.kt rename to src/commonMain/kotlin/com/jsoizo/kotlincsv/client/Annotation.kt index 5a62e94..21e1801 100644 --- a/src/jvmMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/Annotation.kt +++ b/src/commonMain/kotlin/com/jsoizo/kotlincsv/client/Annotation.kt @@ -1,4 +1,4 @@ -package com.github.doyaaaaaken.kotlincsv.client +package com.jsoizo.kotlincsv.client @RequiresOptIn( message = "This API is experimental. It may be changed in the future without notice.", diff --git a/src/commonMain/kotlin/com/jsoizo/kotlincsv/client/CsvReader.kt b/src/commonMain/kotlin/com/jsoizo/kotlincsv/client/CsvReader.kt new file mode 100644 index 0000000..a572de5 --- /dev/null +++ b/src/commonMain/kotlin/com/jsoizo/kotlincsv/client/CsvReader.kt @@ -0,0 +1,110 @@ +package com.jsoizo.kotlincsv.client + +import com.jsoizo.kotlincsv.dsl.CsvReaderScope +import com.jsoizo.kotlincsv.dsl.context.ICsvReaderContext +import kotlinx.io.Source +import kotlinx.io.files.Path + +interface CsvReader : ICsvReaderContext { + /** + * read csv data as String, and convert into List> + */ + fun readAll(data: String): List> + + /** + * read csv data from a Source, and convert into List>. + */ + fun readAll(data: Source): List> + + /** + * read csv data from a Path, and convert into List> + * + * No need to close the [path] when calling this method. + */ + fun readAll(path: Path): List> + + /** + * read csv data with header, and convert into List> + */ + fun readAllWithHeader(data: String): List> + + /** + * read csv data with a header from a Source, and convert into List>. + */ + fun readAllWithHeader(data: Source): List> + + /** + * read csv data with header, and convert into List> + * + * No need to close [path] when calling this method. + */ + fun readAllWithHeader(path: Path): List> + + /** + * open [source] and execute reading process. + * + * If you want to control read flow precisely, use this method. + * Otherwise, use utility method (e.g. CsvReader.readAll ). + * + * Usage example: + *
+     *   val data: Sequence> = csvReader().open(source) {
+     *       readAllAsSequence()
+     *           .map { fields -> fields.map { it.trim() } }
+     *           .map { fields -> fields.map { if(it.isBlank()) null else it } }
+     *   }
+     * 
+ */ + fun open(source: Source, read: CsvReaderScope.() -> T): T + + /** + * open [path] and execute reading process. + * + * If you want to control read flow precisely, use this method. + * Otherwise, use utility method (e.g. CsvReader.readAll ). + * + * Usage example: + *
+     *   val data: Sequence> = csvReader().open("test.csv") {
+     *       readAllAsSequence()
+     *           .map { fields -> fields.map { it.trim() } }
+     *           .map { fields -> fields.map { if(it.isBlank()) null else it } }
+     *   }
+     * 
+ */ + fun open(path: Path, read: CsvReaderScope.() -> T): T + + /** + * open [source] and execute reading process on a **suspending** function. + * + * If you want to control read flow precisely, use this method. + * Otherwise, use utility method (e.g. CsvReader.readAll ). + * + * Usage example: + *
+     *   val data: Sequence> = csvReader().open(source) {
+     *       readAllAsSequence()
+     *           .map { fields -> fields.map { it.trim() } }
+     *           .map { fields -> fields.map { if(it.isBlank()) null else it } }
+     *   }
+     * 
+ */ + suspend fun openAsync(source: Source, read: suspend CsvReaderScope.() -> T): T + + /** + * open [path] and execute reading process on a **suspending** function. + * + * If you want to control read flow precisely, use this method. + * Otherwise, use utility method (e.g. CsvReader.readAll ). + * + * Usage example: + *
+     *   val data: Sequence> = csvReader().openAsync("test.csv") {
+     *       readAllAsSequence()
+     *           .map { fields -> fields.map { it.trim() } }
+     *           .map { fields -> fields.map { if(it.isBlank()) null else it } }
+     *   }
+     * 
+ */ + suspend fun openAsync(path: Path, read: suspend CsvReaderScope.() -> T): T +} diff --git a/src/commonMain/kotlin/com/jsoizo/kotlincsv/client/CsvReaderImpl.kt b/src/commonMain/kotlin/com/jsoizo/kotlincsv/client/CsvReaderImpl.kt new file mode 100644 index 0000000..28b0c9f --- /dev/null +++ b/src/commonMain/kotlin/com/jsoizo/kotlincsv/client/CsvReaderImpl.kt @@ -0,0 +1,108 @@ +package com.jsoizo.kotlincsv.client + +import com.jsoizo.kotlincsv.dsl.CsvReaderScope +import com.jsoizo.kotlincsv.dsl.context.CsvReaderContext +import com.jsoizo.kotlincsv.dsl.context.ICsvReaderContext +import kotlinx.io.Buffer +import kotlinx.io.Source +import kotlinx.io.buffered +import kotlinx.io.files.Path +import kotlinx.io.files.SystemFileSystem + +/** + * CSVReader implementation. + * + * @author gsteckman + */ +internal class CsvReaderImpl( + private val ctx: CsvReaderContext = CsvReaderContext() +) : CsvReader, ICsvReaderContext by ctx { + override fun readAll(data: String): List> = data.csvReadAll(ctx) + + override fun readAll(data: Source): List> = data.csvReadAll(ctx) + + override fun readAll(path: Path): List> = path.csvReadAll(ctx) + + override fun readAllWithHeader(data: String): List> = + data.csvReadAllWithHeader(ctx) + + override fun readAllWithHeader(data: Source): List> = data.csvReadAllWithHeader(ctx) + + override fun readAllWithHeader(path: Path): List> = path.csvReadAllWithHeader(ctx) + + override fun open(source: Source, read: CsvReaderScope.() -> T): T = + SourceCsvReaderScope(ctx, source, ctx.logger).read() + + override fun open(path: Path, read: CsvReaderScope.() -> T): T = + SystemFileSystem.source(path).buffered().use { + return open(it, read) + } + + override suspend fun openAsync(source: Source, read: suspend CsvReaderScope.() -> T): T = + SourceCsvReaderScope(ctx, source, ctx.logger).read() + + override suspend fun openAsync(path: Path, read: suspend CsvReaderScope.() -> T): T = + SystemFileSystem.source(path).buffered().use { + return openAsync(it, read) + } +} + +/** + * read csv data as String, and convert into List> + */ +fun String.csvReadAll(ctx: CsvReaderContext = CsvReaderContext()): List> = + Buffer().apply{ write(encodeToByteArray()) }.use { + it.csvReadAll(ctx) + } + +/** + * read csv data with header, and convert into List> + */ +fun String.csvReadAllWithHeader(ctx: CsvReaderContext = CsvReaderContext()): List> = + Buffer().apply{ write(encodeToByteArray()) }.use { + it.csvReadAllWithHeader(ctx) + } + +/** + * read csv data from a Source, and convert into List>. + */ +fun Source.csvReadAll(ctx: CsvReaderContext = CsvReaderContext()): List> = + SourceCsvReaderScope(ctx, this, ctx.logger).readAllAsSequence().toList() + +/** + * read csv data from a Path, and convert into List>. + * + * No need to close the Path when calling this method. + */ +fun Path.csvReadAll(ctx: CsvReaderContext = CsvReaderContext()): List> = + SystemFileSystem.source(this).buffered().use { + return it.csvReadAll(ctx) + } + +/** + * read csv data with a header from a Source, and convert into List>. + */ +fun Source.csvReadAllWithHeader(ctx: CsvReaderContext = CsvReaderContext()): List> = + SourceCsvReaderScope(ctx, this, ctx.logger).readAllWithHeaderAsSequence().toList() + +/** + * read csv data with header, and convert into List> + * + * No need to close Path when calling this method. + */ +fun Path.csvReadAllWithHeader(ctx: CsvReaderContext = CsvReaderContext()): List> = + SystemFileSystem.source(this).buffered().use { + return it.csvReadAllWithHeader(ctx) + } + +/** + * read all csv rows as Sequence + */ +fun Source.csvReadAllAsSequence(fieldsNum: Int? = null, ctx: CsvReaderContext = CsvReaderContext()) + : Sequence> = SourceCsvReaderScope(ctx, this, ctx.logger).readAllAsSequence(fieldsNum) + +/** + * read all csv rows as Sequence with header information + */ +fun Source.csvReadAllWithHeaderAsSequence(ctx: CsvReaderContext = CsvReaderContext()) + : Sequence> = SourceCsvReaderScope(ctx, this, ctx.logger).readAllWithHeaderAsSequence() diff --git a/src/commonMain/kotlin/com/jsoizo/kotlincsv/client/CsvWriter.kt b/src/commonMain/kotlin/com/jsoizo/kotlincsv/client/CsvWriter.kt new file mode 100644 index 0000000..564e249 --- /dev/null +++ b/src/commonMain/kotlin/com/jsoizo/kotlincsv/client/CsvWriter.kt @@ -0,0 +1,24 @@ +package com.jsoizo.kotlincsv.client + +import com.jsoizo.kotlincsv.dsl.CsvWriterScope +import com.jsoizo.kotlincsv.dsl.context.ICsvWriterContext +import kotlinx.io.Sink +import kotlinx.io.files.Path + +interface CsvWriter : ICsvWriterContext { + fun writeAll(rows: List>, sink: Sink, append: Boolean = false) + + fun writeAll(rows: List>, path: Path, append: Boolean = false) + + suspend fun writeAllAsync(rows: List>, sink: Sink, append: Boolean = false) + + suspend fun writeAllAsync(rows: List>, path: Path, append: Boolean = false) + + fun open(sink: Sink, append: Boolean = false, write: CsvWriterScope.() -> Unit) + + fun open(path: Path, append: Boolean = false, write: CsvWriterScope.() -> Unit) + + suspend fun openAsync(sink: Sink, append: Boolean = false, write: suspend CsvWriterScope.() -> Unit) + + suspend fun openAsync(path: Path, append: Boolean = false, write: suspend CsvWriterScope.() -> Unit) +} diff --git a/src/commonMain/kotlin/com/jsoizo/kotlincsv/client/CsvWriterImpl.kt b/src/commonMain/kotlin/com/jsoizo/kotlincsv/client/CsvWriterImpl.kt new file mode 100644 index 0000000..4784a1f --- /dev/null +++ b/src/commonMain/kotlin/com/jsoizo/kotlincsv/client/CsvWriterImpl.kt @@ -0,0 +1,79 @@ +package com.jsoizo.kotlincsv.client + +import com.jsoizo.kotlincsv.dsl.CsvWriterScope +import com.jsoizo.kotlincsv.dsl.context.CsvWriterContext +import com.jsoizo.kotlincsv.dsl.context.ICsvWriterContext +import kotlinx.io.Sink +import kotlinx.io.buffered +import kotlinx.io.files.Path +import kotlinx.io.files.SystemFileSystem + +/** + * CSVWriter implementation, which decides where to write. + * + * @author doyaaaaaken + * @author gsteckman + */ +internal class CsvWriterImpl(private val ctx: CsvWriterContext = CsvWriterContext()) + : CsvWriter, ICsvWriterContext by ctx { + + override fun open(sink: Sink, append: Boolean, write: CsvWriterScope.() -> Unit) { + SinkCsvWriterScope(ctx, sink).use { + it.write() + } + } + + override fun open(path: Path, append: Boolean, write: CsvWriterScope.() -> Unit) { + val sink = SystemFileSystem.sink(path, append).buffered() + open(sink, append, write) + } + + override suspend fun openAsync(sink: Sink, + append: Boolean, + write: suspend CsvWriterScope.() -> Unit) { + SinkCsvWriterScope(ctx, sink).useSuspend{ + it.write() + } + } + + override suspend fun openAsync(path: Path, append: Boolean, write: suspend CsvWriterScope.() -> Unit) { + val sink = SystemFileSystem.sink(path, append).buffered() + openAsync(sink, append, write) + } + + /** + * *** ONLY for long-running write case *** + * + * Get and use [SinkCsvWriterScope] directly. + * MUST NOT forget to close [SinkCsvWriterScope] after using it. + * + * Use this method If you want to close file writer manually (i.e. streaming scenario). + */ + @KotlinCsvExperimental + fun openAndGetRawWriter(targetFileName: String, append: Boolean = false): SinkCsvWriterScope { + val sink = SystemFileSystem.sink(Path(targetFileName), append).buffered() + return SinkCsvWriterScope(ctx, sink) + } + + /** + * write all rows on assigned target file + */ + override fun writeAll(rows: List>, sink: Sink, append: Boolean) { + open(sink, append) { writeRows(rows) } + } + + override fun writeAll(rows: List>, path: Path, append: Boolean) { + open(path, append){ writeRows(rows) } + } + + /** + * write all rows on assigned target file + */ + override suspend fun writeAllAsync(rows: List>, sink: Sink, append: Boolean) { + openAsync(sink, append) { writeRows(rows) } + } + + override suspend fun writeAllAsync(rows: List>, path: Path, append: Boolean) { + openAsync(path, append) { writeRows(rows) } + } +} diff --git a/src/jvmMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvFileWriter.kt b/src/commonMain/kotlin/com/jsoizo/kotlincsv/client/SinkCsvWriterScope.kt similarity index 72% rename from src/jvmMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvFileWriter.kt rename to src/commonMain/kotlin/com/jsoizo/kotlincsv/client/SinkCsvWriterScope.kt index eb92589..e46f34d 100644 --- a/src/jvmMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvFileWriter.kt +++ b/src/commonMain/kotlin/com/jsoizo/kotlincsv/client/SinkCsvWriterScope.kt @@ -1,24 +1,27 @@ -package com.github.doyaaaaaken.kotlincsv.client +package com.jsoizo.kotlincsv.client -import com.github.doyaaaaaken.kotlincsv.dsl.context.CsvWriterContext -import com.github.doyaaaaaken.kotlincsv.dsl.context.WriteQuoteMode -import com.github.doyaaaaaken.kotlincsv.util.Const -import java.io.Closeable -import java.io.Flushable -import java.io.IOException -import java.io.PrintWriter +import com.jsoizo.kotlincsv.dsl.CsvWriterScope +import com.jsoizo.kotlincsv.dsl.context.CsvWriterContext +import com.jsoizo.kotlincsv.dsl.context.WriteQuoteMode +import com.jsoizo.kotlincsv.util.Const +import kotlinx.io.Sink +import kotlinx.io.writeString /** - * CSV Writer class, which controls file I/O flow. + * An implementation of [CsvWriterScope] which writes to a kotlinx.io.Sink. This implementation is not + * thread safe. If [SinkCsvWriterScope] needs to be accessed from multiple threads, + * an additional synchronization is required. * * @author doyaaaaaken + * @author gsteckman */ -class CsvFileWriter internal constructor( - private val ctx: CsvWriterContext, - private val writer: PrintWriter -) : ICsvFileWriter, Closeable, Flushable { +internal class SinkCsvWriterScope internal constructor(private val ctx: CsvWriterContext, + private val writer: Sink) : CsvWriterScope { - private val quoteNeededChars = setOf('\r', '\n', ctx.quote.char, ctx.delimiter) + private val quoteNeededChars = setOf('\r', + '\n', + ctx.quote.char, + ctx.delimiter) private var hasWroteInitialChar: Boolean = false @@ -29,10 +32,7 @@ class CsvFileWriter internal constructor( willWritePreTerminator() writeNext(row) willWriteEndTerminator() - - if (writer.checkError()) { - throw IOException("Failed to write") - } + writer.flush() } /** @@ -54,9 +54,7 @@ class CsvFileWriter internal constructor( } } willWriteEndTerminator() - if (writer.checkError()) { - throw IOException("Failed to write") - } + writer.flush() } /** @@ -73,9 +71,7 @@ class CsvFileWriter internal constructor( } willWriteEndTerminator() - if (writer.checkError()) { - throw IOException("Failed to write") - } + writer.flush() } override fun flush() { @@ -88,7 +84,7 @@ class CsvFileWriter internal constructor( private fun writeNext(row: List) { if (!hasWroteInitialChar && ctx.prependBOM) { - writer.print(Const.BOM) + writer.writeString(Const.BOM.toString()) } val rowStr = row.joinToString(ctx.delimiter.toString()) { field -> @@ -98,7 +94,7 @@ class CsvFileWriter internal constructor( attachQuote(field.toString()) } } - writer.print(rowStr) + writer.writeString(rowStr) hasWroteInitialChar = true } @@ -115,7 +111,7 @@ class CsvFileWriter internal constructor( * write terminator for next line */ private fun writeTerminator() { - writer.print(ctx.lineTerminator) + writer.writeString(ctx.lineTerminator) } private fun willWriteEndTerminator() { @@ -156,4 +152,16 @@ class CsvFileWriter internal constructor( if (shouldQuote) append(ctx.quote.char) } } + + fun use(block: (SinkCsvWriterScope)->Unit) { + writer.use { + block(this) + } + } + + suspend fun useSuspend(block: suspend (SinkCsvWriterScope)->Unit) { + writer.use { + block(this) + } + } } diff --git a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvFileReader.kt b/src/commonMain/kotlin/com/jsoizo/kotlincsv/client/SourceCsvReaderScope.kt similarity index 83% rename from src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvFileReader.kt rename to src/commonMain/kotlin/com/jsoizo/kotlincsv/client/SourceCsvReaderScope.kt index 3577ab0..2f84359 100644 --- a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvFileReader.kt +++ b/src/commonMain/kotlin/com/jsoizo/kotlincsv/client/SourceCsvReaderScope.kt @@ -1,26 +1,27 @@ -package com.github.doyaaaaaken.kotlincsv.client +package com.jsoizo.kotlincsv.client -import com.github.doyaaaaaken.kotlincsv.dsl.context.CsvReaderContext -import com.github.doyaaaaaken.kotlincsv.dsl.context.ExcessFieldsRowBehaviour -import com.github.doyaaaaaken.kotlincsv.dsl.context.InsufficientFieldsRowBehaviour -import com.github.doyaaaaaken.kotlincsv.parser.CsvParser -import com.github.doyaaaaaken.kotlincsv.util.CSVAutoRenameFailedException -import com.github.doyaaaaaken.kotlincsv.util.CSVFieldNumDifferentException -import com.github.doyaaaaaken.kotlincsv.util.logger.Logger -import com.github.doyaaaaaken.kotlincsv.util.MalformedCSVException +import com.jsoizo.kotlincsv.dsl.CsvReaderScope +import com.jsoizo.kotlincsv.dsl.context.CsvReaderContext +import com.jsoizo.kotlincsv.dsl.context.ExcessFieldsRowBehaviour +import com.jsoizo.kotlincsv.dsl.context.InsufficientFieldsRowBehaviour +import com.jsoizo.kotlincsv.parser.CsvParser +import com.jsoizo.kotlincsv.util.CSVAutoRenameFailedException +import com.jsoizo.kotlincsv.util.CSVFieldNumDifferentException +import com.jsoizo.kotlincsv.util.logger.Logger +import com.jsoizo.kotlincsv.util.MalformedCSVException +import kotlinx.io.Source /** * CSV Reader class, which controls file I/O flow. * * @author doyaaaaaken */ -class CsvFileReader internal constructor( +internal class SourceCsvReaderScope internal constructor( private val ctx: CsvReaderContext, - reader: Reader, + source: Source, private val logger: Logger, -) { - - private val reader = BufferedLineReader(reader) +) : CsvReaderScope { + private val reader = SourceLineReader(source) private var rowNum = 0L private val parser = CsvParser(ctx.quoteChar, ctx.delimiter, ctx.escapeChar) @@ -40,7 +41,7 @@ class CsvFileReader internal constructor( /** * read all csv rows as Sequence */ - fun readAllAsSequence(fieldsNum: Int? = null): Sequence> { + override fun readAllAsSequence(fieldsNum: Int?): Sequence> { var expectedNumFieldsInRow: Int? = fieldsNum return generateSequence { @Suppress("DEPRECATION") readNext() @@ -60,7 +61,8 @@ class CsvFileReader internal constructor( throw CSVFieldNumDifferentException(numFieldsInRow, row.size, idx + 1) } } else if (numFieldsInRow != row.size) { - if (ctx.skipMissMatchedRow || ctx.insufficientFieldsRowBehaviour == InsufficientFieldsRowBehaviour.IGNORE) { + if (ctx.skipMissMatchedRow || + ctx.insufficientFieldsRowBehaviour == InsufficientFieldsRowBehaviour.IGNORE) { skipMismatchedRow(idx, row, numFieldsInRow) } else if (ctx.insufficientFieldsRowBehaviour == InsufficientFieldsRowBehaviour.EMPTY_STRING) { val numOfMissingFields = numFieldsInRow - row.size @@ -86,7 +88,7 @@ class CsvFileReader internal constructor( /** * read all csv rows as Sequence with header information */ - fun readAllWithHeaderAsSequence(): Sequence> { + override fun readAllWithHeaderAsSequence(): Sequence> { @Suppress("DEPRECATION") var headers = readNext() ?: return emptySequence() if (ctx.autoRenameDuplicateHeaders) { @@ -98,10 +100,6 @@ class CsvFileReader internal constructor( return readAllAsSequence(headers.size).map { fields -> headers.zip(fields).toMap() } } - fun close() { - reader.close() - } - /** * read next csv row (which may contain multiple lines) * diff --git a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/BufferedLineReader.kt b/src/commonMain/kotlin/com/jsoizo/kotlincsv/client/SourceLineReader.kt similarity index 55% rename from src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/BufferedLineReader.kt rename to src/commonMain/kotlin/com/jsoizo/kotlincsv/client/SourceLineReader.kt index 5d63ed7..036a2e8 100644 --- a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/BufferedLineReader.kt +++ b/src/commonMain/kotlin/com/jsoizo/kotlincsv/client/SourceLineReader.kt @@ -1,13 +1,14 @@ -package com.github.doyaaaaaken.kotlincsv.client +package com.jsoizo.kotlincsv.client -import com.github.doyaaaaaken.kotlincsv.util.Const +import com.jsoizo.kotlincsv.util.Const +import kotlinx.io.EOFException +import kotlinx.io.Source +import kotlinx.io.readCodePointValue /** - * buffered reader which can read line with line terminator + * reader from a [Source] which can read line with line terminator. */ -internal class BufferedLineReader( - private val br: Reader -) { +internal class SourceLineReader(private val br: Source) { companion object { private const val BOM = Const.BOM } @@ -18,9 +19,9 @@ internal class BufferedLineReader( fun readLineWithTerminator(): String? { val sb = StringBuilder() do { - val c = br.read() - - if (c == -1) { + val c = try{ + br.readCodePointValue() + } catch(_: EOFException){ if (sb.isEmptyLine()) { return null } else { @@ -35,23 +36,21 @@ internal class BufferedLineReader( } if (ch == '\r') { - br.mark(1) - val c2 = br.read() - if (c2 == -1) { + val s2 = br.peek() + + val c2 = try { + s2.readCodePointValue() + } catch(_: EOFException){ break - } else if (c2.toChar() == '\n') { - sb.append('\n') - } else { - br.reset() } + if (c2.toChar() == '\n') { + sb.append('\n') + br.readCodePointValue() + } break } } while (true) return sb.toString() } - - fun close() { - br.close() - } } diff --git a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/dsl/CsvReaderDsl.kt b/src/commonMain/kotlin/com/jsoizo/kotlincsv/dsl/CsvReaderDsl.kt similarity index 71% rename from src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/dsl/CsvReaderDsl.kt rename to src/commonMain/kotlin/com/jsoizo/kotlincsv/dsl/CsvReaderDsl.kt index 7b6a036..47c0a01 100644 --- a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/dsl/CsvReaderDsl.kt +++ b/src/commonMain/kotlin/com/jsoizo/kotlincsv/dsl/CsvReaderDsl.kt @@ -1,7 +1,8 @@ -package com.github.doyaaaaaken.kotlincsv.dsl +package com.jsoizo.kotlincsv.dsl -import com.github.doyaaaaaken.kotlincsv.client.CsvReader -import com.github.doyaaaaaken.kotlincsv.dsl.context.CsvReaderContext +import com.jsoizo.kotlincsv.client.CsvReader +import com.jsoizo.kotlincsv.client.CsvReaderImpl +import com.jsoizo.kotlincsv.dsl.context.CsvReaderContext /** * DSL Method which provides `CsvReader` @@ -31,5 +32,5 @@ import com.github.doyaaaaaken.kotlincsv.dsl.context.CsvReaderContext */ fun csvReader(init: CsvReaderContext.() -> Unit = {}): CsvReader { val context = CsvReaderContext().apply(init) - return CsvReader(context) + return CsvReaderImpl(context) } diff --git a/src/commonMain/kotlin/com/jsoizo/kotlincsv/dsl/CsvReaderScope.kt b/src/commonMain/kotlin/com/jsoizo/kotlincsv/dsl/CsvReaderScope.kt new file mode 100644 index 0000000..8b0d942 --- /dev/null +++ b/src/commonMain/kotlin/com/jsoizo/kotlincsv/dsl/CsvReaderScope.kt @@ -0,0 +1,13 @@ +package com.jsoizo.kotlincsv.dsl + +interface CsvReaderScope { + /** + * read all csv rows as Sequence + */ + fun readAllAsSequence(fieldsNum: Int? = null): Sequence> + + /** + * read all csv rows as Sequence with header information + */ + fun readAllWithHeaderAsSequence(): Sequence> +} diff --git a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/dsl/CsvWriterDsl.kt b/src/commonMain/kotlin/com/jsoizo/kotlincsv/dsl/CsvWriterDsl.kt similarity index 70% rename from src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/dsl/CsvWriterDsl.kt rename to src/commonMain/kotlin/com/jsoizo/kotlincsv/dsl/CsvWriterDsl.kt index 7706ba9..7eebe1a 100644 --- a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/dsl/CsvWriterDsl.kt +++ b/src/commonMain/kotlin/com/jsoizo/kotlincsv/dsl/CsvWriterDsl.kt @@ -1,7 +1,8 @@ -package com.github.doyaaaaaken.kotlincsv.dsl +package com.jsoizo.kotlincsv.dsl -import com.github.doyaaaaaken.kotlincsv.client.CsvWriter -import com.github.doyaaaaaken.kotlincsv.dsl.context.CsvWriterContext +import com.jsoizo.kotlincsv.client.CsvWriter +import com.jsoizo.kotlincsv.client.CsvWriterImpl +import com.jsoizo.kotlincsv.dsl.context.CsvWriterContext /** * DSL Method which provides `CsvWriter` @@ -30,5 +31,5 @@ import com.github.doyaaaaaken.kotlincsv.dsl.context.CsvWriterContext */ fun csvWriter(init: CsvWriterContext.() -> Unit = {}): CsvWriter { val context = CsvWriterContext().apply(init) - return CsvWriter(context) + return CsvWriterImpl(context) } diff --git a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/ICsvFileWriter.kt b/src/commonMain/kotlin/com/jsoizo/kotlincsv/dsl/CsvWriterScope.kt similarity index 72% rename from src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/ICsvFileWriter.kt rename to src/commonMain/kotlin/com/jsoizo/kotlincsv/dsl/CsvWriterScope.kt index 18e2537..1747c54 100644 --- a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/ICsvFileWriter.kt +++ b/src/commonMain/kotlin/com/jsoizo/kotlincsv/dsl/CsvWriterScope.kt @@ -1,6 +1,6 @@ -package com.github.doyaaaaaken.kotlincsv.client +package com.jsoizo.kotlincsv.dsl -interface ICsvFileWriter { +interface CsvWriterScope { fun writeRow(row: List) fun writeRow(vararg entry: Any?) diff --git a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/dsl/context/CsvReaderContext.kt b/src/commonMain/kotlin/com/jsoizo/kotlincsv/dsl/context/CsvReaderContext.kt similarity index 92% rename from src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/dsl/context/CsvReaderContext.kt rename to src/commonMain/kotlin/com/jsoizo/kotlincsv/dsl/context/CsvReaderContext.kt index ca520a0..2f5d3f2 100644 --- a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/dsl/context/CsvReaderContext.kt +++ b/src/commonMain/kotlin/com/jsoizo/kotlincsv/dsl/context/CsvReaderContext.kt @@ -1,9 +1,9 @@ -package com.github.doyaaaaaken.kotlincsv.dsl.context +package com.jsoizo.kotlincsv.dsl.context -import com.github.doyaaaaaken.kotlincsv.util.Const -import com.github.doyaaaaaken.kotlincsv.util.CsvDslMarker -import com.github.doyaaaaaken.kotlincsv.util.logger.Logger -import com.github.doyaaaaaken.kotlincsv.util.logger.LoggerNop +import com.jsoizo.kotlincsv.util.Const +import com.jsoizo.kotlincsv.util.CsvDslMarker +import com.jsoizo.kotlincsv.util.logger.Logger +import com.jsoizo.kotlincsv.util.logger.LoggerNop /** * Interface for CSV Reader settings diff --git a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/dsl/context/CsvWriteQuoteContext.kt b/src/commonMain/kotlin/com/jsoizo/kotlincsv/dsl/context/CsvWriteQuoteContext.kt similarity index 89% rename from src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/dsl/context/CsvWriteQuoteContext.kt rename to src/commonMain/kotlin/com/jsoizo/kotlincsv/dsl/context/CsvWriteQuoteContext.kt index 01c6228..3abdb38 100644 --- a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/dsl/context/CsvWriteQuoteContext.kt +++ b/src/commonMain/kotlin/com/jsoizo/kotlincsv/dsl/context/CsvWriteQuoteContext.kt @@ -1,6 +1,6 @@ -package com.github.doyaaaaaken.kotlincsv.dsl.context +package com.jsoizo.kotlincsv.dsl.context -import com.github.doyaaaaaken.kotlincsv.util.CsvDslMarker +import com.jsoizo.kotlincsv.util.CsvDslMarker /** * DSL method for Quote settings on writing csv. diff --git a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/dsl/context/CsvWriterContext.kt b/src/commonMain/kotlin/com/jsoizo/kotlincsv/dsl/context/CsvWriterContext.kt similarity index 80% rename from src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/dsl/context/CsvWriterContext.kt rename to src/commonMain/kotlin/com/jsoizo/kotlincsv/dsl/context/CsvWriterContext.kt index 20f0c9f..3581268 100644 --- a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/dsl/context/CsvWriterContext.kt +++ b/src/commonMain/kotlin/com/jsoizo/kotlincsv/dsl/context/CsvWriterContext.kt @@ -1,7 +1,6 @@ -package com.github.doyaaaaaken.kotlincsv.dsl.context +package com.jsoizo.kotlincsv.dsl.context -import com.github.doyaaaaaken.kotlincsv.util.Const -import com.github.doyaaaaaken.kotlincsv.util.CsvDslMarker +import com.jsoizo.kotlincsv.util.CsvDslMarker /** * Interface for CSV Writer settings @@ -10,17 +9,6 @@ import com.github.doyaaaaaken.kotlincsv.util.CsvDslMarker */ @CsvDslMarker interface ICsvWriterContext { - /** - * Charset encoding - * - * The name must be supported by [java.nio.charset.Charset]. - * - * ex.) - * "UTF-8" - * "SJIS" - */ - val charset: String - /** * Character used as delimiter between each fields * @@ -76,7 +64,6 @@ interface ICsvWriterContext { */ @CsvDslMarker class CsvWriterContext : ICsvWriterContext { - override var charset = Const.defaultCharset override var delimiter: Char = ',' override var nullCode: String = "" override var lineTerminator: String = "\r\n" diff --git a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/parser/CsvParser.kt b/src/commonMain/kotlin/com/jsoizo/kotlincsv/parser/CsvParser.kt similarity index 95% rename from src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/parser/CsvParser.kt rename to src/commonMain/kotlin/com/jsoizo/kotlincsv/parser/CsvParser.kt index 47cfe65..199fc4f 100644 --- a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/parser/CsvParser.kt +++ b/src/commonMain/kotlin/com/jsoizo/kotlincsv/parser/CsvParser.kt @@ -1,4 +1,4 @@ -package com.github.doyaaaaaken.kotlincsv.parser +package com.jsoizo.kotlincsv.parser /** * Csv Parse logic while reading csv diff --git a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/parser/ParseStateMachine.kt b/src/commonMain/kotlin/com/jsoizo/kotlincsv/parser/ParseStateMachine.kt similarity index 97% rename from src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/parser/ParseStateMachine.kt rename to src/commonMain/kotlin/com/jsoizo/kotlincsv/parser/ParseStateMachine.kt index 7dd2989..61789c2 100644 --- a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/parser/ParseStateMachine.kt +++ b/src/commonMain/kotlin/com/jsoizo/kotlincsv/parser/ParseStateMachine.kt @@ -1,7 +1,7 @@ -package com.github.doyaaaaaken.kotlincsv.parser +package com.jsoizo.kotlincsv.parser -import com.github.doyaaaaaken.kotlincsv.util.CSVParseFormatException -import com.github.doyaaaaaken.kotlincsv.util.Const +import com.jsoizo.kotlincsv.util.CSVParseFormatException +import com.jsoizo.kotlincsv.util.Const /** * @author doyaaaaaaken diff --git a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/util/CSVException.kt b/src/commonMain/kotlin/com/jsoizo/kotlincsv/util/CSVException.kt similarity index 96% rename from src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/util/CSVException.kt rename to src/commonMain/kotlin/com/jsoizo/kotlincsv/util/CSVException.kt index 5451608..7d85323 100644 --- a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/util/CSVException.kt +++ b/src/commonMain/kotlin/com/jsoizo/kotlincsv/util/CSVException.kt @@ -1,4 +1,4 @@ -package com.github.doyaaaaaken.kotlincsv.util +package com.jsoizo.kotlincsv.util /** * General purpose Exception diff --git a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/util/Const.kt b/src/commonMain/kotlin/com/jsoizo/kotlincsv/util/Const.kt similarity index 78% rename from src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/util/Const.kt rename to src/commonMain/kotlin/com/jsoizo/kotlincsv/util/Const.kt index a5cae91..3a5b641 100644 --- a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/util/Const.kt +++ b/src/commonMain/kotlin/com/jsoizo/kotlincsv/util/Const.kt @@ -1,4 +1,4 @@ -package com.github.doyaaaaaken.kotlincsv.util +package com.jsoizo.kotlincsv.util /** * Constant variables used in this project diff --git a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/util/CsvDslMarker.kt b/src/commonMain/kotlin/com/jsoizo/kotlincsv/util/CsvDslMarker.kt similarity index 61% rename from src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/util/CsvDslMarker.kt rename to src/commonMain/kotlin/com/jsoizo/kotlincsv/util/CsvDslMarker.kt index cf61f5e..1640ec5 100644 --- a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/util/CsvDslMarker.kt +++ b/src/commonMain/kotlin/com/jsoizo/kotlincsv/util/CsvDslMarker.kt @@ -1,4 +1,4 @@ -package com.github.doyaaaaaken.kotlincsv.util +package com.jsoizo.kotlincsv.util /** * @author doyaaaaaken diff --git a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/util/logger/Logger.kt b/src/commonMain/kotlin/com/jsoizo/kotlincsv/util/logger/Logger.kt similarity index 58% rename from src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/util/logger/Logger.kt rename to src/commonMain/kotlin/com/jsoizo/kotlincsv/util/logger/Logger.kt index 92b55e6..b71a605 100644 --- a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/util/logger/Logger.kt +++ b/src/commonMain/kotlin/com/jsoizo/kotlincsv/util/logger/Logger.kt @@ -1,9 +1,9 @@ -package com.github.doyaaaaaken.kotlincsv.util.logger +package com.jsoizo.kotlincsv.util.logger /** * Logger interface for logging debug statements at runtime. * Library consumers may provide implementations suiting their needs. - * @see [com.github.doyaaaaaken.kotlincsv.dsl.context.ICsvReaderContext.logger] + * @see [com.jsoizo.kotlincsv.dsl.context.ICsvReaderContext.logger] */ interface Logger { fun info(message: String) diff --git a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/util/logger/LoggerNop.kt b/src/commonMain/kotlin/com/jsoizo/kotlincsv/util/logger/LoggerNop.kt similarity index 76% rename from src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/util/logger/LoggerNop.kt rename to src/commonMain/kotlin/com/jsoizo/kotlincsv/util/logger/LoggerNop.kt index 4893d7d..05ec6e6 100644 --- a/src/commonMain/kotlin/com/github/doyaaaaaken/kotlincsv/util/logger/LoggerNop.kt +++ b/src/commonMain/kotlin/com/jsoizo/kotlincsv/util/logger/LoggerNop.kt @@ -1,4 +1,4 @@ -package com.github.doyaaaaaken.kotlincsv.util.logger +package com.jsoizo.kotlincsv.util.logger /** * Internal no-operation logger implementation, which does not log anything. diff --git a/src/commonTest/kotlin/com/jsoizo/kotlincsv/client/CsvReadWriteCompatibilityTest.kt b/src/commonTest/kotlin/com/jsoizo/kotlincsv/client/CsvReadWriteCompatibilityTest.kt new file mode 100644 index 0000000..49d2b87 --- /dev/null +++ b/src/commonTest/kotlin/com/jsoizo/kotlincsv/client/CsvReadWriteCompatibilityTest.kt @@ -0,0 +1,21 @@ +package com.jsoizo.kotlincsv.client + +import com.jsoizo.kotlincsv.dsl.context.CsvReaderContext +import com.jsoizo.kotlincsv.dsl.context.CsvWriterContext +import io.kotest.core.spec.style.StringSpec +import io.kotest.matchers.shouldBe +import kotlinx.io.Buffer + +class CsvReadWriteCompatibilityTest : StringSpec({ + "CSVReader and CSVWriter are compatible" { + val data = listOf( + listOf("a", "bb", "ccc"), + listOf("d", "ee", "fff") + ) + val buffer = Buffer() + SinkCsvWriterScope(CsvWriterContext(), buffer).writeRows(data) + val rctx = CsvReaderContext() + val actual = SourceCsvReaderScope(rctx, buffer, rctx.logger).readAllAsSequence().toList() + actual shouldBe data + } +}) diff --git a/src/commonTest/kotlin/com/jsoizo/kotlincsv/client/CsvReaderImplTest.kt b/src/commonTest/kotlin/com/jsoizo/kotlincsv/client/CsvReaderImplTest.kt new file mode 100644 index 0000000..68534ef --- /dev/null +++ b/src/commonTest/kotlin/com/jsoizo/kotlincsv/client/CsvReaderImplTest.kt @@ -0,0 +1,104 @@ +package com.jsoizo.kotlincsv.client + +import com.jsoizo.kotlincsv.dsl.context.CsvReaderContext +import io.kotest.assertions.assertSoftly +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.spec.style.StringSpec +import io.kotest.matchers.should +import io.kotest.matchers.shouldBe +import kotlinx.io.Buffer + +class CsvReaderImplTest : StringSpec({ + "CsvReader class constructor" should { + "be created with CsvReaderContext argument" { + val context = CsvReaderContext().apply { + quoteChar = '\'' + delimiter = '\t' + escapeChar = '"' + skipEmptyLine = true + } + val reader = CsvReaderImpl(context) + assertSoftly { + reader.quoteChar shouldBe '\'' + reader.delimiter shouldBe '\t' + reader.escapeChar shouldBe '"' + reader.skipEmptyLine shouldBe true + } + } + } + + "readAll method (with String argument)" should { + "read simple csv" { + val result = CsvReaderImpl().readAll( + """a,b,c + |d,e,f + """.trimMargin() + ) + result shouldBe listOf(listOf("a", "b", "c"), listOf("d", "e", "f")) + } + "read simple csv as receiver" { + val result = """a,b,c + |d,e,f + """.trimMargin().csvReadAll() + result shouldBe listOf(listOf("a", "b", "c"), listOf("d", "e", "f")) + } + "read simple csv as Source" { + Buffer().apply { + write("""a,b,c + |d,e,f + """.trimMargin().encodeToByteArray()) + }.use { + CsvReaderImpl().readAll(it) shouldBe listOf(listOf("a", "b", "c"), listOf("d", "e", "f")) + } + } + "read simple csv as Source receiver" { + Buffer().apply { + write("""a,b,c + |d,e,f + """.trimMargin().encodeToByteArray()) + }.use { + it.csvReadAll() shouldBe listOf(listOf("a", "b", "c"), listOf("d", "e", "f")) + } + } + "read csv with line separator" { + val result = CsvReaderImpl().readAll( + """a,b,c,"x","y + | hoge" + |d,e,f,g,h + """.trimMargin() + ) + val firstRow = listOf( + "a", "b", "c", "x", """y + | hoge""".trimMargin() + ) + val secondRow = listOf("d", "e", "f", "g", "h") + result shouldBe listOf(firstRow, secondRow) + } + "get failed rowNum and colIndex when exception happened on parsing CSV" { + val reader = CsvReaderImpl() + val ex1 = shouldThrow { + reader.readAll("a,\"\"failed") + } + val ex2 = shouldThrow { + reader.readAll("a,b\nc,\"\"failed") + } + val ex3 = shouldThrow { + reader.readAll("a,\"b\nb\"\nc,\"\"failed") + } + + assertSoftly { + ex1.rowNum shouldBe 1 + ex1.colIndex shouldBe 4 + ex1.char shouldBe 'f' + + ex2.rowNum shouldBe 2 + ex2.colIndex shouldBe 4 + ex2.char shouldBe 'f' + + ex3.rowNum shouldBe 3 + ex3.colIndex shouldBe 4 + ex3.char shouldBe 'f' + } + } + } +}) diff --git a/src/commonTest/kotlin/com/jsoizo/kotlincsv/client/SinkCsvWriterScopeTest.kt b/src/commonTest/kotlin/com/jsoizo/kotlincsv/client/SinkCsvWriterScopeTest.kt new file mode 100644 index 0000000..94080e9 --- /dev/null +++ b/src/commonTest/kotlin/com/jsoizo/kotlincsv/client/SinkCsvWriterScopeTest.kt @@ -0,0 +1,102 @@ +package com.jsoizo.kotlincsv.client + +import com.jsoizo.kotlincsv.dsl.CsvWriterScope +import com.jsoizo.kotlincsv.dsl.context.CsvWriterContext +import io.kotest.core.spec.style.StringSpec +import io.kotest.matchers.shouldBe +import kotlinx.datetime.LocalDate +import kotlinx.datetime.LocalDateTime +import kotlinx.io.Buffer +import kotlinx.io.readString + +class SinkCsvWriterScopeTest : StringSpec({ + fun writeToBuffer(write: CsvWriterScope.() -> Unit) : Buffer { + val ctx = CsvWriterContext() + val buffer = Buffer() + SinkCsvWriterScope(ctx, buffer).apply { write() }.close() + return buffer + } + + "writeRow method should write any primitive types" { + val row = listOf("String", 'C', 1, 2L, 3.45, true, null) + val expected = "String,C,1,2,3.45,true,\r\n" + + writeToBuffer { writeRow(row) }.readString() shouldBe expected + } + "writeRow method should write kotlinx.datetime.LocalDate and kotlinx.datetime.LocalDateTime types" { + val row = listOf( + LocalDate(2019, 8, 19), + LocalDateTime(2020, 9, 20, 14, 32, 21) + ) + val expected = "2019-08-19,2020-09-20T14:32:21\r\n" + writeToBuffer { writeRow(row) }.readString() shouldBe expected + } + "writeRow method should write row from variable arguments" { + val date1 = LocalDate(2019, 8, 19) + val date2 = LocalDateTime(2020, 9, 20, 14, 32, 21) + + val expected = "a,b,c\r\n" + + "d,e,f\r\n" + + "1,2,3\r\n" + + "2019-08-19,2020-09-20T14:32:21\r\n" + writeToBuffer { + writeRow("a", "b", "c") + writeRow("d", "e", "f") + writeRow(1, 2, 3) + writeRow(date1, date2) + }.readString() shouldBe expected + } + "writeAll method should write Sequence data" { + val rows = listOf(listOf("a", "b", "c"), listOf("d", "e", "f")).asSequence() + val expected = "a,b,c\r\nd,e,f\r\n" + writeToBuffer { + writeRows(rows) + }.readString() shouldBe expected + } + "writeAll method should write escaped field when a field contains quoteChar in it" { + val rows = listOf(listOf("a", "\"b", "c"), listOf("d", "e", "f\"")) + val expected = "a,\"\"\"b\",c\r\nd,e,\"f\"\"\"\r\n" + writeToBuffer{ + writeRows(rows) + }.readString() shouldBe expected + } + "writeAll method should write escaped field when a field contains delimiter in it" { + val rows = listOf(listOf("a", ",b", "c"), listOf("d", "e", "f,")) + val expected = "a,\",b\",c\r\nd,e,\"f,\"\r\n" + writeToBuffer{ + writeRows(rows) + }.readString() shouldBe expected + } + "writeAll method should write quoted field when a field contains cr or lf in it" { + val rows = listOf(listOf("a", "\nb", "c"), listOf("d", "e", "f\r\n")) + val expected = "a,\"\nb\",c\r\nd,e,\"f\r\n\"\r\n" + writeToBuffer{ + writeRows(rows) + }.readString() shouldBe expected + } + "writeAll method should write no line terminator when row is empty for rows from list" { + val rows = listOf(listOf("a", "b", "c"), listOf(), listOf("d", "e", "f")) + val expected = "a,b,c\r\nd,e,f\r\n" + writeToBuffer{ + writeRows(rows) + }.readString() shouldBe expected + } + "writeAll method should write no line terminator when row is empty for rows from sequence" { + val rows = listOf(listOf("a", "b", "c"), listOf(), listOf("d", "e", "f")).asSequence() + val expected = "a,b,c\r\nd,e,f\r\n" + writeToBuffer{ + writeRows(rows) + }.readString() shouldBe expected + } + + "flush method should flush stream" { + val row = listOf("a", "b") + val ctx = CsvWriterContext() + val buffer = Buffer() + SinkCsvWriterScope(ctx, buffer).apply { + writeRow(row) + flush() + buffer.readString() shouldBe "a,b\r\n" + } + } +}) diff --git a/src/jvmTest/kotlin/com/github/doyaaaaaken/kotlincsv/client/BufferedLineReaderTest.kt b/src/commonTest/kotlin/com/jsoizo/kotlincsv/client/SourceLineReaderTest.kt similarity index 71% rename from src/jvmTest/kotlin/com/github/doyaaaaaken/kotlincsv/client/BufferedLineReaderTest.kt rename to src/commonTest/kotlin/com/jsoizo/kotlincsv/client/SourceLineReaderTest.kt index 018425b..e0e6f7f 100644 --- a/src/jvmTest/kotlin/com/github/doyaaaaaken/kotlincsv/client/BufferedLineReaderTest.kt +++ b/src/commonTest/kotlin/com/jsoizo/kotlincsv/client/SourceLineReaderTest.kt @@ -1,14 +1,15 @@ -package com.github.doyaaaaaken.kotlincsv.client +package com.jsoizo.kotlincsv.client import io.kotest.assertions.assertSoftly import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe +import kotlinx.io.Buffer -class BufferedLineReaderTest : StringSpec({ +class SourceLineReaderTest : StringSpec({ "regard \\n as line terminator" { val str = "a,b,c\nd,e,f" - val br = str.byteInputStream().bufferedReader() - val blr = BufferedLineReader(br) + val br = Buffer().apply{ write(str.encodeToByteArray()) } + val blr = SourceLineReader(br) assertSoftly { blr.readLineWithTerminator() shouldBe "a,b,c\n" blr.readLineWithTerminator() shouldBe "d,e,f" @@ -18,8 +19,8 @@ class BufferedLineReaderTest : StringSpec({ "regard \\r\\n as line terminator" { val str = "a,b,c\r\nd,e,f" - val br = str.byteInputStream().bufferedReader() - val blr = BufferedLineReader(br) + val br = Buffer().apply{ write(str.encodeToByteArray()) } + val blr = SourceLineReader(br) assertSoftly { blr.readLineWithTerminator() shouldBe "a,b,c\r\n" blr.readLineWithTerminator() shouldBe "d,e,f" @@ -29,8 +30,8 @@ class BufferedLineReaderTest : StringSpec({ "regard \\r as line terminator" { val str = "a,b,c\rd,e,f" - val br = str.byteInputStream().bufferedReader() - val blr = BufferedLineReader(br) + val br = Buffer().apply{ write(str.encodeToByteArray()) } + val blr = SourceLineReader(br) assertSoftly { blr.readLineWithTerminator() shouldBe "a,b,c\r" blr.readLineWithTerminator() shouldBe "d,e,f" @@ -40,8 +41,8 @@ class BufferedLineReaderTest : StringSpec({ "regard \\u2028 as line terminator" { val str = "a,b,c\u2028d,e,f" - val br = str.byteInputStream().bufferedReader() - val blr = BufferedLineReader(br) + val br = Buffer().apply{ write(str.encodeToByteArray()) } + val blr = SourceLineReader(br) assertSoftly { blr.readLineWithTerminator() shouldBe "a,b,c\u2028" blr.readLineWithTerminator() shouldBe "d,e,f" @@ -51,8 +52,8 @@ class BufferedLineReaderTest : StringSpec({ "regard \\u2029 as line terminator" { val str = "a,b,c\u2029d,e,f" - val br = str.byteInputStream().bufferedReader() - val blr = BufferedLineReader(br) + val br = Buffer().apply{ write(str.encodeToByteArray()) } + val blr = SourceLineReader(br) assertSoftly { blr.readLineWithTerminator() shouldBe "a,b,c\u2029" blr.readLineWithTerminator() shouldBe "d,e,f" @@ -62,8 +63,8 @@ class BufferedLineReaderTest : StringSpec({ "regard \\u0085 as line terminator" { val str = "a,b,c\u0085d,e,f" - val br = str.byteInputStream().bufferedReader() - val blr = BufferedLineReader(br) + val br = Buffer().apply{ write(str.encodeToByteArray()) } + val blr = SourceLineReader(br) assertSoftly { blr.readLineWithTerminator() shouldBe "a,b,c\u0085" blr.readLineWithTerminator() shouldBe "d,e,f" @@ -73,8 +74,8 @@ class BufferedLineReaderTest : StringSpec({ "deal with \\r at the end of file" { val str = "a,b,c\r" - val br = str.byteInputStream().bufferedReader() - val blr = BufferedLineReader(br) + val br = Buffer().apply{ write(str.encodeToByteArray()) } + val blr = SourceLineReader(br) assertSoftly { blr.readLineWithTerminator() shouldBe "a,b,c\r" blr.readLineWithTerminator() shouldBe null diff --git a/src/jsMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvReader.kt b/src/jsMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvReader.kt deleted file mode 100644 index 7792b44..0000000 --- a/src/jsMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvReader.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.github.doyaaaaaken.kotlincsv.client - -import com.github.doyaaaaaken.kotlincsv.dsl.context.CsvReaderContext -import com.github.doyaaaaaken.kotlincsv.dsl.context.ICsvReaderContext - -/** - * CSV Reader class - * - * @author doyaaaaaken - */ -actual class CsvReader actual constructor( - private val ctx: CsvReaderContext -) : ICsvReaderContext by ctx { - - /** - * read csv data as String, and convert into List> - */ - actual fun readAll(data: String): List> { - return CsvFileReader(ctx, StringReaderImpl(data), logger).readAllAsSequence().toList() - } - - /** - * read csv data with header, and convert into List> - */ - actual fun readAllWithHeader(data: String): List> { - return CsvFileReader(ctx, StringReaderImpl(data), logger).readAllWithHeaderAsSequence().toList() - } -} diff --git a/src/jsMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvWriter.kt b/src/jsMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvWriter.kt deleted file mode 100644 index a52ad8f..0000000 --- a/src/jsMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvWriter.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.github.doyaaaaaken.kotlincsv.client - -import com.github.doyaaaaaken.kotlincsv.dsl.context.CsvWriterContext - -/** - * CSV Writer class - * - * @author doyaaaaaken - */ -actual class CsvWriter actual constructor(ctx: CsvWriterContext) { - - actual fun open(targetFileName: String, append: Boolean, write: ICsvFileWriter.() -> Unit) { - TODO("Not Implemented") - } - - actual fun writeAll(rows: List>, targetFileName: String, append: Boolean) { - TODO("Not Implemented") - } - - actual suspend fun writeAllAsync(rows: List>, targetFileName: String, append: Boolean) { - TODO("Not Implemented") - } - - actual suspend fun openAsync(targetFileName: String, append: Boolean, write: suspend ICsvFileWriter.() -> Unit) { - TODO("Not Implemented") - } -} diff --git a/src/jvmMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvReader.kt b/src/jvmMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvReader.kt deleted file mode 100644 index a02256e..0000000 --- a/src/jvmMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvReader.kt +++ /dev/null @@ -1,207 +0,0 @@ -package com.github.doyaaaaaken.kotlincsv.client - -import com.github.doyaaaaaken.kotlincsv.dsl.context.CsvReaderContext -import com.github.doyaaaaaken.kotlincsv.dsl.context.ICsvReaderContext -import java.io.File -import java.io.InputStream -import java.nio.charset.Charset - -/** - * CSV Reader class - * - * @author doyaaaaaken - */ -actual class CsvReader actual constructor( - private val ctx: CsvReaderContext -) : ICsvReaderContext by ctx { - - private val charsetCode = Charset.forName(charset) - - /** - * read csv data as String, and convert into List> - * - * No need to close InputStream when calling this method. - */ - actual fun readAll(data: String): List> { - val br = data.byteInputStream(charsetCode).bufferedReader(charsetCode) - return open(br) { readAllAsSequence().toList() } - } - - /** - * read csv data as File, and convert into List> - * - * No need to close InputStream when calling this method. - */ - fun readAll(file: File): List> { - val br = file.inputStream().bufferedReader(charsetCode) - return open(br) { readAllAsSequence().toList() } - } - - /** - * read csv data as InputStream, and convert into List> - * - * No need to close InputStream when calling this method. - */ - fun readAll(ips: InputStream): List> { - val br = ips.bufferedReader(charsetCode) - return open(br) { readAllAsSequence().toList() } - } - - /** - * read csv data with header, and convert into List> - * - * No need to close InputStream when calling this method. - */ - actual fun readAllWithHeader(data: String): List> { - val br = data.byteInputStream(charsetCode).bufferedReader(charsetCode) - return open(br) { readAllWithHeaderAsSequence().toList() } - } - - /** - * read csv data with header, and convert into List> - * - * No need to close InputStream when calling this method. - */ - fun readAllWithHeader(file: File): List> { - val br = file.inputStream().bufferedReader(charsetCode) - return open(br) { readAllWithHeaderAsSequence().toList() } - } - - /** - * read csv data with header, and convert into List> - * - * No need to close InputStream when calling this method. - */ - fun readAllWithHeader(ips: InputStream): List> { - val br = ips.bufferedReader(charsetCode) - return open(br) { readAllWithHeaderAsSequence().toList() } - } - - /** - * open inputStreamReader and execute reading process. - * - * If you want to control read flow precisely, use this method. - * Otherwise, use utility method (e.g. CsvReader.readAll ). - * - * Usage example: - *
-     *   val data: Sequence> = csvReader().open("test.csv") {
-     *       readAllAsSequence()
-     *           .map { fields -> fields.map { it.trim() } }
-     *           .map { fields -> fields.map { if(it.isBlank()) null else it } }
-     *   }
-     * 
- */ - fun open(fileName: String, read: CsvFileReader.() -> T): T { - return open(File(fileName), read) - } - - /** - * open inputStreamReader and execute reading process on a **suspending** function. - * - * If you want to control read flow precisely, use this method. - * Otherwise, use utility method (e.g. CsvReader.readAll ). - * - * Usage example: - *
-     *   val data: Sequence> = csvReader().openAsync("test.csv") {
-     *       readAllAsSequence()
-     *           .map { fields -> fields.map { it.trim() } }
-     *           .map { fields -> fields.map { if(it.isBlank()) null else it } }
-     *   }
-     * 
- */ - suspend fun openAsync(fileName: String, read: suspend CsvFileReader.() -> T): T { - return openAsync(File(fileName), read) - } - - /** - * open inputStreamReader and execute reading process. - * - * If you want to control read flow precisely, use this method. - * Otherwise, use utility method (e.g. CsvReader.readAll ). - * - * Usage example: - * @see open method - */ - fun open(file: File, read: CsvFileReader.() -> T): T { - val br = file.inputStream().bufferedReader(charsetCode) - return open(br, read) - } - - /** - * open inputStreamReader and execute reading process on a **suspending** function. - * - * If you want to control read flow precisely, use this method. - * Otherwise, use utility method (e.g. CsvReader.readAll ). - * - * Usage example: - * @see openAsync method - */ - suspend fun openAsync(file: File, read: suspend CsvFileReader.() -> T): T { - val br = file.inputStream().bufferedReader(charsetCode) - return openAsync(br, read) - } - - /** - * open inputStreamReader and execute reading process on a **suspending** function. - * - * If you want to control read flow precisely, use this method. - * Otherwise, use utility method (e.g. CsvReader.readAll ). - * - * Usage example: - * @see open method - */ - fun open(ips: InputStream, read: CsvFileReader.() -> T): T { - val br = ips.bufferedReader(charsetCode) - return open(br, read) - } - - /** - * open inputStreamReader and execute reading process on a **suspending** function. - * - * If you want to control read flow precisely, use this method. - * Otherwise, use utility method (e.g. CsvReader.readAll ). - * - * Usage example: - * @see openAsync method - */ - suspend fun openAsync(ips: InputStream, read: suspend CsvFileReader.() -> T): T { - val br = ips.bufferedReader(charsetCode) - return openAsync(br, read) - } - - private fun open(br: Reader, doRead: CsvFileReader.() -> T): T { - val reader = CsvFileReader(ctx, br, logger) - return reader.use { - reader.doRead() - } - } - - private suspend fun openAsync(br: Reader, doRead: suspend CsvFileReader.() -> T): T { - val reader = CsvFileReader(ctx, br, logger) - return reader.use { - reader.doRead() - } - } -} - -private inline fun CsvFileReader.use(block: (CsvFileReader) -> R): R { - var exception: Throwable? = null - try { - return block(this) - } catch (e: Throwable) { - exception = e - throw e - } finally { - when (exception) { - null -> close() - else -> - try { - close() - } catch (t: Throwable) { - exception.addSuppressed(t) - } - } - } -} diff --git a/src/jvmMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvWriter.kt b/src/jvmMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvWriter.kt deleted file mode 100644 index c3adf1a..0000000 --- a/src/jvmMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvWriter.kt +++ /dev/null @@ -1,150 +0,0 @@ -package com.github.doyaaaaaken.kotlincsv.client - -import com.github.doyaaaaaken.kotlincsv.dsl.context.CsvWriterContext -import com.github.doyaaaaaken.kotlincsv.dsl.context.ICsvWriterContext -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import java.io.* -import java.nio.charset.Charset - -/** - * CSV Writer class, which decides where to write and returns CsvFileWriter class (class for controlling File I/O). - * - * @see CsvFileWriter - * - * @author doyaaaaaken - */ -actual class CsvWriter actual constructor( - private val ctx: CsvWriterContext -) : ICsvWriterContext by ctx { - - actual fun open(targetFileName: String, append: Boolean, write: ICsvFileWriter.() -> Unit) { - val targetFile = File(targetFileName) - open(targetFile, append, write) - } - - actual suspend fun openAsync(targetFileName: String, append: Boolean, write: suspend ICsvFileWriter.() -> Unit) { - val targetFile = File(targetFileName) - openAsync(targetFile, append, write) - } - - fun open(targetFile: File, append: Boolean = false, write: ICsvFileWriter.() -> Unit) { - val fos = FileOutputStream(targetFile, append).buffered() - open(fos, write) - } - - suspend fun openAsync(targetFile: File, append: Boolean = false, write: suspend ICsvFileWriter.() -> Unit) = - withContext(Dispatchers.IO) { - val fos = FileOutputStream(targetFile, append).buffered() - openAsync(fos, write) - } - - fun open(ops: OutputStream, write: ICsvFileWriter.() -> Unit) { - val osw = OutputStreamWriter(ops, ctx.charset) - val writer = CsvFileWriter(ctx, PrintWriter(osw)) - writer.use { it.write() } - } - - suspend fun openAsync(ops: OutputStream, write: suspend ICsvFileWriter.() -> Unit) = withContext(Dispatchers.IO) { - val osw = OutputStreamWriter(ops, ctx.charset) - val writer = CsvFileWriter(ctx, PrintWriter(osw)) - writer.use { it.write() } - } - - internal fun writeAsString(write: ICsvFileWriter.() -> Unit): String { - val baos = ByteArrayOutputStream() - open(baos, write) - return String(baos.toByteArray(), Charset.forName(ctx.charset)) - } - - /** - * *** ONLY for long-running write case *** - * - * Get and use [CsvFileWriter] directly. - * MUST NOT forget to close [CsvFileWriter] after using it. - * - * Use this method If you want to close file writer manually (i.e. streaming scenario). - */ - @KotlinCsvExperimental - fun openAndGetRawWriter(targetFileName: String, append: Boolean = false): CsvFileWriter { - val targetFile = File(targetFileName) - return openAndGetRawWriter(targetFile, append) - } - - /** - * *** ONLY for long-running write case *** - * - * Get and use [CsvFileWriter] directly. - * MUST NOT forget to close [CsvFileWriter] after using it. - * - * Use this method If you want to close file writer manually (i.e. streaming scenario). - */ - @KotlinCsvExperimental - fun openAndGetRawWriter(targetFile: File, append: Boolean = false): CsvFileWriter { - val fos = FileOutputStream(targetFile, append) - return openAndGetRawWriter(fos) - } - - /** - * *** ONLY for long-running write case *** - * - * Get and use [CsvFileWriter] directly. - * MUST NOT forget to close [CsvFileWriter] after using it. - * - * Use this method If you want to close file writer manually (i.e. streaming scenario). - */ - @KotlinCsvExperimental - fun openAndGetRawWriter(ops: OutputStream): CsvFileWriter { - val osw = OutputStreamWriter(ops, ctx.charset) - return CsvFileWriter(ctx, PrintWriter(osw)) - } - - /** - * write all rows on assigned target file - */ - actual fun writeAll(rows: List>, targetFileName: String, append: Boolean) { - open(targetFileName, append) { writeRows(rows) } - } - - /** - * write all rows on assigned target file - */ - actual suspend fun writeAllAsync(rows: List>, targetFileName: String, append: Boolean) { - openAsync(targetFileName, append) { writeRows(rows) } - } - - /** - * write all rows on assigned target file - */ - fun writeAll(rows: List>, targetFile: File, append: Boolean = false) { - open(targetFile, append) { writeRows(rows) } - } - - /** - * write all rows on assigned target file - */ - suspend fun writeAllAsync(rows: List>, targetFile: File, append: Boolean = false) { - openAsync(targetFile, append) { writeRows(rows) } - } - - /** - * write all rows on assigned output stream - */ - fun writeAll(rows: List>, ops: OutputStream) { - open(ops) { writeRows(rows) } - } - - /** - * write all rows on assigned output stream - */ - suspend fun writeAllAsync(rows: List>, ops: OutputStream) { - openAsync(ops) { writeRows(rows) } - } - - /** - * write all rows to string - */ - fun writeAllAsString(rows: List>): String { - return writeAsString { writeRows(rows) } - } -} diff --git a/src/jvmMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/Reader.kt b/src/jvmMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/Reader.kt deleted file mode 100644 index 9b8281a..0000000 --- a/src/jvmMain/kotlin/com/github/doyaaaaaken/kotlincsv/client/Reader.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.github.doyaaaaaken.kotlincsv.client - -import java.io.InputStream -import java.nio.charset.Charset - -class ReaderJvmImpl(private val reader: java.io.BufferedReader) : Reader { - override fun read(): Int { - return reader.read() - } - - override fun mark(readAheadLimit: Int) { - reader.mark(readAheadLimit) - } - - override fun reset() { - reader.reset() - } - - override fun close() { - reader.close() - } -} - -internal fun InputStream.bufferedReader(charset: Charset = Charsets.UTF_8): Reader = - ReaderJvmImpl(reader(charset).buffered()) diff --git a/src/jvmTest/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvFileWriterTest.kt b/src/jvmTest/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvFileWriterTest.kt deleted file mode 100644 index 4021679..0000000 --- a/src/jvmTest/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvFileWriterTest.kt +++ /dev/null @@ -1,218 +0,0 @@ -package com.github.doyaaaaaken.kotlincsv.client - -import com.github.doyaaaaaken.kotlincsv.dsl.csvWriter -import io.kotest.assertions.throwables.shouldThrow -import io.kotest.core.spec.style.WordSpec -import io.kotest.matchers.shouldBe -import kotlinx.coroutines.delay -import java.io.File -import java.io.IOException -import java.nio.charset.Charset -import java.time.LocalDate -import java.time.LocalDateTime - -class CsvFileWriterTest : WordSpec({ - val testFileName = "test.csv" - - afterTest { File(testFileName).delete() } - - fun readTestFile(charset: Charset = Charsets.UTF_8): String { - return File(testFileName).readText(charset) - } - - "writeRow method" should { - "write any primitive types" { - val row = listOf("String", 'C', 1, 2L, 3.45, true, null) - val expected = "String,C,1,2,3.45,true,\r\n" - csvWriter().open(testFileName) { - writeRow(row) - } - val actual = readTestFile() - actual shouldBe expected - } - "write java.time.LocalDate and java.time.LocalDateTime types" { - val row = listOf( - LocalDate.of(2019, 8, 19), - LocalDateTime.of(2020, 9, 20, 14, 32, 21) - ) - val expected = "2019-08-19,2020-09-20T14:32:21\r\n" - csvWriter().open(testFileName) { - writeRow(row) - } - val actual = readTestFile() - actual shouldBe expected - } - "write row from variable arguments" { - - val date1 = LocalDate.of(2019, 8, 19) - val date2 = LocalDateTime.of(2020, 9, 20, 14, 32, 21) - - val expected = "a,b,c\r\n" + - "d,e,f\r\n" + - "1,2,3\r\n" + - "2019-08-19,2020-09-20T14:32:21\r\n" - csvWriter().open(testFileName) { - writeRow("a", "b", "c") - writeRow("d", "e", "f") - writeRow(1, 2, 3) - writeRow(date1, date2) - } - val actual = readTestFile() - actual shouldBe expected - } - } - "writeAll method" should { - "write Sequence data" { - val rows = listOf(listOf("a", "b", "c"), listOf("d", "e", "f")).asSequence() - val expected = "a,b,c\r\nd,e,f\r\n" - csvWriter().open(testFileName) { - writeRows(rows) - } - val actual = readTestFile() - actual shouldBe expected - } - "write escaped field when a field contains quoteChar in it" { - val rows = listOf(listOf("a", "\"b", "c"), listOf("d", "e", "f\"")) - val expected = "a,\"\"\"b\",c\r\nd,e,\"f\"\"\"\r\n" - csvWriter().open(testFileName) { - writeRows(rows) - } - val actual = readTestFile() - actual shouldBe expected - } - "write escaped field when a field contains delimiter in it" { - val rows = listOf(listOf("a", ",b", "c"), listOf("d", "e", "f,")) - val expected = "a,\",b\",c\r\nd,e,\"f,\"\r\n" - csvWriter().open(testFileName) { - writeRows(rows) - } - val actual = readTestFile() - actual shouldBe expected - } - "write quoted field when a field contains cr or lf in it" { - val rows = listOf(listOf("a", "\nb", "c"), listOf("d", "e", "f\r\n")) - val expected = "a,\"\nb\",c\r\nd,e,\"f\r\n\"\r\n" - csvWriter().open(testFileName) { - writeRows(rows) - } - val actual = readTestFile() - actual shouldBe expected - } - "write no line terminator when row is empty for rows from list" { - val rows = listOf(listOf("a", "b", "c"), listOf(), listOf("d", "e", "f")) - val expected = "a,b,c\r\nd,e,f\r\n" - csvWriter().open(testFileName) { - writeRows(rows) - } - val actual = readTestFile() - actual shouldBe expected - } - "write no line terminator when row is empty for rows from sequence" { - val rows = listOf(listOf("a", "b", "c"), listOf(), listOf("d", "e", "f")).asSequence() - val expected = "a,b,c\r\nd,e,f\r\n" - csvWriter().open(testFileName) { - writeRows(rows) - } - val actual = readTestFile() - actual shouldBe expected - } - } - "close method" should { - "throw Exception when stream is already closed" { - val row = listOf("a", "b") - shouldThrow { - csvWriter().open(testFileName) { - close() - writeRow(row) - } - } - } - } - "flush method" should { - "flush stream" { - val row = listOf("a", "b") - csvWriter().open(testFileName) { - writeRow(row) - flush() - val actual = readTestFile() - actual shouldBe "a,b\r\n" - } - } - } - "suspend writeRow method" should { - "suspend write any primitive types" { - val row = listOf("String", 'C', 1, 2L, 3.45, true, null) - val expected = "String,C,1,2,3.45,true,\r\n" - csvWriter().openAsync(testFileName) { - writeRow(row) - } - val actual = readTestFile() - actual shouldBe expected - } - "suspend write row from variable arguments" { - - val date1 = LocalDate.of(2019, 8, 19) - val date2 = LocalDateTime.of(2020, 9, 20, 14, 32, 21) - - val expected = "a,b,c\r\n" + - "d,e,f\r\n" + - "1,2,3\r\n" + - "2019-08-19,2020-09-20T14:32:21\r\n" - csvWriter().openAsync(testFileName) { - writeRow("a", "b", "c") - writeRow("d", "e", "f") - writeRow(1, 2, 3) - writeRow(date1, date2) - } - val actual = readTestFile() - actual shouldBe expected - } - "suspend write all Sequence data" { - val rows = listOf(listOf("a", "b", "c"), listOf("d", "e", "f")).asSequence() - val expected = "a,b,c\r\nd,e,f\r\n" - csvWriter().openAsync(testFileName) { - writeRows(rows) - } - val actual = readTestFile() - actual shouldBe expected - } - } - "suspend close method" should { - "throw Exception when stream is already closed" { - val row = listOf("a", "b") - shouldThrow { - csvWriter().openAsync(testFileName) { - close() - writeRow(row) - } - } - } - } - "suspend flush method" should { - "flush stream" { - val row = listOf("a", "b") - csvWriter().openAsync(testFileName) { - writeRow(row) - flush() - val actual = readTestFile() - actual shouldBe "a,b\r\n" - } - } - } - "validate suspend test as flow" should { - "execute line" { - val rows = listOf(listOf("a", "b", "c"), listOf("d", "e", "f")).asSequence() - val expected = "a,b,c\r\nd,e,f\r\n" - csvWriter().openAsync(testFileName) { - delay(100) - rows.forEach { - delay(100) - writeRow(it) - } - } - val actual = readTestFile() - actual shouldBe expected - } - } - -}) diff --git a/src/jvmTest/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvReadWriteCompatibilityTest.kt b/src/jvmTest/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvReadWriteCompatibilityTest.kt deleted file mode 100644 index 4809de2..0000000 --- a/src/jvmTest/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvReadWriteCompatibilityTest.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.github.doyaaaaaken.kotlincsv.client - -import com.github.doyaaaaaken.kotlincsv.dsl.csvReader -import com.github.doyaaaaaken.kotlincsv.dsl.csvWriter -import io.kotest.core.spec.style.StringSpec -import io.kotest.matchers.shouldBe -import java.io.File - - -class CsvReadWriteCompatibilityTest : StringSpec({ - - val testFileName = "compatibility-test.csv" - - afterTest { File(testFileName).delete() } - - "CSVReader and CSVWriter are compatible" { - val data = listOf( - listOf("a", "bb", "ccc"), - listOf("d", "ee", "fff") - ) - csvWriter().open(testFileName) { - writeRows(data) - } - val actual = csvReader().readAll(File(testFileName)) - actual shouldBe data - } -}) diff --git a/src/jvmTest/kotlin/com/github/doyaaaaaken/kotlincsv/client/StringReaderTest.kt b/src/jvmTest/kotlin/com/github/doyaaaaaken/kotlincsv/client/StringReaderTest.kt deleted file mode 100644 index 93f55da..0000000 --- a/src/jvmTest/kotlin/com/github/doyaaaaaken/kotlincsv/client/StringReaderTest.kt +++ /dev/null @@ -1,161 +0,0 @@ -package com.github.doyaaaaaken.kotlincsv.client - -import com.github.doyaaaaaken.kotlincsv.dsl.context.CsvReaderContext -import com.github.doyaaaaaken.kotlincsv.util.CSVFieldNumDifferentException -import com.github.doyaaaaaken.kotlincsv.util.CSVParseFormatException -import com.github.doyaaaaaken.kotlincsv.util.MalformedCSVException -import com.github.doyaaaaaken.kotlincsv.util.logger.LoggerNop -import io.kotest.assertions.assertSoftly -import io.kotest.assertions.throwables.shouldThrow -import io.kotest.core.spec.style.WordSpec -import io.kotest.matchers.shouldBe - -class StringReaderTest : WordSpec({ - "readAll method (with String argument)" should { - "read simple csv" { - val result = readAll( - """a,b,c - |d,e,f - """.trimMargin() - ) - result shouldBe listOf(listOf("a", "b", "c"), listOf("d", "e", "f")) - } - "read csv with line separator" { - val result = readAll( - """a,b,c,"x","y - | hoge" - |d,e,f,g,h - """.trimMargin() - ) - result shouldBe listOf( - listOf( - "a", "b", "c", "x", """y - | hoge""".trimMargin() - ), listOf("d", "e", "f", "g", "h") - ) - } - "get failed rowNum and colIndex when exception happened on parsing CSV" { - val ex1 = shouldThrow { - readAll("a,\"\"failed") - } - val ex2 = shouldThrow { - readAll("a,b\nc,\"\"failed") - } - val ex3 = shouldThrow { - readAll("a,\"b\nb\"\nc,\"\"failed") - } - - assertSoftly { - ex1.rowNum shouldBe 1 - ex1.colIndex shouldBe 4 - ex1.char shouldBe 'f' - - ex2.rowNum shouldBe 2 - ex2.colIndex shouldBe 4 - ex2.char shouldBe 'f' - - ex3.rowNum shouldBe 3 - ex3.colIndex shouldBe 4 - ex3.char shouldBe 'f' - } - } - } - - "readAll method" should { - "read simple csv" { - val result = readAll(readTestDataFile("simple.csv")) - result shouldBe listOf(listOf("a", "b", "c"), listOf("d", "e", "f")) - } - "read csv with empty field" { - val result = readAll(readTestDataFile("empty-fields.csv")) - result shouldBe listOf(listOf("a", "", "b", "", "c", ""), listOf("d", "", "e", "", "f", "")) - } - "read csv with escaped field" { - val result = readAll(readTestDataFile("escape.csv")) - result shouldBe listOf(listOf("a", "b", "c"), listOf("d", "\"e", "f")) - } - "read csv with line breaks enclosed in double quotes" { - val result = readAll(readTestDataFile("line-breaks.csv")) - result shouldBe listOf(listOf("a", "b\nb", "c"), listOf("\nd", "e", "f")) - } - //refs https://github.com/tototoshi/scala-csv/issues/22 - "read csv with \u2028 field" { - val result = readAll(readTestDataFile("unicode2028.csv")) - result shouldBe listOf(listOf("\u2028")) - } - "throw exception when reading malformed csv" { - shouldThrow { - readAll(readTestDataFile("malformed.csv")) - } - } - "throw exception when reading csv with different fields num on each row" { - val ex = shouldThrow { - readAll(readTestDataFile("different-fields-num.csv")) - } - assertSoftly { - ex.fieldNum shouldBe 3 - ex.fieldNumOnFailedRow shouldBe 2 - ex.csvRowNum shouldBe 2 - } - } - } - - "readAllWithHeader method" should { - val expected = listOf( - mapOf("h1" to "a", "h2" to "b", "h3" to "c"), - mapOf("h1" to "d", "h2" to "e", "h3" to "f") - ) - - "read simple csv file" { - val file = readTestDataFile("with-header.csv") - val result = readAllWithHeader(file) - result shouldBe expected - } - - "read from String" { - val data = """h1,h2,h3 - |a,b,c - |d,e,f - """.trimMargin() - val result = readAllWithHeader(data) - result shouldBe expected - } - - "read from String containing line break" { - val data = """h1,"h - |2",h3 - |a,b,c - """.trimMargin() - val result = readAllWithHeader(data) - val h2 = """h - |2""".trimMargin() - result shouldBe listOf(mapOf("h1" to "a", h2 to "b", "h3" to "c")) - } - "number of fields in a row has to be based on the header #82" { - val data = "1,2,3\na,b\nx,y,z" - - val exception = shouldThrow { - readAllWithHeader(data) - } - exception.fieldNum shouldBe 3 - } - } -}) - -private fun readTestDataFile(fileName: String): String { - return java.io.File("src/jvmTest/resources/testdata/csv/$fileName").readText() -} - -/** - * read csv data as String, and convert into List> - */ -private fun readAll(data: String): List> { - return CsvFileReader(CsvReaderContext(), StringReaderImpl(data), LoggerNop).readAllAsSequence().toList() -} - -/** - * read csv data with header, and convert into List> - */ -private fun readAllWithHeader(data: String): List> { - return CsvFileReader(CsvReaderContext(), StringReaderImpl(data), LoggerNop).readAllWithHeaderAsSequence().toList() -} diff --git a/src/jvmTest/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvReaderTest.kt b/src/jvmTest/kotlin/com/jsoizo/kotlincsv/client/CsvReaderTest.kt similarity index 80% rename from src/jvmTest/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvReaderTest.kt rename to src/jvmTest/kotlin/com/jsoizo/kotlincsv/client/CsvReaderTest.kt index df92f47..a566a40 100644 --- a/src/jvmTest/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvReaderTest.kt +++ b/src/jvmTest/kotlin/com/jsoizo/kotlincsv/client/CsvReaderTest.kt @@ -1,46 +1,27 @@ -package com.github.doyaaaaaken.kotlincsv.client - -import com.github.doyaaaaaken.kotlincsv.dsl.context.CsvReaderContext -import com.github.doyaaaaaken.kotlincsv.dsl.context.ExcessFieldsRowBehaviour -import com.github.doyaaaaaken.kotlincsv.dsl.context.InsufficientFieldsRowBehaviour -import com.github.doyaaaaaken.kotlincsv.dsl.csvReader -import com.github.doyaaaaaken.kotlincsv.util.CSVFieldNumDifferentException -import com.github.doyaaaaaken.kotlincsv.util.CSVParseFormatException -import com.github.doyaaaaaken.kotlincsv.util.Const -import com.github.doyaaaaaken.kotlincsv.util.MalformedCSVException +package com.jsoizo.kotlincsv.client + +import com.jsoizo.kotlincsv.dsl.context.ExcessFieldsRowBehaviour +import com.jsoizo.kotlincsv.dsl.context.InsufficientFieldsRowBehaviour +import com.jsoizo.kotlincsv.dsl.csvReader +import com.jsoizo.kotlincsv.util.CSVFieldNumDifferentException +import com.jsoizo.kotlincsv.util.CSVParseFormatException +import com.jsoizo.kotlincsv.util.MalformedCSVException import io.kotest.assertions.assertSoftly import io.kotest.assertions.throwables.shouldThrow -import io.kotest.core.spec.style.WordSpec +import io.kotest.core.spec.style.StringSpec +import io.kotest.matchers.should import io.kotest.matchers.shouldBe import kotlinx.coroutines.flow.asFlow -import kotlinx.coroutines.flow.collect -import java.io.File - -class CsvReaderTest : WordSpec({ - "CsvReader class constructor" should { - "be created with no argument" { - val reader = CsvReader() - reader.charset shouldBe Const.defaultCharset - } - "be created with CsvReaderContext argument" { - val context = CsvReaderContext().apply { - charset = Charsets.ISO_8859_1.name() - quoteChar = '\'' - delimiter = '\t' - escapeChar = '"' - skipEmptyLine = true - } - val reader = CsvReader(context) - assertSoftly { - reader.charset shouldBe Charsets.ISO_8859_1.name() - reader.quoteChar shouldBe '\'' - reader.delimiter shouldBe '\t' - reader.escapeChar shouldBe '"' - reader.skipEmptyLine shouldBe true - } - } - } - +import kotlinx.io.buffered +import kotlinx.io.files.Path +import kotlinx.io.files.SystemFileSystem +import kotlinx.io.readString + +/** + * This class is not in commonTest because reading Path(s) from files is not supported on all + * platforms. + */ +class CsvReaderTest : StringSpec({ "readAll method (with String argument)" should { "read simple csv" { val result = csvReader().readAll( @@ -91,18 +72,13 @@ class CsvReaderTest : WordSpec({ } } } - - "readAll method (with InputStream argument)" should { + "readAll method (with Path argument)" should { "read simple csv" { - val file = readTestDataFile("simple.csv") - val result = csvReader().readAll(file.inputStream()) + val result = csvReader().readAll(readTestDataFile("simple.csv")) result shouldBe listOf(listOf("a", "b", "c"), listOf("d", "e", "f")) } - } - - "readAll method (with File argument)" should { - "read simple csv" { - val result = csvReader().readAll(readTestDataFile("simple.csv")) + "read simple csv as receiver" { + val result = readTestDataFile("simple.csv").csvReadAll() result shouldBe listOf(listOf("a", "b", "c"), listOf("d", "e", "f")) } "read tsv file" { @@ -325,7 +301,29 @@ class CsvReaderTest : WordSpec({ val result = csvReader().readAllWithHeader(file) result shouldBe expected } - + "read simple csv file as receiver" { + val file = readTestDataFile("with-header.csv") + val result = file.csvReadAllWithHeader() + result shouldBe expected + } + "read simple csv as Source" { + val src = SystemFileSystem.source(readTestDataFile("with-header.csv")).buffered() + src.use { + CsvReaderImpl().readAllWithHeader(it) shouldBe expected + } + } + "read simple csv as String receiver" { + val src = SystemFileSystem.source(readTestDataFile("with-header.csv")).buffered() + src.use { + it.readString().csvReadAllWithHeader() shouldBe expected + } + } + "read simple csv as Source receiver" { + val src = SystemFileSystem.source(readTestDataFile("with-header.csv")).buffered() + src.use { + it.csvReadAllWithHeader() shouldBe expected + } + } "throw on duplicated headers" { val file = readTestDataFile("with-duplicate-header.csv") shouldThrow { csvReader().readAllWithHeader(file) } @@ -356,12 +354,6 @@ class CsvReaderTest : WordSpec({ result shouldBe expected } - "read from InputStream" { - val file = readTestDataFile("with-header.csv") - val result = csvReader().readAllWithHeader(file.inputStream()) - result shouldBe expected - } - "read from String containing line break" { val data = """h1,"h |2",h3 @@ -382,53 +374,22 @@ class CsvReaderTest : WordSpec({ } } - "open method (with fileName argument)" should { - val rows = csvReader().open("src/jvmTest/resources/testdata/csv/simple.csv") { - val row1 = readNext() - val row2 = readNext() - listOf(row1, row2) + "open method (with Path argument)" should { + val rows = csvReader().open(readTestDataFile("simple.csv")) { + readAllAsSequence().toList() } rows shouldBe listOf(listOf("a", "b", "c"), listOf("d", "e", "f")) } - "open method (with InputStream argument)" should { - val file = readTestDataFile("simple.csv") - val rows = csvReader().open(file.inputStream()) { - val row1 = readNext() - val row2 = readNext() - listOf(row1, row2) - } - rows shouldBe listOf(listOf("a", "b", "c"), listOf("d", "e", "f")) - } "execute as suspending function" should { - "open suspending method (with fileName argument)" { - val rows = csvReader().openAsync("src/jvmTest/resources/testdata/csv/simple.csv") { - val row1 = readNext() - val row2 = readNext() - listOf(row1, row2) - } - rows shouldBe listOf(listOf("a", "b", "c"), listOf("d", "e", "f")) - } - "open suspending method (with file argument)" { - val file = readTestDataFile("simple.csv") - val rows = csvReader().openAsync(file) { - val row1 = readNext() - val row2 = readNext() - listOf(row1, row2) - } - rows shouldBe listOf(listOf("a", "b", "c"), listOf("d", "e", "f")) - } - "open suspending method (with InputStream argument)" { - val fileStream = readTestDataFile("simple.csv").inputStream() - val rows = csvReader().openAsync(fileStream) { - val row1 = readNext() - val row2 = readNext() - listOf(row1, row2) + "open suspending method (with Path argument)" { + val rows = csvReader().openAsync(readTestDataFile("simple.csv")) { + readAllAsSequence().toList() } rows shouldBe listOf(listOf("a", "b", "c"), listOf("d", "e", "f")) } "validate test as flow" { - val fileStream = readTestDataFile("simple.csv").inputStream() + val fileStream = readTestDataFile("simple.csv") val rows = mutableListOf>() csvReader().openAsync(fileStream) { readAllAsSequence().asFlow().collect { @@ -438,8 +399,28 @@ class CsvReaderTest : WordSpec({ rows shouldBe listOf(listOf("a", "b", "c"), listOf("d", "e", "f")) } } + + "csvReadAllAsSequence method (with Source receiver)" should { + val rows = SystemFileSystem.source(readTestDataFile("simple.csv")).buffered().use{ + it.csvReadAllAsSequence().toList() + } + rows shouldBe listOf(listOf("a", "b", "c"), listOf("d", "e", "f")) + } + + "csvReadAllAsSequenceWithHeader (with Source receiver)" should { + val expected = listOf( + mapOf("h1" to "a", "h2" to "b", "h3" to "c"), + mapOf("h1" to "d", "h2" to "e", "h3" to "f") + ) + val result = SystemFileSystem.source(readTestDataFile("with-header.csv")).buffered().use{ + it.csvReadAllWithHeaderAsSequence().toList() + } + result shouldBe expected + } }) -private fun readTestDataFile(fileName: String): File { - return File("src/jvmTest/resources/testdata/csv/$fileName") +private fun readTestDataFile(fileName: String): Path { + val path = Path("src", "jvmTest", "resources", "testdata", "csv", fileName) + println("Made path: $path") + return path } diff --git a/src/jvmTest/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvWriterTest.kt b/src/jvmTest/kotlin/com/jsoizo/kotlincsv/client/CsvWriterTest.kt similarity index 65% rename from src/jvmTest/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvWriterTest.kt rename to src/jvmTest/kotlin/com/jsoizo/kotlincsv/client/CsvWriterTest.kt index 511dfcf..0cdaf7b 100644 --- a/src/jvmTest/kotlin/com/github/doyaaaaaken/kotlincsv/client/CsvWriterTest.kt +++ b/src/jvmTest/kotlin/com/jsoizo/kotlincsv/client/CsvWriterTest.kt @@ -1,34 +1,42 @@ -package com.github.doyaaaaaken.kotlincsv.client +package com.jsoizo.kotlincsv.client -import com.github.doyaaaaaken.kotlincsv.dsl.context.CsvWriterContext -import com.github.doyaaaaaken.kotlincsv.dsl.context.WriteQuoteMode -import com.github.doyaaaaaken.kotlincsv.dsl.csvWriter -import com.github.doyaaaaaken.kotlincsv.util.Const +import com.jsoizo.kotlincsv.dsl.context.CsvWriterContext +import com.jsoizo.kotlincsv.dsl.context.WriteQuoteMode +import com.jsoizo.kotlincsv.dsl.csvWriter import io.kotest.assertions.assertSoftly import io.kotest.core.spec.style.WordSpec import io.kotest.matchers.shouldBe +import kotlinx.coroutines.delay +import kotlinx.io.Buffer +import kotlinx.io.files.Path +import kotlinx.io.files.SystemFileSystem +import kotlinx.io.readString import java.io.File import java.nio.charset.Charset - +import java.time.LocalDate +import java.time.LocalDateTime class CsvWriterTest : WordSpec({ val testFileName = "test.csv" - afterTest { File(testFileName).delete() } + afterTest { + // afterTest getting called more than once and kotlinx-io throws exception if trying to + // delete non-existent file + Path(testFileName).also { + if(SystemFileSystem.exists(it)) { + SystemFileSystem.delete(it) + } + } + } fun readTestFile(charset: Charset = Charsets.UTF_8): String { return File(testFileName).readText(charset) } "CsvWriter class constructor" should { - "be created with no argument" { - val writer = CsvWriter() - writer.charset shouldBe Const.defaultCharset - } "be created with CsvWriterContext argument" { val context = CsvWriterContext().apply { - charset = Charsets.ISO_8859_1.name() delimiter = '\t' nullCode = "NULL" lineTerminator = "\n" @@ -39,9 +47,8 @@ class CsvWriterTest : WordSpec({ mode = WriteQuoteMode.ALL } } - val writer = CsvWriter(context) + val writer = CsvWriterImpl(context) assertSoftly { - writer.charset shouldBe Charsets.ISO_8859_1.name() writer.delimiter shouldBe '\t' writer.nullCode shouldBe "NULL" writer.lineTerminator shouldBe "\n" @@ -59,26 +66,27 @@ class CsvWriterTest : WordSpec({ val expected = "a,b,\r\nd,2,1.0\r\n" "write simple csv data into file with writing each rows" { - csvWriter().open(testFileName) { + val buffer = Buffer() + csvWriter().open(buffer) { writeRow(row1) writeRow(row2) } - val actual = readTestFile() + val actual = buffer.readString() actual shouldBe expected } "write simple csv data into file with writing all at one time" { - csvWriter().open(testFileName) { writeRows(listOf(row1, row2)) } + csvWriter().open(Path(testFileName)) { writeRows(listOf(row1, row2)) } val actual = readTestFile() actual shouldBe expected } "write simple csv data to the tail of existing file with append = true" { val writer = csvWriter() - writer.open(File(testFileName), true) { + writer.open(Path(testFileName), true) { writeRows(listOf(row1, row2)) } - writer.open(File(testFileName), true) { + writer.open(Path(testFileName), true) { writeRows(listOf(row1, row2)) } val actual = readTestFile() @@ -87,10 +95,10 @@ class CsvWriterTest : WordSpec({ "overwrite simple csv data with append = false" { val writer = csvWriter() - writer.open(File(testFileName), false) { + writer.open(Path(testFileName), false) { writeRows(listOf(row2, row2, row2)) } - writer.open(File(testFileName), false) { + writer.open(Path(testFileName), false) { writeRows(listOf(row1, row2)) } val actual = readTestFile() @@ -98,65 +106,50 @@ class CsvWriterTest : WordSpec({ } } - "writeAsString method" should { - val row1 = listOf("a", "b", null) - val row2 = listOf("d", "2", "1.0") - val expected = "a,b,\r\nd,2,1.0\r\n" - - "write simple csv data to String" { - val actual = csvWriter().writeAsString { - writeRow(row1) - writeRow(row2) - } - actual shouldBe expected - } - } - "writeAll method without calling `open` method" should { val rows = listOf(listOf("a", "b", "c"), listOf("d", "e", "f")) val expected = "a,b,c\r\nd,e,f\r\n" "write data with target file name" { - csvWriter().writeAll(rows, testFileName) + csvWriter().writeAll(rows, Path(testFileName)) val actual = readTestFile() actual shouldBe expected } - "write data with target file (java.io.File)" { - csvWriter().writeAll(rows, File(testFileName)) - val actual = readTestFile() + "write data to Sink" { + val buffer = Buffer() + csvWriter().writeAll(rows, buffer) + val actual = buffer.readString() actual shouldBe expected } + } - "write data with target output stream (java.io.OutputStream)" { - csvWriter().writeAll(rows, File(testFileName).outputStream()) + "writeAllAsync method without calling `open` method" should { + val rows = listOf(listOf("a", "b", "c"), listOf("d", "e", "f")) + val expected = "a,b,c\r\nd,e,f\r\n" + + "write data with target file name" { + csvWriter().writeAllAsync(rows, Path(testFileName)) val actual = readTestFile() actual shouldBe expected } - "write data to String" { - val actual = csvWriter().writeAllAsString(rows) + "write data to Sink" { + val buffer = Buffer() + csvWriter().writeAllAsync(rows, buffer) + val actual = buffer.readString() actual shouldBe expected } } "Customized CsvWriter" should { - "write csv with SJIS charset" { - csvWriter { - charset = "SJIS" - }.open(File(testFileName)) { - writeRows(listOf(listOf("あ", "い"))) - } - val actual = readTestFile(Charset.forName("SJIS")) - actual shouldBe "あ,い\r\n" - } "write csv with '|' delimiter" { val row1 = listOf("a", "b") val row2 = listOf("c", "d") val expected = "a|b\r\nc|d\r\n" csvWriter { delimiter = '|' - }.open(File(testFileName)) { + }.open(Path(testFileName)) { writeRows(listOf(row1, row2)) } val actual = readTestFile() @@ -166,7 +159,7 @@ class CsvWriterTest : WordSpec({ val row = listOf(null, null) csvWriter { nullCode = "NULL" - }.open(testFileName) { + }.open(Path(testFileName)) { writeRow(row) } val actual = readTestFile() @@ -178,7 +171,7 @@ class CsvWriterTest : WordSpec({ val expected = "a,b\nc,d\n" csvWriter { lineTerminator = "\n" - }.open(File(testFileName)) { + }.open(Path(testFileName)) { writeRows(listOf(row1, row2)) } val actual = readTestFile() @@ -192,7 +185,7 @@ class CsvWriterTest : WordSpec({ quote { mode = WriteQuoteMode.ALL } - }.open(File(testFileName)) { + }.open(Path(testFileName)) { writeRows(listOf(row1, row2)) } val actual = readTestFile() @@ -206,7 +199,7 @@ class CsvWriterTest : WordSpec({ quote { mode = WriteQuoteMode.NON_NUMERIC } - }.open(File(testFileName)) { + }.open(Path(testFileName)) { writeRows(listOf(row1, row2)) } val actual = readTestFile() @@ -220,7 +213,7 @@ class CsvWriterTest : WordSpec({ quote { char = '\'' } - }.open(File(testFileName)) { + }.open(Path(testFileName)) { writeRows(listOf(row1, row2)) } val actual = readTestFile() @@ -234,7 +227,7 @@ class CsvWriterTest : WordSpec({ mode = WriteQuoteMode.ALL char = '_' } - }.writeAll(rows, testFileName) + }.writeAll(rows, Path(testFileName)) val actual = readTestFile() actual shouldBe expected } @@ -245,7 +238,7 @@ class CsvWriterTest : WordSpec({ csvWriter { lineTerminator = "\n" outputLastLineTerminator = false - }.open(File(testFileName)) { + }.open(Path(testFileName)) { writeRows(listOf(row1, row2)) } val actual = readTestFile() @@ -258,7 +251,7 @@ class CsvWriterTest : WordSpec({ csvWriter { lineTerminator = "\n" outputLastLineTerminator = true - }.open(File(testFileName)) { + }.open(Path(testFileName)) { writeRows(listOf(row1, row2)) } val actual = readTestFile() @@ -270,7 +263,7 @@ class CsvWriterTest : WordSpec({ val expected = "a,b\r\nc,d" csvWriter { outputLastLineTerminator = false - }.open(File(testFileName)) { + }.open(Path(testFileName)) { writeRows(listOf(row1, row2)) } val actual = readTestFile() @@ -282,7 +275,7 @@ class CsvWriterTest : WordSpec({ val expected = "\uFEFFa,b\r\nc,d\r\n" csvWriter { prependBOM = true - }.open(File(testFileName)) { + }.open(Path(testFileName)) { writeRows(listOf(row1, row2)) } val actual = readTestFile() @@ -298,7 +291,7 @@ class CsvWriterTest : WordSpec({ val expected = "a,b\r\nc,d\r\ne,f\r\ng,h\r\n1,2\r\n3,4" csvWriter { outputLastLineTerminator = false - }.open(File(testFileName)) { + }.open(Path(testFileName)) { writeRow(row1) writeRows(listOf(row2, row3)) writeRow(row4) @@ -316,7 +309,7 @@ class CsvWriterTest : WordSpec({ "get raw writer from fileName string and can use it" { @OptIn(KotlinCsvExperimental::class) - val writer = csvWriter().openAndGetRawWriter(testFileName) + val writer = CsvWriterImpl().openAndGetRawWriter(testFileName) writer.writeRow(row1) writer.writeRow(row2) writer.close() @@ -324,27 +317,77 @@ class CsvWriterTest : WordSpec({ val actual = readTestFile() actual shouldBe expected } + } - "get raw writer from java.io.File and can use it" { - @OptIn(KotlinCsvExperimental::class) - val writer = csvWriter().openAndGetRawWriter(File(testFileName)) - writer.writeRow(row1) - writer.writeRow(row2) - writer.close() - + "suspend writeRow method" should { + "suspend write any primitive types to Path" { + val row = listOf("String", 'C', 1, 2L, 3.45, true, null) + val expected = "String,C,1,2,3.45,true,\r\n" + csvWriter().openAsync(Path(testFileName)) { + writeRow(row) + } val actual = readTestFile() actual shouldBe expected } + "suspend write any primitive types to Sink" { + val row = listOf("String", 'C', 1, 2L, 3.45, true, null) + val expected = "String,C,1,2,3.45,true,\r\n" + val buffer = Buffer() + csvWriter().openAsync(buffer) { + writeRow(row) + } + val actual = buffer.readString() + actual shouldBe expected + } + "suspend write row from variable arguments" { + val date1 = LocalDate.of(2019, 8, 19) + val date2 = LocalDateTime.of(2020, 9, 20, 14, 32, 21) - "get raw writer from OutputStream and can use it" { - val ops = File(testFileName).outputStream() - - @OptIn(KotlinCsvExperimental::class) - val writer = csvWriter().openAndGetRawWriter(ops) - writer.writeRow(row1) - writer.writeRow(row2) - writer.close() - + val expected = "a,b,c\r\n" + + "d,e,f\r\n" + + "1,2,3\r\n" + + "2019-08-19,2020-09-20T14:32:21\r\n" + csvWriter().openAsync(Path(testFileName)) { + writeRow("a", "b", "c") + writeRow("d", "e", "f") + writeRow(1, 2, 3) + writeRow(date1, date2) + } + val actual = readTestFile() + actual shouldBe expected + } + "suspend write all Sequence data" { + val rows = listOf(listOf("a", "b", "c"), listOf("d", "e", "f")).asSequence() + val expected = "a,b,c\r\nd,e,f\r\n" + csvWriter().openAsync(Path(testFileName)) { + writeRows(rows) + } + val actual = readTestFile() + actual shouldBe expected + } + } + "suspend flush method" should { + "flush stream" { + val row = listOf("a", "b") + csvWriter().openAsync(Path(testFileName)) { + writeRow(row) + flush() + val actual = readTestFile() + actual shouldBe "a,b\r\n" + } + } + } + "validate suspend test as flow" should { + "execute line" { + val rows = listOf(listOf("a", "b", "c"), listOf("d", "e", "f")).asSequence() + val expected = "a,b,c\r\nd,e,f\r\n" + csvWriter().openAsync(Path(testFileName)) { + delay(100) + rows.forEach { + delay(100) + writeRow(it) + } + } val actual = readTestFile() actual shouldBe expected } diff --git a/src/jvmTest/kotlin/com/github/doyaaaaaken/kotlincsv/dsl/CsvReaderDslTest.kt b/src/jvmTest/kotlin/com/jsoizo/kotlincsv/dsl/CsvReaderDslTest.kt similarity index 76% rename from src/jvmTest/kotlin/com/github/doyaaaaaken/kotlincsv/dsl/CsvReaderDslTest.kt rename to src/jvmTest/kotlin/com/jsoizo/kotlincsv/dsl/CsvReaderDslTest.kt index c99ae7e..c9b08cf 100644 --- a/src/jvmTest/kotlin/com/github/doyaaaaaken/kotlincsv/dsl/CsvReaderDslTest.kt +++ b/src/jvmTest/kotlin/com/jsoizo/kotlincsv/dsl/CsvReaderDslTest.kt @@ -1,8 +1,8 @@ -package com.github.doyaaaaaken.kotlincsv.dsl +package com.jsoizo.kotlincsv.dsl -import com.github.doyaaaaaken.kotlincsv.client.CsvReader -import com.github.doyaaaaaken.kotlincsv.dsl.context.ExcessFieldsRowBehaviour -import com.github.doyaaaaaken.kotlincsv.dsl.context.InsufficientFieldsRowBehaviour +import com.jsoizo.kotlincsv.client.CsvReaderImpl +import com.jsoizo.kotlincsv.dsl.context.ExcessFieldsRowBehaviour +import com.jsoizo.kotlincsv.dsl.context.InsufficientFieldsRowBehaviour import io.kotest.assertions.assertSoftly import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe @@ -14,7 +14,7 @@ import io.kotest.matchers.types.shouldBeTypeOf class CsvReaderDslTest : StringSpec({ "csvReader method should work as global method with no argument" { val reader = csvReader() - reader.shouldBeTypeOf() + reader.shouldBeTypeOf() } "csvReader method should work as dsl" { val reader = csvReader { @@ -28,7 +28,6 @@ class CsvReaderDslTest : StringSpec({ excessFieldsRowBehaviour = ExcessFieldsRowBehaviour.IGNORE } assertSoftly { - reader.charset shouldBe Charsets.ISO_8859_1.name() reader.quoteChar shouldBe '\'' reader.delimiter shouldBe '\t' reader.skipEmptyLine shouldBe true diff --git a/src/jvmTest/kotlin/com/github/doyaaaaaken/kotlincsv/dsl/CsvWriterDslTest.kt b/src/jvmTest/kotlin/com/jsoizo/kotlincsv/dsl/CsvWriterDslTest.kt similarity index 76% rename from src/jvmTest/kotlin/com/github/doyaaaaaken/kotlincsv/dsl/CsvWriterDslTest.kt rename to src/jvmTest/kotlin/com/jsoizo/kotlincsv/dsl/CsvWriterDslTest.kt index bd2d51a..3363e9d 100644 --- a/src/jvmTest/kotlin/com/github/doyaaaaaken/kotlincsv/dsl/CsvWriterDslTest.kt +++ b/src/jvmTest/kotlin/com/jsoizo/kotlincsv/dsl/CsvWriterDslTest.kt @@ -1,7 +1,7 @@ -package com.github.doyaaaaaken.kotlincsv.dsl +package com.jsoizo.kotlincsv.dsl -import com.github.doyaaaaaken.kotlincsv.client.CsvWriter -import com.github.doyaaaaaken.kotlincsv.dsl.context.WriteQuoteMode +import com.jsoizo.kotlincsv.client.CsvWriterImpl +import com.jsoizo.kotlincsv.dsl.context.WriteQuoteMode import io.kotest.assertions.assertSoftly import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe @@ -13,11 +13,10 @@ import io.kotest.matchers.types.shouldBeTypeOf class CsvWriterDslTest : StringSpec({ "csvWriter method should work as global method with no argument" { val writer = csvWriter() - writer.shouldBeTypeOf() + writer.shouldBeTypeOf() } "csvWriter method should work as dsl" { val writer = csvWriter { - charset = Charsets.ISO_8859_1.name() delimiter = '\t' nullCode = "NULL" lineTerminator = "\n" @@ -29,7 +28,6 @@ class CsvWriterDslTest : StringSpec({ } } assertSoftly { - writer.charset shouldBe Charsets.ISO_8859_1.name() writer.delimiter shouldBe '\t' writer.nullCode shouldBe "NULL" writer.lineTerminator shouldBe "\n" diff --git a/src/jvmTest/kotlin/com/github/doyaaaaaken/kotlincsv/parser/CsvParserTest.kt b/src/jvmTest/kotlin/com/jsoizo/kotlincsv/parser/CsvParserTest.kt similarity index 95% rename from src/jvmTest/kotlin/com/github/doyaaaaaken/kotlincsv/parser/CsvParserTest.kt rename to src/jvmTest/kotlin/com/jsoizo/kotlincsv/parser/CsvParserTest.kt index 4ea8f0e..f78ad04 100644 --- a/src/jvmTest/kotlin/com/github/doyaaaaaken/kotlincsv/parser/CsvParserTest.kt +++ b/src/jvmTest/kotlin/com/jsoizo/kotlincsv/parser/CsvParserTest.kt @@ -1,6 +1,6 @@ -package com.github.doyaaaaaken.kotlincsv.parser +package com.jsoizo.kotlincsv.parser -import com.github.doyaaaaaken.kotlincsv.util.CSVParseFormatException +import com.jsoizo.kotlincsv.util.CSVParseFormatException import io.kotest.assertions.assertSoftly import io.kotest.assertions.throwables.shouldThrow import io.kotest.core.spec.style.WordSpec