Releases: arainko/ducktape
ducktape 0.2.9
ducktape 0.2.9
This release brings lints to Field.modify{Source,Dest}Names
and Case.modify{Source,Dest}Names
which provide warnings when a rename config doesn't actually do anything:
case class Source(int: Int, level1: SourceEnum)
case class Dest(int: Int, level1: DestEnum)
enum DestEnum {
case One(int: Int, str: String)
case Two(int: Int, str: String, level1: DestLevel1, level2: DestLevel1Enum)
case Three(int: Int, str: String)
}
enum SourceEnum {
case One(int: Int, str: String)
case Two(int: Int, str: String, level1: SourceLevel1, level2: SourceLevel1Enum)
case Three(int: Int, str: String)
}
enum SourceLevel1Enum {
case One
case Two
}
enum DestLevel1Enum {
case One
case Two
}
case class SourceLevel1(int: Int)
case class DestLevel1(int: Int)
assertTransformsConfigured(
Source(1, SourceEnum.Two(2, "2", SourceLevel1(3), SourceLevel1Enum.Two)),
Dest(1, DestEnum.Two(2, "2", DestLevel1(3), DestLevel1Enum.Two))
)(
Case.modifySourceNames(_.toLowerCase).local(_.level1.at[SourceEnum.Two])
// ^ WARNING: Config is not actually being used anywhere
)
)
...or when one rename overrides another:
test("local flags with higher priority overwrite flags with lower priority") {
case class Source(int: Int, str: String)
case class Dest(INT_ADDITION: Int, STR: String)
assertTransformsConfigured(
Source(1, "asd"),
Dest(1, "asd")
)(
Field.modifyDestNames(_.toUpperCase.replace("_WHATEVER_THIS_FLAGS_HAS_LOWER_PRIO", "")).local(a => a),
// ^ WARNING: Config is being overridden by Field.modifyDestNames(_.toLowerCase.replace("_addition", "")).local(a => a) @ FlagSuite.scala:523:82
),
Field.modifyDestNames(_.toLowerCase.replace("_addition", "")).local(a => a)
)
}
What's Changed
- Update sbt-mdoc to 2.7.0 by @scala-steward in #259
- Update sbt-mdoc to 2.7.1 by @scala-steward in #260
- Update README.md by @arainko in #262
- Unify
Map
access in Planner by @arainko in #263 - Update scalafmt-core to 3.9.5 by @scala-steward in #265
- Update munit to 1.1.1 by @scala-steward in #264
- Update scalafmt-core to 3.9.6 by @scala-steward in #266
- Update scala3-library, ... to 3.3.6 by @scala-steward in #268
- Update sbt-scalafix to 0.14.3 by @scala-steward in #267
- Publish using the central host by @arainko in #269
- Update sbt, scripted-plugin to 1.11.0 by @scala-steward in #271
- Flag lints by @arainko in #272
- Update scalafmt-core to 3.9.7 by @scala-steward in #273
- Update sbt-scalajs, scalajs-library_2.13, ... to 1.19.0 by @scala-steward in #261
- Update sbt, scripted-plugin to 1.11.1 by @scala-steward in #274
- bump sbt-typelevel by @arainko in #276
- Update auxlib, clib, javalib, nativelib, ... to 0.5.8 by @scala-steward in #277
- Update sbt, scripted-plugin to 1.11.2 by @scala-steward in #278
Full Changelog: v0.2.8...v0.2.9
ducktape 0.2.8
ducktape 0.2.8
Highlights of this release:
- Support for field and case name transformations via
Field.modifySourceNames
,Field.modifyDestNames
,Case.modifySourceNames
andCase.modifyDestNames
and modifiers for those config options that let you fine-tune the transformations further:.regional(_.path.to.field)
- this will apply renames to all transformations 'below' the path you've picked (this is the default).typeSpecific[SomeType]
- this will apply the renames to subtypes of a type you've picked.local(_.path.to.field)
- this will apply renames to the field corresponding to that field/enum subcase (but not the field and the enum subcase themselves!)
Head on over to the docs to see more.
Examples
test("dest field regional flag covers the selected case class and everything below it") {
case class Source(int: Int, str: String, level1: SourceLevel1)
case class SourceLevel1(INT: Int, STR: String, LEVEL2: SourceLevel2)
case class SourceLevel2(INT: Int, STR: String)
case class Dest(int: Int, str: String, level1: DestLevel1)
case class DestLevel1(INT: Int, STR: String, LEVEL2: DestLevel2)
case class DestLevel2(INT: Int, STR: String)
val source = Source(1, "1", SourceLevel1(2, "2", SourceLevel2(3, "3")))
val expected = Dest(1, "1", DestLevel1(2, "2", DestLevel2(3, "3")))
assertTransformsConfigured(source, expected)(
Field.modifyDestNames(_.toUpperCase).regional(_.level1)
)
}
test("source case regional flag covers the selected subtype and everything below (picked as a field in case class)") {
case class Source(int: Int, level1: SourceEnum)
case class Dest(int: Int, level1: DestEnum)
enum DestEnum {
case one(int: Int, str: String)
case two(int: Int, str: String, level1: DestLevel1, level2: DestLevel1Enum)
case three(int: Int, str: String)
}
enum SourceEnum {
case ONE(int: Int, str: String)
case TWO(int: Int, str: String, level1: SourceLevel1, level2: SourceLevel1Enum)
case THREE(int: Int, str: String)
}
enum SourceLevel1Enum {
case One
case Two
}
enum DestLevel1Enum {
case one
case two
}
case class SourceLevel1(int: Int)
case class DestLevel1(int: Int)
assertTransformsConfigured(
Source(1, SourceEnum.TWO(2, "2", SourceLevel1(3), SourceLevel1Enum.Two)),
Dest(1, DestEnum.two(2, "2", DestLevel1(3), DestLevel1Enum.two))
)(
Case.modifySourceNames(_.toLowerCase).regional(_.level1)
)
}
Examples of local flags:
test("dest field local flag covers the selected case class and nothing else") {
case class Source(int: Int, str: String, level1: SourceLevel1)
case class SourceLevel1(INT: Int, STR: String, LEVEL2: SourceLevel2)
case class SourceLevel2(int: Int, str: String)
case class Dest(int: Int, str: String, level1: DestLevel1)
case class DestLevel1(int: Int, str: String, level2: DestLevel2)
case class DestLevel2(int: Int, str: String)
val source = Source(1, "1", SourceLevel1(2, "2", SourceLevel2(3, "3")))
val expected = Dest(1, "1", DestLevel1(2, "2", DestLevel2(3, "3")))
assertTransformsConfigured(source, expected)(
Field.modifyDestNames(_.toUpperCase).local(_.level1)
)
}
and an example of a type specific flags:
test("source field type specific flag covers all subtypes of an enum and nothing else (even when the enum is nested)") {
case class Source
8000
span>(int: Int, level1: SourceEnum)
case class Dest(int: Int, level1: DestEnum)
sealed trait SourceEnum
object SourceEnum {
sealed trait NestLevel1 extends SourceEnum
sealed trait NestLevel2 extends SourceEnum
case class One(int: Int, str: String) extends NestLevel2
case class Two(int: Int, str: String, level1: SourceLevel1) extends NestLevel1
case class Three(int: Int, str: String) extends NestLevel2
}
sealed trait DestEnum
object DestEnum {
sealed trait NestLevel1 extends DestEnum
sealed trait NestLevel2 extends DestEnum
case class One(INT: Int, STR: String) extends NestLevel2
case class Two(INT: Int, STR: String, LEVEL1: DestLevel1) extends NestLevel1
case class Three(INT: Int, STR: String) extends NestLevel2
}
case class SourceLevel1(int: Int)
case class DestLevel1(int: Int)
assertTransformsConfigured(
Source(1, SourceEnum.Two(2, "2", SourceLevel1(3))),
Dest(1, DestEnum.Two(2, "2", DestLevel1(3)))
)(
Field.modifySourceNames(_.toUpperCase).typeSpecific[SourceEnum]
)
}
Renamer
is the thing that fuels the rename DSL:
sealed trait Renamer {
/**
* Equivalent to `String#toUpperCase`
*/
def toUpperCase: Renamer
/**
* Equivalent to `String#toLowerCase`
*/
def toLowerCase: Renamer
/**
* Equivalent to the function `(str: String) => if str == from then to else str`
*/
def rename(from: String, to: String): Renamer
/**
* Equivalent to `String#replace(target, replacement)`
*/
def replace(target: String, replacement: String): Renamer
/**
* Equivalent to the function `(str: String) => Pattern.compile(pattern).matcher(str).replaceAll(replacement)`
*/
def regexReplace(pattern: String, replacement: String): Renamer
/**
* Equivalent to `String#stripPrefix(prefix)`
*/
def stripPrefix(prefix: String): Renamer
/**
* Equivalent to `String#stripSuffix(suffix)`
*/
def stripSuffix(suffix: String): Renamer
/**
* Equivalent to `String#capitalize`
*/
def capitalize: Renamer
}
- Most of the API has finally had scaladoc with examples added to it
What's Changed
- Update sbt-typelevel-ci-release, ... to 0.7.5 by @scala-steward in #228
- Update sbt-scalajs, scalajs-library_2.13, ... to 1.18.1 by @scala-steward in #230
- Update munit to 1.0.4 by @scala-steward in #229
- Update munit to 1.1.0 by @scala-steward in #237
- Update sbt-typelevel-ci-release, ... to 0.7.6 by @scala-steward in #236
- Update scalafmt-core to 3.8.5 by @scala-steward in #235
- Update sbt-scalafmt to 2.5.4 by @scala-steward in #234
- Update sbt-typelevel-ci-release, ... to 0.7.7 by @scala-steward in #240
- Update scalafmt-core to 3.8.6 by @scala-steward in #239
- Update sbt-scalajs, scalajs-library_2.13, ... to 1.18.2 by @scala-steward in #238
- Update sbt-scalafix to 0.14.0 by @scala-steward in #233
- Update scala3-library, ... to 3.3.5 by @scala-steward in #241
- Update sbt-mdoc to 2.6.3 by @scala-steward in #242
- Update scalafmt-core to 3.9.0 by @scala-steward in #244
- Update sbt-mdoc to 2.6.4 by @scala-steward in #243
- Update sbt-scalafix to 0.14.2 by @scala-steward in #245
- Update scalafmt-core to 3.9.1 by @scala-steward in #246
- Update auxlib, clib, javalib, nativelib, ... to 0.5.7 by @scala-steward in #247
- Update scalafmt-core to 3.9.2 by @scala-steward in #248
- Update sbt, scripted-plugin to 1.10.8 by @scala-steward in #249
- Update sbt, scripted-plugin to 1.10.10 by @scala-steward in #250
- [Issue #75] Add support for field/case renames via flags by @arainko in #252
- Update scalafmt-core to 3.9.3 by @scala-steward in #251
- Update scalafmt-core to 3.9.4 by @scala-steward in #253
- Add scaladoc to
Field
,Case
,Renamer
by @arainko in #254 Transformer
scaladoc, moreRenamer
methods, additional tests by @arainko in #255- Update sbt, scripted-plugin to 1.10.11 by @scala-steward in #256
- Update sbt-mdoc to 2.6.5 by @scala-steward in #257
- Rough renames docs by @arainko in #258
Full Changelog: v0.2.7...v0.2.8
ducktape 0.2.7
ducktape 0.2.7
This release brings two new config options: Field.computedDeep
and Field.fallibleComputedDeep
that allows you to compute a field by using the closest possible source value:
case class SourceToplevel1(level1: Option[SourceLevel1])
case class SourceLevel1(level2: Option[SourceLevel2])
case class SourceLevel2(level3: SourceLevel3)
case class SourceLevel3(int: Int)
case class DestToplevel1(level1: Option[DestLevel1])
case class DestLevel1(level2: Option[DestLevel2])
case class DestLevel2(level3: Option[DestLevel3])
case class DestLevel3(int: Long)
val source = SourceToplevel1(Some(SourceLevel1(Some(SourceLevel2(SourceLevel3(1))))))
val expected = DestToplevel1(Some(DestLevel1(Some(DestLevel2(Some(DestLevel3(11)))))))
assertTransformsConfigured(source, expected)(
Field.computedDeep(_.level1.element.level2.element.level3.element.int, (int: Int) => int.toLong + 10)
)
while also 'cutting through' Options, collections and other things that disallowed using 'Field.computed' in the past.
What's Changed
- intial set of scaladoc (only for syntax methods for now) by @arainko in #216
- disable bsp for non-jvm projects by @arainko in #218
- Update sbt, scripted-plugin to 1.10.4 by @scala-steward in #217
- [Issue 199] Add
Field.computedDeep
andField.fallibleComputedDeep
by @arainko in #220 - Update sbt, scripted-plugin to 1.10.5 by @scala-steward in #221
- Update auxlib, clib, javalib, nativelib, ... to 0.5.6 by @scala-steward in #222
- Update sbt, scripted-plugin to 1.10.6 by @scala-steward in #223
- Update munit to 1.0.3 by @scala-steward in #224
- Update sbt-mdoc to 2.6.2 by @scala-steward in #225
- Field renames - preliminary work by @arainko in #226
- Update sbt, scripted-plugin to 1.10.7 by @scala-steward in #227
Full Changelog: v0.2.6...v0.2.7
ducktape 0.2.6
This is a bugfix release that fixes a compiletime issue where the wrong owners were used for a lambda in fallible accumulating transformations which sometimes manifested itself in an error after macro expansion.
It also fixes a runtime issue that resulted in an exception being thrown when directly configuring coproduct cases with Field
configs.
What's Changed
- Update README.md by @arainko in #200
- Update sbt-typelevel-ci-release, ... to 0.7.3 by @scala-steward in #201
- Update munit to 1.0.2 by @scala-steward in #202
- Update sbt-mdoc to 2.6.0 by @scala-steward in #204
- Update sbt to 1.10.2 by @scala-steward in #203
- Update sbt-mdoc to 2.6.1 by @scala-steward in #205
- [Issue#188] change the test for F-unwrapping, clarify the docs by @arainko in #209
- Update scala3-library, ... to 3.3.4 by @scala-steward in #208
- Update sbt-scalafix to 0.13.0 by @scala-steward in #206
- Update sbt-scalajs, scalajs-library_2.13, ... to 1.17.0 by @scala-steward in #207
- Update sbt-typelevel-ci-release, ... to 0.7.4 by @scala-steward in #210
- [Issue-211] by @arainko in #212
- Fix warnings by @arainko in #213
- reenable -Xcheck-macros, fix owner issues by @arainko in #214
- Update sbt, scripted-plugin to 1.10.3 by @scala-steward in #215
Full Changelog: v0.2.5...v0.2.6
ducktape 0.2.5
This release removes a limitation of transformation configs that disallowed using them like lenses for updates, eg. the following:
case class Person(age: Int, name: String)
Person(1, "Joe").into[Person].transform(Field.const(_.age, 23))
would previously fail to compile because the transformation underneath was an identity transformation (i.e. not a configurable product-to-product transformation) - this has now been lifted and code like the above now works as you'd expect.
What's Changed
- Update scalafmt-core to 3.8.3 by @scala-steward in #192
- update scalafmt and scalafix, run both by @arainko in #185
- [Issue 179] lens like configs by @arainko in #193
- Update munit to 1.0.1 by @scala-steward in #194
- [Issue-195] Improve error message in case a
Factory
instance is missing when transforming between collections by @arainko in #198 - Update auxlib, clib, javalib, nativelib, ... to 0.5.5 by @scala-steward in #196
Full Changelog: v0.2.4...v0.2.5
ducktape 0.2.4
ducktape 0.2.4
This a bugfix release.
Special-cased Option
transformations shouldn't get dropped when doing fallible transformations with a Mode[Option]
in scope.
Additionally, match-type-based tuple transformations are now bulletproof to most of the quirks of match types so stuff like tuple concatenation shouldn't throw it off.
What's Changed
- Tuple docs by @arainko in #184
- Update sbt-typelevel-ci-release, ... to 0.7.2 by @scala-steward in #186
- Make
Context.current
a transparent inline to make it work under Scala 3.4.+ by @WojciechMazur in #189 - [Issues #187 and #190] Make F-unwrapping not interfere with special-cased Option transformations and make transformations more bulletproof in relation to match types by @arainko in #191
Full Changelog: v0.2.3...v0.2.4
ducktape 0.2.3
ducktape 0.2.3
This release brings the ability to transform tuples in all kind of ways (tuple-to-tuple, product-to-tuple, tuple-to-product), so stuff like this is now possible:
import io.github.arainko.ducktape.*
case class Source(field1: Int, field2: List[Int], field3: Int, field4: Int)
Source(1, List(2, 2, 2), 3, 4).to[(Int, Vector[Int], Option[Int])]
// res18: Tuple3[Int, Vector[Int], Option[Int]] = (
// 1,
// Vector(2, 2, 2),
// Some(value = 3)
// )
(1, List(2, 2, 2), 3, 4).to[Source]
// res19: Source = Source(
// field1 = 1,
// field2 = List(2, 2, 2),
// field3 = 3,
// field4 = 4
// )
The newly added ability for F-unwrapping
(i.e. lifting the wrapper type into the 'outside' of a fallible transformation) in conjuction with tuple transformation enables some fantastic use cases for match types, like the ability to 'traverse' a tuple:
import io.github.arainko.ducktape.*
val source =
(
Right(1),
Right("str"),
Right(List(3, 3, 3)),
Right(4)
)
Mode.Accumulating.either[String, List].locally {
source.fallibleTo[Tuple.InverseMap[source.type, Mode.current.Self]]
}
// res0: Either[List[String], *:[Int, *:[String, *:[List[Int], *:[Int, EmptyTuple]]]]] = Right(
// value = (1, "str", List(3, 3, 3), 4)
// )
...which is really just the tip of the iceberg - stay tuned for a more in-depth look on how this mechanism can be leveraged in other use cases.
Head on over to the docs on configuring tuples to see how the configuration DSL works with tuples.
What's Changed
- Update README.md by @arainko in #172
- Update nscplugin, sbt-scala-native to 0.5.3 by @scala-steward in #171
- Refactor
Debug
by @arainko in #174 - Update sbt-mdoc to 2.5.3 by @scala-steward in #175
- Update auxlib, clib, javalib, nativelib, ... to 0.5.4 by @scala-steward in #176
- [Issue 154] Tuple transformations by @arainko in #177
- Update sbt to 1.10.1 by @scala-steward in #180
- Update sbt-mdoc to 2.5.4 by @scala-steward in #181
- [Issue 178] F-unwrapping by @arainko in #182
- F-unwrapping cleanup by @arainko in #183
Full Changelog: v0.2.2...v0.2.3
ducktape 0.2.2
This release brings back lints for configs that override each other, for example:
final case class FieldSource(additionalArg: String, str: String)
val fieldSource = FieldSource("str-sourced", "str2")
val expected = TestClassWithAdditionalString(1, "str2", "str-computed")
testClass
.into[TestClassWithAdditionalString]
.transform(
Field.allMatching(fieldSource),
Field.allMatching(fieldSource),
)
will result in a compilation warning that looks like this:
Configs for:
* TestClassWithAdditionalString.str
* TestClassWithAdditionalString.additionalArg
are being overriden by Field.allMatching(fieldSource) @ AppliedBuilderSuite.scala:185:41
It also introduces Mode#locally
that allows you to use a Mode in a scope of a function so that it doesn't need to be bound to a given.
What's Changed
- [Issue#142] config linting by @arainko in #159
- Update munit to 1.0.0 by @scala-steward in #168
- Add 'Mode#locally' by @arainko in #169
Full Changelog: v0.2.1...v0.2.2
ducktape 0.2.1
This release adds a lint that rejects calls to non-Transformer.define* family of methods in given Transformer declarations (#165 )
What's Changed
- Update README.md versions by @arainko in #152
- Update scalafmt-core to 3.8.1 by @scala-steward in #153
- Update sbt-typelevel-ci-release, ... to 0.7.0 by @scala-steward in #160
- Update munit to 1.0.0-RC1 by @scala-steward in #161
- Update sbt to 1.10.0 by @scala-steward in #163
- Update sbt-typelevel-ci-release, ... to 0.7.1 by @scala-steward in #164
- [Issue#165] reject calls to _.to and _.into.transform (and their fallible counterparts) in given Transformer definitions by @arainko in #167
Full Changelog: v0.2.0...v0.2.1
ducktape 0.2.0
ducktape 0.2.0
First of all, head on over to the new docs site.
The library has been rebuilt from the ground-up to not rely on automatic derivation of Transformer
and Transformer.Fallible
and enable further development of more advanced features (the examples of which are nested configurations and support for regional configs like Field.fallbackToDefault
- with more still to come). All of that should result in a much higher quality of code that gets generated underneath (for example, fallible transformations do not generate interim Transformer.Fallible
instances anymore).
While this release is not binary-compatible with ducktape 0.1.x
, it's aiming to be as source-compatible as possible. All the known breakages are documented in the Coming from 0.1.x section of the docs. There are also a bunch of deprecated forwarders with (hopefully) user-friendly tips on how to make the deprecation warnings go away.
New highlight features
-
ability to configure deeply nested transformations without having to resort to building out new
Transformer
instances, best showcased in theConfiguring transformations
section of the docs -
revamped error reporting - errors are now accumulated and shown at once and are supposed to be actionable e.g. in case of a field that's missing it'll give the user a hint and a path to that field for usage in one of the configuration options, for example:
No field 'name' found in MdocApp0.this.wire.PaymentMethod.Card @ Person.paymentMethods.element.at[MdocApp0.this.domain.Payment.Card].name
More information is available in the
Motivating example
section of the docs. -
new flavor of configuration options - regional configs:
Field.fallbackToDefault
for falling back to default values in cases where a transformation couldn't be derived,Field.fallbackToNone
for falling back toNone
forOption
fields for which a transformation couldn't be derived
Regional configs can be made to work in only user-selected subregions of the transformations, eg.:
Field.fallbackToDefault.regional(_.field1.at[Case1])
will make it apply only 'below'
field1
at it'sCase1
subtype. More info and examples are available inConfiguring transformations
What's Changed
- Update versions in README by @arainko in #141
- Update sbt-typelevel-ci-release to 0.6.7 by @scala-steward in #148
- Update scalafmt-core to 3.8.0 by @scala-steward in #147
- Update munit to 1.0.0-M11 by @scala-steward in #146
- Update scala3-library, ... to 3.3.3 by @scala-steward in #145
- Update sbt-scalajs, scalajs-library_2.13, ... to 1.16.0 by @scala-steward in #144
Full Changelog: v0.2.0-RC1...v0.2.0