Repository: cashapp/redwood
Pull request #452 (2022-10-13 00:01:04)

Implement LayoutModifier.toString().
Closes: https://github.com/cashapp/redwood/issues/430


Patch:
diff --git a/build.gradle b/build.gradle
index 62bd99e2a..23fc5d950 100644
--- a/build.gradle
+++ b/build.gradle
@@ -19,14 +19,6 @@ buildscript {
     mavenCentral()
     google()
     gradlePluginPortal()
-
-    // Remove once Paparazzi 1.1.0 is released with support for the multiplatform plugin.
-    maven {
-      url 'https://oss.sonatype.org/content/repositories/snapshots/'
-      content {
-        includeGroup "app.cash.paparazzi"
-      }
-    }
   }
 }
 
@@ -36,14 +28,6 @@ allprojects {
   repositories {
     mavenCentral()
     google()
-
-    // Remove once Paparazzi 1.1.0 is released with support for the multiplatform plugin.
-    maven {
-      url 'https://oss.sonatype.org/content/repositories/snapshots/'
-      content {
-        includeGroup "app.cash.paparazzi"
-      }
-    }
   }
 
   tasks.withType(Test).configureEach {
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 12468690f..a2ed2c679 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -46,7 +46,7 @@ google-material = { module = "com.google.android.material:material", version = "
 jetbrains-compose-runtime = { module = "org.jetbrains.compose.runtime:runtime", version.ref = "jbComposeRuntime" }
 
 androidGradlePlugin = { module = "com.android.tools.build:gradle", version = "7.3.0" }
-kotlinPoet = { module = "com.squareup:kotlinpoet", version = "1.11.0" }
+kotlinPoet = { module = "com.squareup:kotlinpoet", version = "1.12.0" }
 clikt = { module = "com.github.ajalt.clikt:clikt", version = "3.1.0" }
 junit = { module = "junit:junit", version = "4.13.1" }
 testParameterInjector = { module = "com.google.testparameterinjector:test-parameter-injector", version = "1.1" }
@@ -57,7 +57,7 @@ okHttp = { module = "com.squareup.okhttp3:okhttp", version = "4.9.1" }
 zipline = { module = "app.cash.zipline:zipline", version.ref = "zipline" }
 zipline-gradlePlugin = { module = "app.cash.zipline:zipline-gradle-plugin", version.ref = "zipline" }
 zipline-loader = { module = "app.cash.zipline:zipline-loader", version.ref = "zipline" }
-paparazzi-gradlePlugin = { module = "app.cash.paparazzi:paparazzi-gradle-plugin", version = "1.1.0-SNAPSHOT" }
+paparazzi-gradlePlugin = { module = "app.cash.paparazzi:paparazzi-gradle-plugin", version = "1.1.0" }
 jimfs = "com.google.jimfs:jimfs:1.2"
 coil-compose = { module = "io.coil-kt:coil-compose", version = "2.1.0" }
 
diff --git a/redwood-cli/build.gradle b/redwood-cli/build.gradle
index 80e560d52..bf43020c7 100644
--- a/redwood-cli/build.gradle
+++ b/redwood-cli/build.gradle
@@ -17,6 +17,7 @@ dependencies {
   implementation libs.kotlinx.serialization.core
   implementation libs.xmlutil.serialization
 
+  testImplementation projects.testSchema.compose
   testImplementation libs.kotlin.test
   testImplementation libs.junit
   testImplementation libs.truth
diff --git a/redwood-cli/src/main/kotlin/app/cash/redwood/generator/composeGeneration.kt b/redwood-cli/src/main/kotlin/app/cash/redwood/generator/composeGeneration.kt
index 136ab3e3b..953f5c1f7 100644
--- a/redwood-cli/src/main/kotlin/app/cash/redwood/generator/composeGeneration.kt
+++ b/redwood-cli/src/main/kotlin/app/cash/redwood/generator/composeGeneration.kt
@@ -331,6 +331,7 @@ private fun generateLayoutModifier(
     .addSuperinterface(interfaceType)
     .addFunction(layoutModifierEquals(schema, layoutModifier))
     .addFunction(layoutModifierHashCode(layoutModifier))
+    .addFunction(layoutModifierToString(layoutModifier))
     .build()
 
   return function to type
diff --git a/redwood-cli/src/main/kotlin/app/cash/redwood/generator/diffConsumingGeneration.kt b/redwood-cli/src/main/kotlin/app/cash/redwood/generator/diffConsumingGeneration.kt
index fae5a91c0..1823ab995 100644
--- a/redwood-cli/src/main/kotlin/app/cash/redwood/generator/diffConsumingGeneration.kt
+++ b/redwood-cli/src/main/kotlin/app/cash/redwood/generator/diffConsumingGeneration.kt
@@ -456,6 +456,7 @@ internal fun generateDiffConsumingLayoutModifier(schema: Schema, host: Schema =
             .addSuperinterface(schema.layoutModifierType(layoutModifier))
             .addFunction(layoutModifierEquals(schema, layoutModifier))
             .addFunction(layoutModifierHashCode(layoutModifier))
+            .addFunction(layoutModifierToString(layoutModifier))
             .build(),
         )
       }
diff --git a/redwood-cli/src/main/kotlin/app/cash/redwood/generator/sharedHelpers.kt b/redwood-cli/src/main/kotlin/app/cash/redwood/generator/sharedHelpers.kt
index 5ed47f764..4eef61e63 100644
--- a/redwood-cli/src/main/kotlin/app/cash/redwood/generator/sharedHelpers.kt
+++ b/redwood-cli/src/main/kotlin/app/cash/redwood/generator/sharedHelpers.kt
@@ -28,6 +28,7 @@ import com.squareup.kotlinpoet.INT
 import com.squareup.kotlinpoet.KModifier
 import com.squareup.kotlinpoet.LambdaTypeName
 import com.squareup.kotlinpoet.MemberName
+import com.squareup.kotlinpoet.STRING
 import com.squareup.kotlinpoet.TypeName
 import com.squareup.kotlinpoet.UNIT
 import com.squareup.kotlinpoet.asClassName
@@ -139,3 +140,24 @@ internal fun layoutModifierHashCode(layoutModifier: LayoutModifier): FunSpec {
     }
     .build()
 }
+
+internal fun layoutModifierToString(layoutModifier: LayoutModifier): FunSpec {
+  val simpleName = layoutModifier.type.simpleName!!
+  return FunSpec.builder("toString")
+    .addModifiers(KModifier.OVERRIDE)
+    .returns(STRING)
+    .apply {
+      if (layoutModifier.properties.isEmpty()) {
+        addStatement("return %S", simpleName)
+      } else {
+        val statement = StringBuilder().append(simpleName).append("(")
+        val lastIndex = layoutModifier.properties.lastIndex
+        for ((index, property) in layoutModifier.properties.withIndex()) {
+          val suffix = if (index != lastIndex) ", " else ")"
+          statement.append(property.name).append("=$").append(property.name).append(suffix)
+        }
+        addStatement("return %P", statement.toString())
+      }
+    }
+    .build()
+}


Test patch:
diff --git a/redwood-cli/src/test/kotlin/app/cash/redwood/generator/LayoutModifierGenerationTest.kt b/redwood-cli/src/test/kotlin/app/cash/redwood/generator/LayoutModifierGenerationTest.kt
index 36906bd6f..abc38979e 100644
--- a/redwood-cli/src/test/kotlin/app/cash/redwood/generator/LayoutModifierGenerationTest.kt
+++ b/redwood-cli/src/test/kotlin/app/cash/redwood/generator/LayoutModifierGenerationTest.kt
@@ -19,6 +19,11 @@ import app.cash.redwood.schema.LayoutModifier
 import app.cash.redwood.schema.Schema
 import app.cash.redwood.schema.parser.parseSchema
 import com.google.common.truth.Truth.assertThat
+import example.redwood.compose.customType
+import example.redwood.compose.customTypeStateless
+import example.redwood.compose.customTypeWithDefault
+import kotlin.time.Duration.Companion.minutes
+import kotlin.time.Duration.Companion.seconds
 import org.junit.Test
 
 class LayoutModifierGenerationTest {
@@ -86,4 +91,16 @@ class LayoutModifierGenerationTest {
       """.trimMargin(),
     )
   }
+
+  @Test
+  fun `layout modifier implements toString`() {
+    var type = app.cash.redwood.LayoutModifier.customType(20.seconds)
+    assertThat(type.toString()).isEqualTo("CustomType(customType=20s)")
+
+    type = app.cash.redwood.LayoutModifier.customTypeStateless()
+    assertThat(type.toString()).isEqualTo("CustomTypeStateless")
+
+    type = app.cash.redwood.LayoutModifier.customTypeWithDefault(40.minutes, "hello")
+    assertThat(type.toString()).isEqualTo("CustomTypeWithDefault(customType=40m, string=hello)")
+  }
 }
diff --git a/test-schema/src/main/kotlin/example/redwood/schema.kt b/test-schema/src/main/kotlin/example/redwood/schema.kt
index 1646ef1b7..ad2207a4e 100644
--- a/test-schema/src/main/kotlin/example/redwood/schema.kt
+++ b/test-schema/src/main/kotlin/example/redwood/schema.kt
@@ -32,11 +32,11 @@ import kotlin.time.Duration
     RowVerticalAlignment::class,
     AccessibilityDescription::class,
     CustomType::class,
+    CustomTypeStateless::class,
     CustomTypeWithDefault::class,
     Text::class,
     Button::class,
     TextInput::class,
-    SomeMarker::class,
     Space::class,
   ],
   dependencies = [
@@ -95,11 +95,11 @@ public data class CustomType(
   val customType: Duration,
 )
 
+@LayoutModifier(4)
+public object CustomTypeStateless
+
 @LayoutModifier(5)
 public data class CustomTypeWithDefault(
   val customType: Duration,
   @Default("\"sup\"") val string: String,
 )
-
-@LayoutModifier(4)
-public object SomeMarker