From 3beede807b13a9807dd2e10bc32bf8a8f4f0f24a Mon Sep 17 00:00:00 2001 From: Diego Casella Date: Mon, 13 May 2024 20:14:15 +0200 Subject: [PATCH] fix: do not redact regular case classes (#16) * fix: scala2: do not run redaction on case classes without any @redacted annotation * fix: added example & test for value case classes; bumped version --- README.md | 26 ++++++++++---- .../redacted/RedactedPluginComponent.scala | 3 ++ .../polentino/redacted/RedactedSpec.scala | 34 +++++++++++++++++++ .../github/polentino/redacted/package.scala | 5 +++ version.sbt | 2 +- 5 files changed, 63 insertions(+), 7 deletions(-) create mode 100644 tests/src/test/scala/io/github/polentino/redacted/package.scala diff --git a/README.md b/README.md index 1b903aa..3b325ab 100644 --- a/README.md +++ b/README.md @@ -51,8 +51,8 @@ in your `build.sbt` file, add the following lines ```scala 3 val redactedVersion = // use latest version of the library - resolvers += DefaultMavenRepository -, +resolvers += DefaultMavenRepository + libraryDependencies ++= Seq( "io.github.polentino" %% "redacted" % redactedVersion cross CrossVersion.full, compilerPlugin("io.github.polentino" %% "redacted-plugin" % redactedVersion cross CrossVersion.full) @@ -104,7 +104,7 @@ will still print the real values: > $ abcdefghijklmnopqrstuvwxyz > $ polentino911@somemail.com -### Nested case class! +### Nested case class It also works with nested case classes: @@ -118,7 +118,7 @@ println(wrapper) will print > Wrapper(id-1,User(8b2d4570-d043-473b-a56d-fe98105ccc2b, polentino911, ***)) -### Nested case class with upper level annotation! +### Nested case class with upper level annotation It also works with nested case classes: @@ -132,6 +132,21 @@ println(wrapper) will print > Wrapper(id-1,***) +### Value case classes + +`@redacted` plays nicely with value case classes too, i.e. + +```scala 3 +case class Password(@redacted value: String) extends AnyVal +val p = Password("somepassword") +println(p) +``` +will print on console + +```scala 3 +Password(***) +``` + ### Note on curried case classes While it is possible to write something like @@ -182,8 +197,7 @@ implementation by selectively returning either the `***` string, or the value of ```scala 3 def toString(): String = - "(" + this.< field not redacted > + "," + "***" + -...+")" + "(" + this.< field not redacted > + "," + "***" +...+")" ``` ## Improvements diff --git a/plugin/src/main/scala-2/io/github/polentino/redacted/RedactedPluginComponent.scala b/plugin/src/main/scala-2/io/github/polentino/redacted/RedactedPluginComponent.scala index 09bd079..ad5feb4 100644 --- a/plugin/src/main/scala-2/io/github/polentino/redacted/RedactedPluginComponent.scala +++ b/plugin/src/main/scala-2/io/github/polentino/redacted/RedactedPluginComponent.scala @@ -91,6 +91,9 @@ class RedactedPluginComponent(val global: Global) extends PluginComponent with T classDef.impl.body.collectFirst { case d: DefDef if d.name.decode == GenBCode.INSTANCE_CONSTRUCTOR_NAME => d.vparamss.headOption.fold(List.empty[ValDef])(v => v.filter(_.mods.hasAnnotationNamed(redactedTypeName))) + } match { + case Some(fields) if fields.nonEmpty => Some(fields) + case _ => None } /** Utility method to generate a new `toString` definition based on the parameters marked with `@redacted`. diff --git a/tests/src/test/scala/io/github/polentino/redacted/RedactedSpec.scala b/tests/src/test/scala/io/github/polentino/redacted/RedactedSpec.scala index 0c582c8..379d44a 100644 --- a/tests/src/test/scala/io/github/polentino/redacted/RedactedSpec.scala +++ b/tests/src/test/scala/io/github/polentino/redacted/RedactedSpec.scala @@ -28,6 +28,25 @@ class RedactedSpec extends AnyFlatSpec with ScalaCheckPropertyChecks { } } + it should "not change the default behavior, if no annotation is used" in { + case class NormalCaseClass(name: String, age: Int) + + forAll { (name: String, age: Int) => + val expected = s"NormalCaseClass($name,$age)" + val testing = NormalCaseClass(name, age) + val implicitToString = s"$testing" + val explicitToString = testing.toString + + val cp = new Checkpoint + cp { assert(implicitToString == expected) } + cp { assert(explicitToString == expected) } + cp { + assert(testing.name == name && testing.age == age) + } + cp.reportAll() + } + } + it should "work with a redacted case class with many members" in { case class ManyMembers(field1: String, @redacted field2: String, @redacted field3: String, field4: String) @@ -136,6 +155,21 @@ class RedactedSpec extends AnyFlatSpec with ScalaCheckPropertyChecks { } } + it should "work with value case classes" in { + forAll { (pwd: String) => + val expected = s"Password(***)" + val testing = Password(pwd) + val implicitToString = s"$testing" + val explicitToString = testing.toString + + val cp = new Checkpoint + cp { assert(implicitToString == expected) } + cp { assert(explicitToString == expected) } + cp { assert(testing.value == pwd) } + cp.reportAll() + } + } + it must "not change the behavior of `hashCode`" in { final case class TestClass(uuid: UUID, name: String, age: Int) object RedactedTestClass { diff --git a/tests/src/test/scala/io/github/polentino/redacted/package.scala b/tests/src/test/scala/io/github/polentino/redacted/package.scala new file mode 100644 index 0000000..5fad1ed --- /dev/null +++ b/tests/src/test/scala/io/github/polentino/redacted/package.scala @@ -0,0 +1,5 @@ +package io.github.polentino + +package object redacted { + case class Password(@redacted value: String) extends AnyVal +} diff --git a/version.sbt b/version.sbt index 9283500..141979e 100644 --- a/version.sbt +++ b/version.sbt @@ -1 +1 @@ -ThisBuild / version := "0.5.0" +ThisBuild / version := "0.5.1"