module 2 lesson 1
This commit is contained in:
5
.gitignore
vendored
5
.gitignore
vendored
@ -7,4 +7,7 @@
|
||||
.idea
|
||||
|
||||
### Mac OS ###
|
||||
.DS_Store
|
||||
.DS_Store
|
||||
|
||||
### Kotlin ###
|
||||
.kotlin
|
||||
|
||||
32
build-plugin/build.gradle.kts
Normal file
32
build-plugin/build.gradle.kts
Normal file
@ -0,0 +1,32 @@
|
||||
plugins {
|
||||
`kotlin-dsl`
|
||||
}
|
||||
|
||||
gradlePlugin {
|
||||
plugins {
|
||||
register("build-jvm") {
|
||||
id = "build-jvm"
|
||||
implementationClass = "ru.otus.build.plugin.BuildPluginJvm"
|
||||
}
|
||||
register("build-kmp") {
|
||||
id = "build-kmp"
|
||||
implementationClass = "ru.otus.build.plugin.BuildPluginMultiplatform"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// enable Ktlint formatting
|
||||
// add("detektPlugins", libs.plugin.detektFormatting)
|
||||
|
||||
implementation(files(libs.javaClass.superclass.protectionDomain.codeSource.location))
|
||||
|
||||
implementation(libs.plugin.kotlin)
|
||||
// implementation(libs.plugin.dokka)
|
||||
implementation(libs.plugin.binaryCompatibilityValidator)
|
||||
// implementation(libs.plugin.mavenPublish)
|
||||
}
|
||||
1
build-plugin/gradle.properties
Normal file
1
build-plugin/gradle.properties
Normal file
@ -0,0 +1 @@
|
||||
kotlin.code.style=official
|
||||
9
build-plugin/settings.gradle.kts
Normal file
9
build-plugin/settings.gradle.kts
Normal file
@ -0,0 +1,9 @@
|
||||
rootProject.name = "backend-build"
|
||||
|
||||
dependencyResolutionManagement {
|
||||
versionCatalogs {
|
||||
create("libs") {
|
||||
from(files("../gradle/libs.versions.toml"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package ru.otus.build.plugin
|
||||
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.kotlin.dsl.repositories
|
||||
|
||||
@Suppress("unused")
|
||||
internal class BuildPluginJvm : Plugin<Project> {
|
||||
|
||||
override fun apply(project: Project) = with(project) {
|
||||
pluginManager.apply("org.jetbrains.kotlin.jvm")
|
||||
// pluginManager.apply(KotlinPlatformJvmPlugin::class.java)
|
||||
group = rootProject.group
|
||||
version = rootProject.version
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,62 @@
|
||||
package ru.otus.build.plugin
|
||||
|
||||
import org.gradle.accessors.dm.LibrariesForLibs
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.tasks.compile.JavaCompile
|
||||
import org.gradle.jvm.toolchain.JavaLanguageVersion
|
||||
import org.gradle.kotlin.dsl.configure
|
||||
import org.gradle.kotlin.dsl.repositories
|
||||
import org.gradle.kotlin.dsl.the
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
|
||||
|
||||
@Suppress("unused")
|
||||
internal class BuildPluginMultiplatform : Plugin<Project> {
|
||||
|
||||
override fun apply(project: Project) = with(project) {
|
||||
pluginManager.apply("org.jetbrains.kotlin.multiplatform")
|
||||
group = rootProject.group
|
||||
version = rootProject.version
|
||||
|
||||
plugins.withId("org.jetbrains.kotlin.multiplatform") {
|
||||
extensions.configure<KotlinMultiplatformExtension> {
|
||||
configureTargets(this@with)
|
||||
sourceSets.configureEach {
|
||||
languageSettings.apply {
|
||||
languageVersion = "1.9"
|
||||
progressiveMode = true
|
||||
optIn("kotlin.time.ExperimentalTime")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("LongMethod", "MagicNumber")
|
||||
private fun KotlinMultiplatformExtension.configureTargets(project: Project) {
|
||||
val libs = project.the<LibrariesForLibs>()
|
||||
jvmToolchain {
|
||||
languageVersion.set(JavaLanguageVersion.of(libs.versions.jvm.language.get()))
|
||||
// vendor.set(JvmVendorSpec.AZUL)
|
||||
}
|
||||
|
||||
jvm {
|
||||
compilations.configureEach {
|
||||
compilerOptions.configure {
|
||||
jvmTarget.set(JvmTarget.valueOf("JVM_${libs.versions.jvm.compiler.get()}"))
|
||||
}
|
||||
}
|
||||
}
|
||||
linuxX64()
|
||||
macosArm64()
|
||||
macosX64()
|
||||
project.tasks.withType(JavaCompile::class.java) {
|
||||
sourceCompatibility = libs.versions.jvm.language.get()
|
||||
targetCompatibility = libs.versions.jvm.compiler.get()
|
||||
}
|
||||
}
|
||||
@ -1,2 +1,2 @@
|
||||
kotlin.code.style=official
|
||||
kotlinVersion=2.0.20
|
||||
kotlinVersion=2.1.0
|
||||
|
||||
29
gradle/libs.versions.toml
Normal file
29
gradle/libs.versions.toml
Normal file
@ -0,0 +1,29 @@
|
||||
[versions]
|
||||
kotlin = "2.1.0"
|
||||
coroutines = "1.9.0"
|
||||
datetime = "0.6.1"
|
||||
okhhtp = "4.12.0"
|
||||
jackson-module = "2.18.2"
|
||||
slf4j = "2.0.9"
|
||||
logback-classic = "1.4.11"
|
||||
|
||||
# BASE
|
||||
jvm-compiler = "17"
|
||||
jvm-language = "21"
|
||||
|
||||
[plugins]
|
||||
jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
|
||||
multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
|
||||
java-gradle-plugin = { id = "java-gradle-plugin" }
|
||||
|
||||
[libraries]
|
||||
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
|
||||
kotlin-coroutines = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
|
||||
kotlin-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines" }
|
||||
kotlin-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "datetime" }
|
||||
kotlin-jackson-module = { module = "com.fasterxml.jackson.module:jackson-module-kotlin", version.ref = "jackson-module" }
|
||||
okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhhtp" }
|
||||
slf4j = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
|
||||
logback-classic = { module = "ch.qos.logback:logback-classic", version.ref = "logback-classic" }
|
||||
plugin-kotlin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
|
||||
plugin-binaryCompatibilityValidator = "org.jetbrains.kotlinx:binary-compatibility-validator:0.13.2"
|
||||
12
ok-lessons/build.gradle.kts
Normal file
12
ok-lessons/build.gradle.kts
Normal file
@ -0,0 +1,12 @@
|
||||
plugins {
|
||||
alias(libs.plugins.jvm) apply false
|
||||
alias(libs.plugins.multiplatform) apply false
|
||||
}
|
||||
|
||||
group = "ru.otus"
|
||||
version = "0.0.1"
|
||||
|
||||
subprojects {
|
||||
group = rootProject.group
|
||||
version = rootProject.version
|
||||
}
|
||||
3
ok-lessons/gradle.properties
Normal file
3
ok-lessons/gradle.properties
Normal file
@ -0,0 +1,3 @@
|
||||
kotlin.code.style=official
|
||||
kotlin.mpp.enableCInteropCommonization=true
|
||||
kotlin.native.ignoreDisabledTargets=true
|
||||
@ -1,5 +1,5 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
alias(libs.plugins.jvm)
|
||||
}
|
||||
|
||||
group = "ru.otus"
|
||||
17
ok-lessons/m1l2-basic/build.gradle.kts
Normal file
17
ok-lessons/m1l2-basic/build.gradle.kts
Normal file
@ -0,0 +1,17 @@
|
||||
plugins {
|
||||
alias(libs.plugins.jvm)
|
||||
}
|
||||
|
||||
group = "ru.otus"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation(kotlin("test"))
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
17
ok-lessons/m1l3-func/build.gradle.kts
Normal file
17
ok-lessons/m1l3-func/build.gradle.kts
Normal file
@ -0,0 +1,17 @@
|
||||
plugins {
|
||||
alias(libs.plugins.jvm)
|
||||
}
|
||||
|
||||
group = "ru.otus"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation(kotlin("test"))
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
20
ok-lessons/m1l3-func/src/main/kotlin/ru/otus/Main.kt
Normal file
20
ok-lessons/m1l3-func/src/main/kotlin/ru/otus/Main.kt
Normal file
@ -0,0 +1,20 @@
|
||||
package ru.otus
|
||||
|
||||
fun mapToFML(fml: Map<String, String>): String = "${fml["last"]} ${fml["first"]} ${fml["middle"]}"
|
||||
|
||||
fun mapToFL(fl: Map<String, String>): String = "${fl["last"]} ${fl["first"]}"
|
||||
|
||||
fun mapToF(f: Map<String, String>): String = "${f["first"]}"
|
||||
|
||||
fun mapListToNames(mapList: List<Map<String, String>>): List<String> {
|
||||
val result = mutableListOf<String>()
|
||||
mapList.forEach {
|
||||
when(it.keys) {
|
||||
setOf("first") -> result.add(mapToF(it))
|
||||
setOf("first", "last") -> result.add(mapToFL(it))
|
||||
setOf("first", "middle", "last") -> result.add(mapToFML(it))
|
||||
}
|
||||
}
|
||||
|
||||
return result.toList()
|
||||
}
|
||||
40
ok-lessons/m1l3-func/src/test/kotlin/ru/otus/HomeWorkFunc.kt
Normal file
40
ok-lessons/m1l3-func/src/test/kotlin/ru/otus/HomeWorkFunc.kt
Normal file
@ -0,0 +1,40 @@
|
||||
package ru.otus
|
||||
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
|
||||
/*
|
||||
* Реализовать функцию, которая преобразует список словарей строк в ФИО
|
||||
* Функцию сделать с использованием разных функций для разного числа составляющих имени
|
||||
* Итого, должно получиться 4 функции
|
||||
*
|
||||
* Для успешного решения задания, требуется раскомментировать тест, тест должен выполняться успешно
|
||||
* */
|
||||
class HomeWorkFunc {
|
||||
|
||||
@Test
|
||||
fun mapListToNamesTest() {
|
||||
val input = listOf(
|
||||
mapOf(
|
||||
"first" to "Иван",
|
||||
"middle" to "Васильевич",
|
||||
"last" to "Рюрикович",
|
||||
),
|
||||
mapOf(
|
||||
"first" to "Петька",
|
||||
),
|
||||
mapOf(
|
||||
"first" to "Сергей",
|
||||
"last" to "Королев",
|
||||
),
|
||||
)
|
||||
val expected = listOf(
|
||||
"Рюрикович Иван Васильевич",
|
||||
"Петька",
|
||||
"Королев Сергей",
|
||||
)
|
||||
|
||||
val res = mapListToNames(input)
|
||||
assertEquals(expected, res)
|
||||
}
|
||||
}
|
||||
17
ok-lessons/m1l4-oop/build.gradle.kts
Normal file
17
ok-lessons/m1l4-oop/build.gradle.kts
Normal file
@ -0,0 +1,17 @@
|
||||
plugins {
|
||||
alias(libs.plugins.jvm)
|
||||
}
|
||||
|
||||
group = "ru.otus"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation(kotlin("test"))
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
5
ok-lessons/m1l4-oop/src/main/kotlin/ru/otus/Figure.kt
Normal file
5
ok-lessons/m1l4-oop/src/main/kotlin/ru/otus/Figure.kt
Normal file
@ -0,0 +1,5 @@
|
||||
package ru.otus
|
||||
|
||||
interface Figure {
|
||||
fun area(): Int
|
||||
}
|
||||
3
ok-lessons/m1l4-oop/src/main/kotlin/ru/otus/Main.kt
Normal file
3
ok-lessons/m1l4-oop/src/main/kotlin/ru/otus/Main.kt
Normal file
@ -0,0 +1,3 @@
|
||||
package ru.otus
|
||||
|
||||
fun diffArea(a: Figure, b: Figure): Int = a.area() - b.area()
|
||||
24
ok-lessons/m1l4-oop/src/main/kotlin/ru/otus/Rectangle.kt
Normal file
24
ok-lessons/m1l4-oop/src/main/kotlin/ru/otus/Rectangle.kt
Normal file
@ -0,0 +1,24 @@
|
||||
package ru.otus
|
||||
|
||||
class Rectangle(val width: Int, val height: Int): Figure {
|
||||
override fun area() = width * height
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other?.javaClass != javaClass) {
|
||||
return false
|
||||
}
|
||||
|
||||
other as Rectangle
|
||||
|
||||
if (width != other.width) return false
|
||||
if (height != other.height) return false
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return javaClass.hashCode()
|
||||
}
|
||||
|
||||
override fun toString() = "Rectangle(${width}x$height)"
|
||||
}
|
||||
21
ok-lessons/m1l4-oop/src/main/kotlin/ru/otus/Square.kt
Normal file
21
ok-lessons/m1l4-oop/src/main/kotlin/ru/otus/Square.kt
Normal file
@ -0,0 +1,21 @@
|
||||
package ru.otus
|
||||
|
||||
import kotlin.math.pow
|
||||
|
||||
class Square(val x: Int): Figure {
|
||||
override fun area(): Int = x.toDouble().pow(2.0).toInt()
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other?.javaClass != javaClass) {
|
||||
return false
|
||||
}
|
||||
|
||||
other as Square
|
||||
|
||||
return x == other.x
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return javaClass.hashCode()
|
||||
}
|
||||
}
|
||||
74
ok-lessons/m1l4-oop/src/test/kotlin/ru/otus/HomeWorkOOP.kt
Normal file
74
ok-lessons/m1l4-oop/src/test/kotlin/ru/otus/HomeWorkOOP.kt
Normal file
@ -0,0 +1,74 @@
|
||||
package ru.otus
|
||||
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertFalse
|
||||
import org.junit.jupiter.api.Assertions.assertNotEquals
|
||||
|
||||
class HomeWorkOOP {
|
||||
// task 1 - make a Rectangle class that will have width and height
|
||||
// as well as the area calculation method - area()
|
||||
// the test below should pass - uncomment the code in it
|
||||
@Test
|
||||
fun rectangleArea() {
|
||||
val r = Rectangle(10, 20)
|
||||
assertEquals(200, r.area())
|
||||
assertEquals(10, r.width)
|
||||
assertEquals(20, r.height)
|
||||
}
|
||||
|
||||
// task 2 - make the Rectangle.toString() method
|
||||
// the test below should pass - uncomment the code in it
|
||||
@Test
|
||||
fun rectangleToString() {
|
||||
val r = Rectangle(10, 20)
|
||||
assertEquals("Rectangle(10x20)", r.toString())
|
||||
}
|
||||
|
||||
// task 3 - make Rectangle.equals() and Rectangle.hashCode() methods
|
||||
// the test below should pass - uncomment the code in it
|
||||
@Test
|
||||
fun rectangleEquals() {
|
||||
val a = Rectangle(10, 20)
|
||||
val b = Rectangle(10, 20)
|
||||
val c = Rectangle(20, 10)
|
||||
assertEquals(a, b)
|
||||
assertEquals(a.hashCode(), b.hashCode())
|
||||
assertFalse (a === b)
|
||||
assertNotEquals(a, c)
|
||||
}
|
||||
|
||||
// task 4 - make the Square class
|
||||
// the test below should pass - uncomment the code in it
|
||||
@Test
|
||||
fun squareEquals() {
|
||||
val a = Square(10)
|
||||
val b = Square(10)
|
||||
val c = Square(20)
|
||||
assertEquals(a, b)
|
||||
assertEquals(a.hashCode(), b.hashCode())
|
||||
assertFalse (a === b)
|
||||
assertNotEquals(a, c)
|
||||
println(a)
|
||||
}
|
||||
|
||||
// task 5 - make the Figure interface with the area() method, inherit Rectangle and Square from it
|
||||
// the test below should pass - uncomment the code in it
|
||||
@Test
|
||||
fun figureArea() {
|
||||
var f : Figure = Rectangle(10, 20)
|
||||
assertEquals(f.area(), 200)
|
||||
|
||||
f = Square(10)
|
||||
assertEquals(f.area(), 100)
|
||||
}
|
||||
|
||||
// task 6 - make the diffArea(a, b) method
|
||||
// the test below should pass - uncomment the code in it
|
||||
@Test
|
||||
fun diffArea() {
|
||||
val a = Rectangle(10, 20)
|
||||
val b = Square(10)
|
||||
assertEquals(diffArea(a, b), 100)
|
||||
}
|
||||
}
|
||||
17
ok-lessons/m2l1-dsl/build.gradle.kts
Normal file
17
ok-lessons/m2l1-dsl/build.gradle.kts
Normal file
@ -0,0 +1,17 @@
|
||||
plugins {
|
||||
alias(libs.plugins.jvm)
|
||||
}
|
||||
|
||||
group = "ru.otus"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation(kotlin("test"))
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
package ru.otus
|
||||
|
||||
fun query(block: SqlSelectBuilder.() -> Unit) = SqlSelectBuilder().apply(block)
|
||||
@ -0,0 +1,59 @@
|
||||
package ru.otus
|
||||
|
||||
class SqlSelectBuilder {
|
||||
private var table: String? = null
|
||||
private val columns = mutableListOf<String>()
|
||||
private var whereClause: String? = null
|
||||
|
||||
fun select(vararg columns: String) = apply {
|
||||
this.columns.addAll(columns)
|
||||
}
|
||||
|
||||
fun from(table: String) = apply {
|
||||
this.table = table
|
||||
}
|
||||
|
||||
fun where(conditionBuilder: ConditionBuilder.() -> Unit) = apply {
|
||||
val condition = ConditionBuilder().apply(conditionBuilder).build()
|
||||
whereClause = condition
|
||||
}
|
||||
|
||||
fun build(): String {
|
||||
requireNotNull(table) { "Table name must be specified." }
|
||||
val columnsPart = if (columns.isEmpty()) "*" else columns.joinToString(", ")
|
||||
val wherePart = whereClause?.let { " where $it" } ?: ""
|
||||
return "select $columnsPart from $table$wherePart"
|
||||
}
|
||||
|
||||
class ConditionBuilder {
|
||||
private val conditions = mutableListOf<String>()
|
||||
|
||||
infix fun String.eq(value: Any?): String {
|
||||
val formattedValue = formatValue(value)
|
||||
conditions.add("$this = $formattedValue")
|
||||
return this
|
||||
}
|
||||
|
||||
infix fun String.nonEq(value: Any?): String {
|
||||
val formattedValue = formatValue(value)
|
||||
conditions.add(if (value == null) "$this !is null" else "$this != $formattedValue")
|
||||
return this
|
||||
}
|
||||
|
||||
fun or(block: ConditionBuilder.() -> Unit): ConditionBuilder {
|
||||
val nestedConditions = ConditionBuilder().apply(block).build()
|
||||
conditions.add("($nestedConditions)")
|
||||
return this
|
||||
}
|
||||
|
||||
fun build(): String {
|
||||
return conditions.joinToString(" or ")
|
||||
}
|
||||
|
||||
private fun formatValue(value: Any?): String = when (value) {
|
||||
is String -> "'$value'"
|
||||
null -> "null"
|
||||
else -> value.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
110
ok-lessons/m2l1-dsl/src/test/kotlin/ru/otus/HomeWorkDSL.kt
Normal file
110
ok-lessons/m2l1-dsl/src/test/kotlin/ru/otus/HomeWorkDSL.kt
Normal file
@ -0,0 +1,110 @@
|
||||
package ru.otus
|
||||
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFailsWith
|
||||
|
||||
class HomeWorkDSL {
|
||||
private fun checkSQL(expected: String, sql: SqlSelectBuilder) {
|
||||
assertEquals(expected, sql.build())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `simple select all from table`() {
|
||||
val expected = "select * from table"
|
||||
|
||||
val real = query {
|
||||
from("table")
|
||||
}
|
||||
|
||||
checkSQL(expected, real)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `check that select can't be used without table`() {
|
||||
assertFailsWith<Exception> {
|
||||
query {
|
||||
select("col_a")
|
||||
}.build()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `select certain columns from table`() {
|
||||
val expected = "select col_a, col_b from table"
|
||||
|
||||
val real = query {
|
||||
select("col_a", "col_b")
|
||||
from("table")
|
||||
}
|
||||
|
||||
checkSQL(expected, real)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `select certain columns from table 1`() {
|
||||
val expected = "select col_a, col_b from table"
|
||||
|
||||
val real = query {
|
||||
select("col_a", "col_b")
|
||||
from("table")
|
||||
}
|
||||
|
||||
checkSQL(expected, real)
|
||||
}
|
||||
|
||||
/**
|
||||
* __eq__ is "equals" function. Must be one of char:
|
||||
* - for strings - "="
|
||||
* - for numbers - "="
|
||||
* - for null - "is"
|
||||
*/
|
||||
@Test
|
||||
fun `select with complex where condition with one condition`() {
|
||||
val expected = "select * from table where col_a = 'id'"
|
||||
|
||||
val real = query {
|
||||
from("table")
|
||||
where { "col_a" eq "id" }
|
||||
}
|
||||
|
||||
checkSQL(expected, real)
|
||||
}
|
||||
|
||||
/**
|
||||
* __nonEq__ is "non equals" function. Must be one of chars:
|
||||
* - for strings - "!="
|
||||
* - for numbers - "!="
|
||||
* - for null - "!is"
|
||||
*/
|
||||
@Test
|
||||
fun `select with complex where condition with two conditions`() {
|
||||
val expected = "select * from table where col_a != 0"
|
||||
|
||||
val real = query {
|
||||
from("table")
|
||||
where {
|
||||
"col_a" nonEq 0
|
||||
}
|
||||
}
|
||||
|
||||
checkSQL(expected, real)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `when 'or' conditions are specified then they are respected`() {
|
||||
val expected = "select * from table where (col_a = 4 or col_b !is null)"
|
||||
|
||||
val real = query {
|
||||
from("table")
|
||||
where {
|
||||
or {
|
||||
"col_a" eq 4
|
||||
"col_b" nonEq null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkSQL(expected, real)
|
||||
}
|
||||
}
|
||||
24
ok-lessons/m2l2-coroutines/build.gradle.kts
Normal file
24
ok-lessons/m2l2-coroutines/build.gradle.kts
Normal file
@ -0,0 +1,24 @@
|
||||
plugins {
|
||||
alias(libs.plugins.jvm)
|
||||
}
|
||||
|
||||
group = "ru.otus"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.kotlin.stdlib)
|
||||
implementation(libs.kotlin.coroutines)
|
||||
|
||||
// Homework Hard
|
||||
implementation(libs.okhttp) // http client
|
||||
implementation(libs.kotlin.jackson.module) // from string to object
|
||||
|
||||
testImplementation(kotlin("test"))
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
package ru.otus.easy
|
||||
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
suspend fun findNumberInList(toFind: Int, numbers: List<Int>): Int {
|
||||
delay(2000L)
|
||||
return numbers.firstOrNull { it == toFind } ?: -1
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
package ru.otus.easy
|
||||
|
||||
fun generateNumbers() = (0..10000).map {
|
||||
(0..100).random()
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
package ru.otus.hard
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
|
||||
import com.fasterxml.jackson.module.kotlin.readValue
|
||||
import okhttp3.Response
|
||||
import ru.otus.hard.dto.Dictionary
|
||||
|
||||
class DictionaryApi(
|
||||
private val objectMapper: ObjectMapper = jacksonObjectMapper()
|
||||
) {
|
||||
|
||||
fun findWord(locale: Locale, word: String): Dictionary? { // make something with context
|
||||
val url = "$DICTIONARY_API/${locale.code}/$word"
|
||||
println("Searching $url")
|
||||
|
||||
return getBody(HttpClient.get(url).execute())?.firstOrNull()
|
||||
}
|
||||
|
||||
|
||||
private fun getBody(response: Response): List<Dictionary>? {
|
||||
if (!response.isSuccessful) {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
return response.body?.let {
|
||||
objectMapper.readValue(it.string())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
package ru.otus.hard
|
||||
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
|
||||
object HttpClient : OkHttpClient() {
|
||||
fun get(uri: String) =
|
||||
Request.Builder().url(uri).build()
|
||||
.let {
|
||||
newCall(it)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package ru.otus.hard
|
||||
|
||||
@Suppress("unused")
|
||||
enum class Locale(val code: String) {
|
||||
EN("en_US"),
|
||||
RU("ru")
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
package ru.otus.hard
|
||||
|
||||
internal const val DICTIONARY_API = "https://api.dictionaryapi.dev/api/v2/entries"
|
||||
@ -0,0 +1,9 @@
|
||||
package ru.otus.hard.dto
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
data class Dictionary(
|
||||
val word: String,
|
||||
val meanings: List<Meaning>
|
||||
)
|
||||
@ -0,0 +1,14 @@
|
||||
package ru.otus.hard.dto
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
data class Meaning(
|
||||
val definitions: List<Definition>
|
||||
)
|
||||
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
data class Definition(
|
||||
val definition: String,
|
||||
val example: String? = ""
|
||||
)
|
||||
4
ok-lessons/m2l2-coroutines/src/main/resources/words.txt
Normal file
4
ok-lessons/m2l2-coroutines/src/main/resources/words.txt
Normal file
@ -0,0 +1,4 @@
|
||||
Jack shook his head whisked the second and first Lobby level Here in the center the registration desk Behind it are the offices The lobby runs for eighty feet in either direction from the desk Over here in the west the Overlook Dining Room and the Colorado Lounge The banquet and ballroom facility in the
|
||||
Only about the basement Jack said For the winter caretaker the most important level of all Where the action
|
||||
will show you all that The basement floor plan is on the room wall frowned impressively perhaps show that as manager he did not concern himself with such mundane aspects of the operation as the and the plumbing Might not be a bad idea put some traps down there too Just a
|
||||
scrawled a note on a pad he took from his inner coat pocket each sheet bore the legend From the Desk of Stuart in bold black script tore it off and dropped it into the out basket It sat there looking lonesome The pad disappeared back jacket pocket like the conclusion of a magician trick you see it now you don't This guy is a real heavyweight
|
||||
@ -0,0 +1,99 @@
|
||||
package ru.otus
|
||||
|
||||
import java.io.File
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.jupiter.api.Test
|
||||
import ru.otus.easy.findNumberInList
|
||||
import ru.otus.easy.generateNumbers
|
||||
import ru.otus.hard.DictionaryApi
|
||||
import ru.otus.hard.Locale
|
||||
import ru.otus.hard.dto.Dictionary
|
||||
|
||||
class HomeWorkCoroutines {
|
||||
|
||||
@Test
|
||||
fun easyHw() = runBlocking {
|
||||
val numbers = generateNumbers()
|
||||
val toFind = 10
|
||||
val toFindOther = 1000
|
||||
|
||||
val foundNumbers = mutableListOf<Deferred<Int>>()
|
||||
|
||||
foundNumbers.add(async(Dispatchers.Default) { findNumberInList(toFind, numbers) })
|
||||
foundNumbers.add(async(Dispatchers.Default) { findNumberInList(toFindOther, numbers) })
|
||||
|
||||
foundNumbers.awaitAll().forEach {
|
||||
if (it != -1) {
|
||||
println("Your number $it found!")
|
||||
} else {
|
||||
println("Not found number $toFind || $toFindOther")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun hardHw(): Unit = runBlocking {
|
||||
val dictionaryApi = DictionaryApi()
|
||||
val words = FileReader.readFile().split(" ", "\n").toSet()
|
||||
|
||||
// Асинхронный поиск значений
|
||||
val deferredDictionaries = findWordsAsync(dictionaryApi, words, Locale.EN)
|
||||
val dictionaries = deferredDictionaries.awaitAll()
|
||||
|
||||
dictionaries.filterNotNull().map { dictionary ->
|
||||
print("For word ${dictionary.word} i found examples: ")
|
||||
println(
|
||||
dictionary.meanings
|
||||
.mapNotNull { definition ->
|
||||
val r = definition.definitions
|
||||
.mapNotNull { it.example.takeIf { it?.isNotBlank() == true } }
|
||||
.takeIf { it.isNotEmpty() }
|
||||
r
|
||||
}
|
||||
.takeIf { it.isNotEmpty() }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun findWords(
|
||||
dictionaryApi: DictionaryApi,
|
||||
words: Set<String>,
|
||||
@Suppress("SameParameterValue") locale: Locale
|
||||
) =
|
||||
// make some suspensions and async
|
||||
words.map {
|
||||
dictionaryApi.findWord(locale, it)
|
||||
}
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
private fun findWordsAsync(
|
||||
dictionaryApi: DictionaryApi,
|
||||
words: Set<String>,
|
||||
@Suppress("SameParameterValue") locale: Locale
|
||||
): List<Deferred<Dictionary?>> {
|
||||
return words.map { word ->
|
||||
GlobalScope.async(Dispatchers.IO) {
|
||||
try {
|
||||
dictionaryApi.findWord(locale, word)
|
||||
} catch (e: Exception) {
|
||||
println("Error searching word $word: ${e.message}")
|
||||
null // Игнорируем ошибку
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object FileReader {
|
||||
fun readFile(): String =
|
||||
File(
|
||||
this::class.java.classLoader.getResource("words.txt")?.toURI()
|
||||
?: throw RuntimeException("Can't read file")
|
||||
).readText()
|
||||
}
|
||||
}
|
||||
22
ok-lessons/m2l3-flows/build.gradle.kts
Normal file
22
ok-lessons/m2l3-flows/build.gradle.kts
Normal file
@ -0,0 +1,22 @@
|
||||
plugins {
|
||||
alias(libs.plugins.jvm)
|
||||
}
|
||||
|
||||
group = "ru.otus"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.kotlin.stdlib)
|
||||
implementation(libs.kotlin.coroutines)
|
||||
implementation(libs.slf4j)
|
||||
implementation(libs.logback.classic)
|
||||
|
||||
testImplementation(kotlin("test"))
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
71
ok-lessons/m2l3-flows/src/main/kotlin/ru/otus/FlowClasses.kt
Normal file
71
ok-lessons/m2l3-flows/src/main/kotlin/ru/otus/FlowClasses.kt
Normal file
@ -0,0 +1,71 @@
|
||||
package ru.otus
|
||||
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.channels.trySendBlocking
|
||||
import kotlinx.coroutines.flow.*
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
import kotlin.concurrent.schedule
|
||||
|
||||
data class Sample(
|
||||
val serialNumber: String,
|
||||
val value: Double,
|
||||
val timestamp: Instant = Instant.now()
|
||||
)
|
||||
|
||||
interface Detector {
|
||||
fun samples(): Flow<Sample>
|
||||
}
|
||||
|
||||
class CoroutineDetector(
|
||||
private val serialNumber: String,
|
||||
private val sampleDistribution: Sequence<Double>,
|
||||
private val samplePeriod: Long
|
||||
) : Detector {
|
||||
override fun samples(): Flow<Sample> =
|
||||
flow {
|
||||
val values = sampleDistribution.iterator()
|
||||
while (true) {
|
||||
emit(Sample(serialNumber, values.next()))
|
||||
delay(samplePeriod)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class BlockingDetector(
|
||||
private val serialNumber: String,
|
||||
private val sampleDistribution: Sequence<Double>,
|
||||
private val samplePeriod: Long
|
||||
) : Detector {
|
||||
override fun samples(): Flow<Sample> =
|
||||
flow {
|
||||
val values = sampleDistribution.iterator()
|
||||
while (true) {
|
||||
emit(Sample(serialNumber, values.next()))
|
||||
Thread.sleep(samplePeriod)
|
||||
}
|
||||
}.flowOn(Dispatchers.IO)
|
||||
}
|
||||
|
||||
class CallbackDetector(
|
||||
private val serialNumber: String,
|
||||
private val sampleDistribution: Sequence<Double>,
|
||||
private val samplePeriod: Long
|
||||
) : Detector {
|
||||
override fun samples(): Flow<Sample> =
|
||||
callbackFlow {
|
||||
val values = sampleDistribution.iterator()
|
||||
|
||||
val timer = Timer()
|
||||
timer.schedule(0L, samplePeriod) {
|
||||
trySendBlocking(Sample(serialNumber, values.next()))
|
||||
}
|
||||
timer.schedule(10_000L) { close() }
|
||||
|
||||
awaitClose { timer.cancel() }
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> Flow<T>.rollingMax(comparator: Comparator<T>): Flow<T> =
|
||||
runningReduce { max, current -> maxOf(max, current, comparator) }
|
||||
@ -0,0 +1,81 @@
|
||||
package ru.otus
|
||||
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import kotlinx.coroutines.flow.asFlow
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.toList
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import java.math.BigDecimal
|
||||
|
||||
/**
|
||||
* Задание.
|
||||
* Добавить необходимые фильтры для того, чтоб тесты заработали как надо.
|
||||
*
|
||||
* Описание. У нас БД в памяти. В ней нужно найти объект, описанный фильтром SearchFilter.
|
||||
*/
|
||||
class HomeWorkFlow {
|
||||
@Test
|
||||
fun filter() = runBlocking {
|
||||
val flt = SearchFilter(
|
||||
title = "шнурки",
|
||||
type = AdType.DEMAND,
|
||||
visibilitiesOr = setOf(AdVisibility.OWNER, AdVisibility.GROUP),
|
||||
priceMin = BigDecimal("10.00"),
|
||||
)
|
||||
val res = LIST
|
||||
.asFlow()
|
||||
.run {
|
||||
// Фильтр по названию
|
||||
flt.title?.let { title -> this.filter { it.title == title } } ?: this
|
||||
}
|
||||
.run {
|
||||
// Фильтр по типу объявления
|
||||
flt.type?.let { type -> this.filter { it.type == type } } ?: this
|
||||
}
|
||||
.run {
|
||||
// Фильтр по видимости (OR условие)
|
||||
flt.visibilitiesOr?.let { visibilities -> this.filter { it.visibility in visibilities } } ?: this
|
||||
}
|
||||
.run {
|
||||
// Фильтр по минимальной цене
|
||||
flt.priceMin?.let { minPrice -> this.filter { it.price >= minPrice } } ?: this
|
||||
}
|
||||
.toList()
|
||||
|
||||
|
||||
assertEquals(1, res.size)
|
||||
assertEquals("5", res.first().id)
|
||||
}
|
||||
|
||||
companion object {
|
||||
data class SearchFilter(
|
||||
val title: String? = null,
|
||||
val visibilitiesOr: Set<AdVisibility>? = null,
|
||||
val priceMin: BigDecimal? = null,
|
||||
val priceMax: BigDecimal? = null,
|
||||
val type: AdType? = null,
|
||||
)
|
||||
|
||||
data class Ad(
|
||||
val id: String,
|
||||
val title: String,
|
||||
val visibility: AdVisibility,
|
||||
val price: BigDecimal,
|
||||
val type: AdType,
|
||||
)
|
||||
|
||||
enum class AdVisibility { PUBLIC, GROUP, OWNER }
|
||||
enum class AdType { DEMAND, SUPPLY }
|
||||
|
||||
val LIST = listOf(
|
||||
Ad("1", "носок", AdVisibility.PUBLIC, BigDecimal("22.13"), AdType.SUPPLY),
|
||||
Ad("2", "носок", AdVisibility.PUBLIC, BigDecimal("22.13"), AdType.DEMAND),
|
||||
Ad("3", "носок", AdVisibility.PUBLIC, BigDecimal("40.13"), AdType.DEMAND),
|
||||
Ad("4", "носок", AdVisibility.OWNER, BigDecimal("40.13"), AdType.DEMAND),
|
||||
Ad("5", "шнурки", AdVisibility.OWNER, BigDecimal("40.13"), AdType.DEMAND),
|
||||
Ad("6", "шнурки", AdVisibility.OWNER, BigDecimal("40.13"), AdType.SUPPLY),
|
||||
Ad("7", "шнурки", AdVisibility.GROUP, BigDecimal("9.99"), AdType.DEMAND),
|
||||
)
|
||||
}
|
||||
}
|
||||
93
ok-lessons/m2l4-kmp/build.gradle.kts
Normal file
93
ok-lessons/m2l4-kmp/build.gradle.kts
Normal file
@ -0,0 +1,93 @@
|
||||
plugins {
|
||||
alias(libs.plugins.multiplatform)
|
||||
// Только для lombock!!
|
||||
java
|
||||
}
|
||||
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvm {
|
||||
withJava()
|
||||
testRuns["test"].executionTask.configure {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
}
|
||||
|
||||
js {
|
||||
browser {
|
||||
testTask {
|
||||
// useKarma {
|
||||
// // Выбираем браузеры, на которых будет тестироваться
|
||||
// useChrome()
|
||||
// useFirefox()
|
||||
// }
|
||||
// Без этой настройки длительные тесты не отрабатывают
|
||||
useMocha {
|
||||
timeout = "100s"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// linuxX64()
|
||||
macosX64 {
|
||||
binaries {
|
||||
executable()
|
||||
}
|
||||
}
|
||||
|
||||
val coroutinesVersion: String by project
|
||||
val datetimeVersion: String by project
|
||||
|
||||
// Description of modules corresponding to our target platforms
|
||||
// common - common code that we can use on different platforms
|
||||
// for each target platform, we can specify our own specific dependencies
|
||||
sourceSets {
|
||||
val commonMain by getting {
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib-common"))
|
||||
implementation(libs.kotlin.coroutines)
|
||||
implementation(libs.kotlin.datetime)
|
||||
}
|
||||
}
|
||||
commonTest {
|
||||
dependencies {
|
||||
implementation(kotlin("test"))
|
||||
implementation(libs.kotlin.coroutines.test)
|
||||
}
|
||||
}
|
||||
jvmMain {
|
||||
}
|
||||
jvmTest {
|
||||
dependencies {
|
||||
implementation(kotlin("test"))
|
||||
}
|
||||
}
|
||||
// dependencies from npm
|
||||
jsMain {
|
||||
dependencies {
|
||||
implementation(npm("js-big-decimal", "~1.3.4"))
|
||||
implementation(npm("is-sorted", "~1.0.5"))
|
||||
}
|
||||
}
|
||||
jsTest {
|
||||
dependencies {
|
||||
implementation(kotlin("test"))
|
||||
}
|
||||
}
|
||||
// С 1.9.20 можно так
|
||||
nativeMain {
|
||||
}
|
||||
nativeTest {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Только для lombock!!
|
||||
dependencies {
|
||||
compileOnly("org.projectlombok:lombok:1.18.34")
|
||||
annotationProcessor("org.projectlombok:lombok:1.18.34")
|
||||
}
|
||||
85
ok-lessons/m2l5-1-interop/build.gradle.kts
Normal file
85
ok-lessons/m2l5-1-interop/build.gradle.kts
Normal file
@ -0,0 +1,85 @@
|
||||
plugins {
|
||||
alias(libs.plugins.multiplatform)
|
||||
}
|
||||
|
||||
kotlin {
|
||||
js {
|
||||
browser {
|
||||
testTask {
|
||||
// useKarma {
|
||||
// // Выбираем браузеры, на которых будет тестироваться
|
||||
// useChrome()
|
||||
// useFirefox()
|
||||
// }
|
||||
// Без этой настройки длительные тесты не отрабатывают
|
||||
useMocha {
|
||||
timeout = "100s"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
binaries.library()
|
||||
generateTypeScriptDefinitions()
|
||||
}
|
||||
listOf(
|
||||
linuxX64(),
|
||||
macosArm64(),
|
||||
).forEach {
|
||||
it.apply {
|
||||
compilations.getByName("main") {
|
||||
cinterops {
|
||||
// настраиваем cinterop в файле src/nativeInterop/cinterop/libcurl.def
|
||||
val libcurl by creating
|
||||
// create("libcurl")
|
||||
}
|
||||
}
|
||||
binaries {
|
||||
executable {
|
||||
entryPoint = "main"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
val coroutinesVersion: String by project
|
||||
val datetimeVersion: String by project
|
||||
|
||||
// Description of modules corresponding to our target platforms
|
||||
// common - common code that we can use on different platforms
|
||||
// for each target platform, we can specify our own specific dependencies
|
||||
sourceSets {
|
||||
val commonMain by getting {
|
||||
dependencies {
|
||||
implementation(kotlin("stdlib-common"))
|
||||
implementation(libs.kotlin.coroutines)
|
||||
implementation(libs.kotlin.datetime)
|
||||
}
|
||||
}
|
||||
val commonTest by getting {
|
||||
dependencies {
|
||||
implementation(kotlin("test"))
|
||||
implementation(libs.kotlin.coroutines.test)
|
||||
}
|
||||
}
|
||||
// dependencies from npm
|
||||
val jsMain by getting {
|
||||
dependencies {
|
||||
implementation(npm("js-big-decimal", "~1.3.4"))
|
||||
implementation(npm("is-sorted", "~1.0.5"))
|
||||
}
|
||||
}
|
||||
val jsTest by getting {
|
||||
dependencies {
|
||||
implementation(kotlin("test"))
|
||||
}
|
||||
}
|
||||
// С 1.9.20 можно так
|
||||
nativeMain {
|
||||
}
|
||||
nativeTest {
|
||||
dependencies {
|
||||
implementation(kotlin("test"))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
@file:Suppress("UNUSED_PARAMETER", "unused")
|
||||
|
||||
// calling JS code through a js function, awkward to maintain
|
||||
fun useMathRound(value: Double) = js("Math.round(value)")
|
||||
|
||||
// using annotations for JS modules
|
||||
@JsModule("is-sorted")
|
||||
@JsNonModule
|
||||
external fun <T> sorted(array: Array<T>): Boolean
|
||||
|
||||
|
||||
// Using wrappers for JS modules.
|
||||
// Can be generated from TS using dukat (External tool).
|
||||
@JsModule("js-big-decimal")
|
||||
@JsNonModule
|
||||
@JsName("BigDecimal")
|
||||
external class JsBigDecimal(value: Any) {
|
||||
fun getValue(): String
|
||||
fun getPrettyValue(digits: Int, separator: String)
|
||||
fun round(precision: Int = definedExternally, mode: dynamic = definedExternally): JsBigDecimal
|
||||
|
||||
companion object {
|
||||
fun getPrettyValue(number: Any, digits: Int, separator: String)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
@file:Suppress("unused")
|
||||
|
||||
@JsModule("js-big-decimal")
|
||||
@JsNonModule
|
||||
open external class BigDecimal {
|
||||
open var value: Any
|
||||
|
||||
constructor(number: Number = definedExternally)
|
||||
constructor()
|
||||
constructor(number: String = definedExternally)
|
||||
|
||||
open fun getValue(): String
|
||||
open fun getPrettyValue(digits: Any, separator: Any): String
|
||||
open fun round(precision: Number = definedExternally, mode: RoundingModes = definedExternally): BigDecimal
|
||||
open fun floor(): BigDecimal
|
||||
open fun ceil(): BigDecimal
|
||||
open fun add(number: BigDecimal): BigDecimal
|
||||
open fun subtract(number: BigDecimal): BigDecimal
|
||||
open fun multiply(number: BigDecimal): BigDecimal
|
||||
open fun divide(number: BigDecimal, precision: Any): BigDecimal
|
||||
open fun modulus(number: BigDecimal): BigDecimal
|
||||
open fun compareTo(number: BigDecimal): dynamic /* 1 | 0 | "-1" */
|
||||
open fun negate(): BigDecimal
|
||||
|
||||
companion object {
|
||||
var RoundingModes: Any
|
||||
var validate: Any
|
||||
fun getPrettyValue(number: Any, digits: Any, separator: Any): String
|
||||
fun round(number: Any, precision: Number = definedExternally, mode: RoundingModes = definedExternally): String
|
||||
fun floor(number: Any): Any
|
||||
fun ceil(number: Any): Any
|
||||
fun add(number1: Any, number2: Any): String
|
||||
fun subtract(number1: Any, number2: Any): String
|
||||
fun multiply(number1: Any, number2: Any): String
|
||||
fun divide(number1: Any, number2: Any, precision: Any): String
|
||||
fun modulus(number1: Any, number2: Any): String
|
||||
fun compareTo(number1: Any, number2: Any): dynamic /* 1 | 0 | "-1" */
|
||||
fun negate(number: Any): String
|
||||
}
|
||||
}
|
||||
15
ok-lessons/m2l5-1-interop/src/jsMain/kotlin/ex3-JsExport.kt
Normal file
15
ok-lessons/m2l5-1-interop/src/jsMain/kotlin/ex3-JsExport.kt
Normal file
@ -0,0 +1,15 @@
|
||||
@file:OptIn(ExperimentalJsExport::class)
|
||||
|
||||
@JsExport
|
||||
fun fromKotlin() {
|
||||
println("From Kotlin function")
|
||||
}
|
||||
|
||||
@JsExport
|
||||
data class FromKotlinClass(
|
||||
val some: String = "",
|
||||
val id: Int = 0,
|
||||
)
|
||||
|
||||
@JsExport
|
||||
val jsonApp = kotlin.js.json(Pair("name", "App1"))
|
||||
@ -0,0 +1,13 @@
|
||||
@file:Suppress("unused")
|
||||
|
||||
@Suppress("ENUM_CLASS_IN_EXTERNAL_DECLARATION_WARNING") // temporary solution
|
||||
external enum class RoundingModes {
|
||||
CEILING /* = 0 */,
|
||||
DOWN /* = 1 */,
|
||||
FLOOR /* = 2 */,
|
||||
HALF_DOWN /* = 3 */,
|
||||
HALF_EVEN /* = 4 */,
|
||||
HALF_UP /* = 5 */,
|
||||
UNNECESSARY /* = 6 */,
|
||||
UP /* = 7 */
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
package ru.otus.otuskotlin.m1l7
|
||||
|
||||
import BigDecimal
|
||||
import JsBigDecimal
|
||||
import sorted
|
||||
import useMathRound
|
||||
import kotlin.math.PI
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class InteroperabilityJSTest {
|
||||
|
||||
@Test
|
||||
fun mathRoundTest() {
|
||||
|
||||
assertEquals(3, useMathRound(PI))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun sortTest() {
|
||||
assertEquals(true, sorted(arrayOf(1, 2, 3)))
|
||||
assertEquals(false, sorted(arrayOf(4, 1, 2, 3)))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun bigDecimalTest() {
|
||||
val bd = JsBigDecimal("${PI * 10000}")
|
||||
println("BigDecimal: ${bd.getValue()}")
|
||||
println("Pretty: ${bd.getPrettyValue(3, ",")}")
|
||||
println("Pretty static: ${JsBigDecimal.getPrettyValue(bd.getValue(), 3, ",")}")
|
||||
assertEquals("31416", bd.round().getValue())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun dukatLibraryTest() {
|
||||
val bd = BigDecimal("${PI * 10000}")
|
||||
println("BigDecimal: ${bd.getValue()}")
|
||||
println("Pretty: ${bd.getPrettyValue(3, ",")}")
|
||||
println("Pretty static: ${BigDecimal.getPrettyValue(bd.getValue(), 3, ",")}")
|
||||
assertEquals("31416", bd.round().getValue())
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,30 @@
|
||||
# list of header files to generate Kotlin stubs
|
||||
headers = curl/curl.h
|
||||
|
||||
# You also need to specify linking parameters for different platforms
|
||||
compilerOpts.linux_x64 = -I/usr/include -I/Library/Developer/CommandLineTools/SDKs/MacOSX15.sdk/usr/include/
|
||||
linkerOpts.osx = -L/opt/homebrew/Cellar/curl/8.11.1/lib -L/Library/Developer/CommandLineTools/SDKs/MacOSX15.sdk/usr/include/ -lcurl
|
||||
linkerOpts.linux_x64 = -L/Library/Developer/CommandLineTools/SDKs/MacOSX15.sdk/usr/include/ -lcurl
|
||||
|
||||
---
|
||||
|
||||
// Структура на СИ, которую будем использовать в Kotlin-коде
|
||||
typedef struct {
|
||||
int a;
|
||||
double b;
|
||||
} MySumStruct;
|
||||
|
||||
// Функция на СИ, принимающая саму структуру в качестве аргумента
|
||||
double sum_struct(MySumStruct s) {
|
||||
return (double)s.a + s.b;
|
||||
}
|
||||
|
||||
// Функция на СИ, принимающая ссылку на структуру в качестве аргумента
|
||||
double sum_ref(MySumStruct *s) {
|
||||
return (double)s->a + s->b;
|
||||
}
|
||||
|
||||
// Функция на СИ, принимающая ссылку на структуру и вызывающую код из Котлин
|
||||
double sum_fun(MySumStruct *s, double (*callback)(int a, double b)) {
|
||||
return callback(s->a, s->b);
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
@file:OptIn(ExperimentalForeignApi::class)
|
||||
|
||||
import kotlinx.cinterop.*
|
||||
import libcurl.*
|
||||
|
||||
@OptIn(ExperimentalForeignApi::class)
|
||||
fun main() {
|
||||
val curl = curl_easy_init()
|
||||
if (curl != null) {
|
||||
curl_easy_setopt(curl, CURLOPT_URL, "http://info.cern.ch")
|
||||
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L)
|
||||
val res = curl_easy_perform(curl)
|
||||
if (res != CURLE_OK) {
|
||||
println("curl_easy_perform() failed ${curl_easy_strerror(res)?.toKString()}")
|
||||
} else {
|
||||
println("curl_easy_perform() succeeded $res")
|
||||
}
|
||||
curl_easy_cleanup(curl)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
@file:OptIn(ExperimentalForeignApi::class)
|
||||
|
||||
import kotlinx.cinterop.*
|
||||
import libcurl.MySumStruct
|
||||
import libcurl.sum_fun
|
||||
import libcurl.sum_ref
|
||||
import libcurl.sum_struct
|
||||
|
||||
// Использование структур в c-функциях
|
||||
fun sumStruct(a: Int, b: Double): Double {
|
||||
|
||||
// Инициализируем экземпляр c-структуры
|
||||
val s: CValue<MySumStruct> = cValue<MySumStruct> {
|
||||
this.a = a
|
||||
this.b = b
|
||||
}
|
||||
// передаем в функцию саму структуру
|
||||
return sum_struct(s)
|
||||
}
|
||||
|
||||
// Использование c-ссылок на структуры в Котлин
|
||||
fun sumRef(a: Int, b: Double): Double = cValue<MySumStruct> {
|
||||
this.a = a
|
||||
this.b = b
|
||||
}.usePinned { // фиксируем положение структуры в памяти на время вызова
|
||||
// передаем в c-функцию ссылку на структуру
|
||||
sum_ref(it.get())
|
||||
}
|
||||
|
||||
// Аллоцирование памяти для использования в c-функциях
|
||||
fun sumAlloc(a: Int, b: Double): Double = memScoped {
|
||||
// Это блок со скоупом памяти. По выходу из этого скоупа, память автоматически очистится
|
||||
// Это избавляет нас от явного освобождения памяти через free(mem)
|
||||
|
||||
// Выделяем динамическую память
|
||||
alloc<MySumStruct> {
|
||||
this.a = a
|
||||
this.b = b
|
||||
}.usePinned {// фиксируем положение структуры в памяти на время вызова
|
||||
// вызываем функцию с сылкой на аллоцированную структуру
|
||||
sum_ref(it.get().ptr)
|
||||
}
|
||||
}
|
||||
|
||||
// Аллоцирование памяти для использования в c-функциях
|
||||
fun sumCallback(a: Int, b: Double): Double {
|
||||
return cValue<MySumStruct> {
|
||||
this.a = a
|
||||
this.b = b
|
||||
}.usePinned { // фиксируем положение структуры в памяти на время вызова
|
||||
|
||||
// Для демонстрации non-capturing природы колбэков для СИ
|
||||
@Suppress("UNUSED_VARIABLE") val capturingVariable = a.toString()
|
||||
|
||||
// передаем в c-функцию специально подготовленную функцию
|
||||
sum_fun(it.get(), staticCFunction { a: Int, b: Double ->
|
||||
// println("Эта строка не сработает: функция должна быть non-capturing, т.е. не ссылаться наружу -> $capturingVariable")
|
||||
a.toDouble() + b
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class Ex2_memory_managementKtTest {
|
||||
|
||||
@Test
|
||||
fun sumStruct() {
|
||||
assertEquals(5.0, sumStruct(2, 3.0))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun sumRef() {
|
||||
assertEquals(5.0, sumRef(2, 3.0))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun sumAlloc() {
|
||||
assertEquals(5.0, sumAlloc(2, 3.0))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun sumCallback() {
|
||||
assertEquals(5.0, sumCallback(2, 3.0))
|
||||
}
|
||||
}
|
||||
7
ok-lessons/m2l5-2-jni/README.md
Normal file
7
ok-lessons/m2l5-2-jni/README.md
Normal file
@ -0,0 +1,7 @@
|
||||
Работа с JNI
|
||||
|
||||
1. Создать Kotlin-класс
|
||||
2. Сгенерировать из котлин-класса заголовочный файл языка C. Этот файл будет описывать обертку, которую нам нужно написать.
|
||||
3. Написать код на C библиотеки-обертки, соответствующий сгенерированному заголовку. В этом коде можно вызывать целевую нативную библиотеку.
|
||||
|
||||
Т.е. еще раз. Для интеграции с JVM требуется написать библиотеку-обертку, которую будет вызывать JRE. В библиотеке-обертке вы выполните вызов вашей целевой нативной библиотеки.
|
||||
122
ok-lessons/m2l5-2-jni/build.gradle.kts
Normal file
122
ok-lessons/m2l5-2-jni/build.gradle.kts
Normal file
@ -0,0 +1,122 @@
|
||||
import org.gradle.internal.jvm.Jvm
|
||||
import org.jetbrains.kotlin.gradle.targets.jvm.tasks.KotlinJvmTest
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.multiplatform)
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvm {
|
||||
testRuns["test"].executionTask.configure {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
}
|
||||
|
||||
// Description of modules corresponding to our target platforms
|
||||
// common - common code that we can use on different platforms
|
||||
// for each target platform, we can specify our own specific dependencies
|
||||
sourceSets {
|
||||
val jvmMain by getting {
|
||||
}
|
||||
val jvmTest by getting {
|
||||
dependencies {
|
||||
implementation(kotlin("test"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks {
|
||||
val pathToLib: String = project.layout.buildDirectory.dir("c/lib").get().toString()
|
||||
println("PATH TO LIB: $pathToLib")
|
||||
|
||||
val jniHeaderDirectory = layout.buildDirectory.dir("jniIncludes").get().asFile
|
||||
|
||||
val generateJniHeaders by creating {
|
||||
group = "interop"
|
||||
dependsOn(getByName("compileKotlinJvm"))
|
||||
|
||||
// For caching
|
||||
inputs.dir("src/jvmMain/kotlin")
|
||||
// outputs.dir("src/jvmMain/generated/jni")
|
||||
outputs.dir(jniHeaderDirectory)
|
||||
|
||||
doLast {
|
||||
val javaHome = Jvm.current().javaHome
|
||||
val javap = javaHome.resolve("bin").walk().firstOrNull { it.name.startsWith("javap") }?.absolutePath ?: error("javap not found")
|
||||
val javac = javaHome.resolve("bin").walk().firstOrNull { it.name.startsWith("javac") }?.absolutePath ?: error("javac not found")
|
||||
val buildDir = file("build/classes/kotlin/jvm/main")
|
||||
val tmpDir = file("build/tmp/jvmJni").apply { mkdirs() }
|
||||
|
||||
val bodyExtractingRegex = """^.+\Rpublic \w* ?class ([^\s]+).*\{\R((?s:.+))\}\R$""".toRegex()
|
||||
val nativeMethodExtractingRegex = """.*\bnative\b.*""".toRegex()
|
||||
|
||||
buildDir.walkTopDown()
|
||||
.filter { "META" !in it.absolutePath }
|
||||
.forEach { file ->
|
||||
if (!file.isFile) return@forEach
|
||||
println("FILE: $file")
|
||||
|
||||
val output = ByteArrayOutputStream().use {
|
||||
project.exec {
|
||||
commandLine(javap, "-private", "-cp", buildDir.absolutePath, file.absolutePath)
|
||||
standardOutput = it
|
||||
}.assertNormalExitValue()
|
||||
it.toString()
|
||||
}
|
||||
|
||||
val (qualifiedName, methodInfo) = bodyExtractingRegex.find(output)?.destructured ?: return@forEach
|
||||
|
||||
val lastDot = qualifiedName.lastIndexOf('.').takeIf { it >= 0 } ?: return@forEach
|
||||
val packageName = qualifiedName.substring(0, lastDot)
|
||||
val className = qualifiedName.substring(lastDot+1, qualifiedName.length)
|
||||
|
||||
val nativeMethods = nativeMethodExtractingRegex
|
||||
.findAll(methodInfo)
|
||||
.map { it.groups }
|
||||
.flatMap { it.asSequence().mapNotNull { group -> group?.value } }
|
||||
.toList()
|
||||
|
||||
if (nativeMethods.isEmpty()) return@forEach
|
||||
|
||||
val source = buildString {
|
||||
appendLine("package $packageName;")
|
||||
appendLine("public class $className {")
|
||||
for (method in nativeMethods) {
|
||||
if ("()" in method) appendLine(method)
|
||||
else {
|
||||
val updatedMethod = StringBuilder(method).apply {
|
||||
var count = 0
|
||||
var i = 0
|
||||
while (i < length) {
|
||||
if (this[i] == ',' || this[i] == ')') insert(i, " arg${count++}".also { i += it.length + 1 })
|
||||
else i++
|
||||
}
|
||||
}
|
||||
appendLine(updatedMethod)
|
||||
}
|
||||
}
|
||||
appendLine("}")
|
||||
}
|
||||
val outputFile = tmpDir.resolve(packageName.replace(".", "/")).apply { mkdirs() }.resolve("$className.java").apply { delete() }.apply { createNewFile() }
|
||||
outputFile.writeText(source)
|
||||
|
||||
project.exec {
|
||||
commandLine(javac, "-h", jniHeaderDirectory.absolutePath, outputFile.absolutePath)
|
||||
}.assertNormalExitValue()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val makeCLib by creating(Exec::class) {
|
||||
dependsOn(generateJniHeaders)
|
||||
group = "build"
|
||||
commandLine("make")
|
||||
workingDir(layout.projectDirectory.dir("c-jni"))
|
||||
}
|
||||
withType(KotlinJvmTest::class) {
|
||||
dependsOn(makeCLib)
|
||||
systemProperty("java.library.path", pathToLib)
|
||||
}
|
||||
}
|
||||
31
ok-lessons/m2l5-2-jni/c-jni/Makefile
Normal file
31
ok-lessons/m2l5-2-jni/c-jni/Makefile
Normal file
@ -0,0 +1,31 @@
|
||||
JDK=/opt/homebrew/opt/openjdk@23
|
||||
INC=-I../build/jniIncludes -I/usr/include -I/Library/Developer/CommandLineTools/SDKs/MacOSX15.sdk/usr/include/ -I$(JDK)/include
|
||||
|
||||
CC = gcc
|
||||
OUT_FILE_NAME = libc_jni.so
|
||||
|
||||
CFLAGS= -fPIC -O0 -g -Wall -c
|
||||
|
||||
OBJ_DIR=../build/c/obj
|
||||
|
||||
OUT_DIR=../build/c/lib
|
||||
|
||||
.PHONY: rebuild all clean
|
||||
|
||||
all: $(OUT_FILE_NAME)
|
||||
|
||||
#Compiling every *.cpp to *.o
|
||||
$(OBJ_DIR)/%.o: %.c dirmake
|
||||
$(CC) -c $(INC) $(CFLAGS) -o $@ $<
|
||||
|
||||
$(OUT_FILE_NAME): $(patsubst %.c,$(OBJ_DIR)/%.o,$(wildcard *.c))
|
||||
$(CC) -shared -o $(OUT_DIR)/$(OUT_FILE_NAME) $^
|
||||
|
||||
dirmake:
|
||||
@mkdir -p $(OUT_DIR)
|
||||
@mkdir -p $(OBJ_DIR)
|
||||
|
||||
clean:
|
||||
rm -f $(OBJ_DIR)/*.o $(OUT_DIR)/$(OUT_FILE_NAME) Makefile.bak
|
||||
|
||||
rebuild: clean all
|
||||
6
ok-lessons/m2l5-2-jni/c-jni/jni.c
Normal file
6
ok-lessons/m2l5-2-jni/c-jni/jni.c
Normal file
@ -0,0 +1,6 @@
|
||||
#include "ru_otus_otuskotlin_interop_NativeExample.h"
|
||||
|
||||
JNIEXPORT jint JNICALL Java_ru_otus_otuskotlin_interop_NativeExample_native_1add
|
||||
(JNIEnv *env, jobject jo, jint a, jint b) {
|
||||
return a + b;
|
||||
}
|
||||
11
ok-lessons/m2l5-2-jni/src/jvmMain/kotlin/NativeExample.kt
Normal file
11
ok-lessons/m2l5-2-jni/src/jvmMain/kotlin/NativeExample.kt
Normal file
@ -0,0 +1,11 @@
|
||||
package ru.otus.otuskotlin.interop
|
||||
|
||||
class NativeExample {
|
||||
external fun native_add(a: Int, b: Int): Int
|
||||
|
||||
companion object {
|
||||
init {
|
||||
System.loadLibrary("c_jni")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
package ru.otus.otuskotlin.interop
|
||||
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class NativeExampleTest {
|
||||
@Test
|
||||
fun cExampleTest() {
|
||||
val re = NativeExample()
|
||||
assertEquals(5, re.native_add(3, 2))
|
||||
}
|
||||
}
|
||||
66
ok-lessons/m2l6-gradle/build.gradle.kts
Normal file
66
ok-lessons/m2l6-gradle/build.gradle.kts
Normal file
@ -0,0 +1,66 @@
|
||||
import org.jetbrains.kotlin.gradle.internal.ensureParentDirsCreated
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.jvm)
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
tasks {
|
||||
// Таска для создания файла
|
||||
val myCustomTask by creating {
|
||||
group = "my group"
|
||||
val dir = layout.buildDirectory.dir("my-in")
|
||||
outputs.dir(dir)
|
||||
|
||||
doFirst {
|
||||
val fileContent = """
|
||||
package my.x
|
||||
|
||||
const val MY_VERSION: String = "${project.version}"
|
||||
""".trimIndent()
|
||||
dir
|
||||
.get()
|
||||
.file("my-version.kt")
|
||||
.asFile
|
||||
.apply {
|
||||
ensureParentDirsCreated()
|
||||
writeText(fileContent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val myCopyTask by creating(Copy::class) {
|
||||
dependsOn(myCustomTask)
|
||||
|
||||
group = "my group"
|
||||
from(myCustomTask.outputs)
|
||||
into(layout.buildDirectory.dir("tmp"))
|
||||
}
|
||||
|
||||
compileKotlin {
|
||||
println(layout.projectDirectory.dir("src/jvmMain/kotlin"))
|
||||
source(layout.buildDirectory.dir("my-in"), layout.projectDirectory.dir("src/jvmMain/kotlin"))
|
||||
dependsOn(myCopyTask)
|
||||
}
|
||||
}
|
||||
|
||||
tasks {
|
||||
create("myTask") {
|
||||
println("Configuration stage")
|
||||
doFirst { println("At task starts") }
|
||||
doLast { println("At task ends") }
|
||||
}
|
||||
}
|
||||
|
||||
afterEvaluate {
|
||||
tasks {
|
||||
create("myOtherTask") {
|
||||
println("After other tasks initialized")
|
||||
}
|
||||
|
||||
forEach { println("TASK $it") }
|
||||
}
|
||||
}
|
||||
13
ok-lessons/m2l6-gradle/src/jvmMain/kotlin/RustExample.kt
Normal file
13
ok-lessons/m2l6-gradle/src/jvmMain/kotlin/RustExample.kt
Normal file
@ -0,0 +1,13 @@
|
||||
package ru.otus.otuskotlin.interop
|
||||
|
||||
class RustExample {
|
||||
external fun rust_add(a: Int, b: Int): Int
|
||||
|
||||
init {
|
||||
System.loadLibrary("c_jni")
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
println(my.x.MY_VERSION)
|
||||
}
|
||||
12
ok-lessons/m2l6-gradle/src/jvmTest/kotlin/RustExampleTest.kt
Normal file
12
ok-lessons/m2l6-gradle/src/jvmTest/kotlin/RustExampleTest.kt
Normal file
@ -0,0 +1,12 @@
|
||||
package ru.otus.otuskotlin.interop
|
||||
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class RustExampleTest {
|
||||
@Test
|
||||
fun rustExampleTest() {
|
||||
val re = RustExample()
|
||||
assertEquals(5, re.rust_add(3, 2))
|
||||
}
|
||||
}
|
||||
7
ok-lessons/m2l6-gradle/sub1/ssub1/build.gradle.kts
Normal file
7
ok-lessons/m2l6-gradle/sub1/ssub1/build.gradle.kts
Normal file
@ -0,0 +1,7 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
7
ok-lessons/m2l6-gradle/sub1/ssub2/build.gradle.kts
Normal file
7
ok-lessons/m2l6-gradle/sub1/ssub2/build.gradle.kts
Normal file
@ -0,0 +1,7 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
15
ok-lessons/m2l6-gradle/sub2/build.gradle.kts
Normal file
15
ok-lessons/m2l6-gradle/sub2/build.gradle.kts
Normal file
@ -0,0 +1,15 @@
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// implementation(project(":m2l5-gradle:sub1:ssub1"))
|
||||
// implementation(project(":m2l5-gradle:sub1:ssub2"))
|
||||
|
||||
implementation(projects.m2l5Gradle.sub1.ssub1)
|
||||
implementation(projects.m2l5Gradle.sub1.ssub2)
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
26
ok-lessons/settings.gradle.kts
Normal file
26
ok-lessons/settings.gradle.kts
Normal file
@ -0,0 +1,26 @@
|
||||
dependencyResolutionManagement {
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
versionCatalogs {
|
||||
create("libs") {
|
||||
from(files("../gradle/libs.versions.toml"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "ok-lessons"
|
||||
|
||||
include("m1l1-first")
|
||||
include("m1l2-basic")
|
||||
include("m1l3-func")
|
||||
include("m1l4-oop")
|
||||
include("m2l1-dsl")
|
||||
include("m2l2-coroutines")
|
||||
include("m2l3-flows")
|
||||
include("m2l4-kmp")
|
||||
include("m2l5-1-interop")
|
||||
include("m2l5-2-jni")
|
||||
include("m2l6-gradle")
|
||||
7
ok-messenger-be/build.gradle.kts
Normal file
7
ok-messenger-be/build.gradle.kts
Normal file
@ -0,0 +1,7 @@
|
||||
plugins {
|
||||
alias(libs.plugins.jvm) apply false
|
||||
alias(libs.plugins.multiplatform) apply false
|
||||
}
|
||||
|
||||
group = "ru.otus.messenger"
|
||||
version = "0.0.1"
|
||||
2
ok-messenger-be/gradle.properties
Normal file
2
ok-messenger-be/gradle.properties
Normal file
@ -0,0 +1,2 @@
|
||||
kotlin.code.style=official
|
||||
kotlin.native.ignoreDisabledTargets=true
|
||||
3
ok-messenger-be/ok-messenger-tmp/build.gradle.kts
Normal file
3
ok-messenger-be/ok-messenger-tmp/build.gradle.kts
Normal file
@ -0,0 +1,3 @@
|
||||
plugins {
|
||||
id("build-jvm")
|
||||
}
|
||||
3
ok-messenger-be/ok-messenger-tmp/src/main/kotlin/Main.kt
Normal file
3
ok-messenger-be/ok-messenger-tmp/src/main/kotlin/Main.kt
Normal file
@ -0,0 +1,3 @@
|
||||
fun main() {
|
||||
print("Hello world")
|
||||
}
|
||||
26
ok-messenger-be/settings.gradle.kts
Normal file
26
ok-messenger-be/settings.gradle.kts
Normal file
@ -0,0 +1,26 @@
|
||||
|
||||
dependencyResolutionManagement {
|
||||
versionCatalogs {
|
||||
create("libs") {
|
||||
from(files("../gradle/libs.versions.toml"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pluginManagement {
|
||||
includeBuild("../build-plugin")
|
||||
plugins {
|
||||
id("build-jvm") apply false
|
||||
id("build-kmp") apply false
|
||||
}
|
||||
repositories {
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "ok-messenger-be"
|
||||
|
||||
//enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
|
||||
|
||||
include(":ok-messenger-tmp")
|
||||
@ -1,10 +1,13 @@
|
||||
pluginManagement {
|
||||
val kotlinVersion: String by settings
|
||||
|
||||
plugins {
|
||||
kotlin("jvm") version kotlinVersion
|
||||
dependencyResolutionManagement {
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "otus-kotlin-developer"
|
||||
include("m1l1-first")
|
||||
|
||||
includeBuild("build-plugin")
|
||||
|
||||
includeBuild("ok-lessons")
|
||||
|
||||
includeBuild("ok-messenger-be")
|
||||
|
||||
Reference in New Issue
Block a user