diff --git a/.gitignore b/.gitignore index 9b5daf9..fbe1a0a 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,5 @@ target/ .vscode .ammonite .bsp -*.iml \ No newline at end of file +*.iml +.DS_Store diff --git a/.mill-jvm-opts b/.mill-jvm-opts new file mode 100644 index 0000000..aa011f6 --- /dev/null +++ b/.mill-jvm-opts @@ -0,0 +1 @@ +-Xss10m diff --git a/AUTOCONFIG.md b/AUTOCONFIG.md index 66fc217..eb9f64b 100644 --- a/AUTOCONFIG.md +++ b/AUTOCONFIG.md @@ -5,7 +5,7 @@ Rather than implementing `ToJson` and `FromJson` by hand, you can generate them @@ -17,8 +17,8 @@ If you like you can even skip the declaration by mixing in `AutoToJson` or ## Modifying field names with annotations @@ -28,7 +28,7 @@ You can change the name of a field being read to/from JSON using the `@JsonName` @@ -39,7 +39,7 @@ If your case class has optional parameters then you can use their default values @@ -52,6 +52,6 @@ By providing an instance of `NullPointerBehavior` in the scope of your `ToJson` diff --git a/USERGUIDE.md b/USERGUIDE.md index 9226b52..7157b98 100644 --- a/USERGUIDE.md +++ b/USERGUIDE.md @@ -21,7 +21,7 @@ @@ -29,7 +29,7 @@ @@ -38,7 +38,7 @@ @@ -48,7 +48,7 @@ @@ -60,7 +60,7 @@ You can use ninny's dynamic update syntax easly to replace values way down in th @@ -69,7 +69,7 @@ You can use ninny's dynamic update syntax easly to replace values way down in th @@ -78,7 +78,7 @@ You can use ninny's dynamic update syntax easly to replace values way down in th @@ -90,7 +90,7 @@ automatically using diff --git a/build.sc b/build.sc index 2e13b74..4a0587d 100644 --- a/build.sc +++ b/build.sc @@ -9,7 +9,7 @@ import $file.forProductN val `2.12` = "2.12.15" val `2.13` = "2.13.10" -val `3` = "3.1.0" +val `3` = "3.3.4" val scalaTest = ivy"org.scalatest::scalatest:3.2.10" val json4sVersion = Map(4 -> "4.0.6", 3 -> "3.6.12") @@ -47,9 +47,13 @@ class Ninny(val crossScalaVersion: String) "-feature", "-unchecked", "-deprecation" - ) ++ (if (crossScalaVersion != `3`) - Seq("-Ywarn-macros:after", "-Ywarn-unused") - else None) + ) ++ ( + if (crossScalaVersion != `3`) Seq("-Ywarn-macros:after", "-Ywarn-unused") + else None + ) ++ ( + if (crossScalaVersion == `2.12`) Seq("-language:higherKinds") + else None + ) def ivyDeps = Agg( diff --git a/ninny/src-3/nrktkt/ninny/Annotation.scala b/ninny/src-3/nrktkt/ninny/Annotation.scala new file mode 100644 index 0000000..bfa62bb --- /dev/null +++ b/ninny/src-3/nrktkt/ninny/Annotation.scala @@ -0,0 +1,48 @@ +package nrktkt.ninny + +import scala.quoted._ + +object Annotation { + type Aux[A, O] = Annotation[A] { type Out = O } + + transparent inline given derived[T]: Annotation[T] = + ${genAnnotationImpl[T]} + + def genAnnotationImpl[T: Type](using Quotes): Expr[Annotation[T]] = { + import quotes.reflect._ + val classSymbol = TypeRepr.of[T].typeSymbol + val objectSymbol = classSymbol.companionModule + val tycons = TypeRepr.of[scala.*:[_, _]] match + case AppliedType(a, _) => a + val (tupleExpr, tupleType) = classSymbol.caseFields.reverse.foldLeft[(Term, TypeRepr)]((Expr(EmptyTuple).asTerm, TypeRepr.of[EmptyTuple.type])){ case ((tpleExpr, tpleTpe), symbol) => + tpleTpe.asType match + case ('[t]) => + val valueOpt = + symbol.annotations.collect { + case Apply(Select(New(ident),_), List(Literal(StringConstant(strVal)))) => strVal + } match + case head :: next => Some(head) + case Nil => None + valueOpt match + case Some(value) => + val tpe = ConstantType(StringConstant(value)) + tpe.asType match + case '[x] => + (Apply(TypeApply(Select.unique(tpleExpr, "*:"), List(TypeTree.of[x], TypeTree.of[t])), List(Literal(StringConstant(value)))), AppliedType(tycons, List(tpe, tpleTpe))) + case None => + (Apply(TypeApply(Select.unique(tpleExpr, "*:"), List(TypeTree.of[Null], TypeTree.of[t])), List('{null}.asTerm)), AppliedType(tycons, List(TypeRepr.of[Null], tpleTpe))) + } + tupleType.asType match + case '[t] => + '{ + new Annotation[T] { + type Out = t + def apply(): Out = ${tupleExpr.asExprOf[t]} + }.asInstanceOf[Annotation.Aux[T, t]] + } + } +} +trait Annotation[A] { + type Out <: Tuple + def apply(): Out +} diff --git a/ninny/src-3/nrktkt/ninny/Auto.scala b/ninny/src-3/nrktkt/ninny/Auto.scala new file mode 100644 index 0000000..d840f4c --- /dev/null +++ b/ninny/src-3/nrktkt/ninny/Auto.scala @@ -0,0 +1,41 @@ +package nrktkt.ninny + +import scala.deriving.Mirror + +trait AutoToJson { + implicit inline def lgToJson[ + A <: Product, + OverridingNames <: Tuple](implicit + mirror: Mirror.ProductOf[A], + names: Annotation.Aux[A, OverridingNames]): ToSomeJson[A] = + ToJsonAuto.labelledGenericToJson.toJson +} + +trait AutoFromJson { + implicit inline def lgFromJson[ + A <: Product, + Values <: Tuple, + Defaults <: Tuple, + OverridingNames <: Tuple, + Size <: Numlike + ](implicit + mirror: Mirror.ProductOf[A], + ev: Size =:= Auto.WrappedSize[mirror.MirroredElemTypes], + defaults: Defaults.Aux[A, Defaults], + annotation: Annotation.Aux[A, OverridingNames], + from: (Sized[List[String], Size], Defaults) => FromJson[Values] + ): FromJson[A] = FromJsonAuto.labelledGenericFromJson.fromJson +} + +object Auto extends AutoToJson with AutoFromJson { + private[ninny] type OverrideNames[Names <: Tuple, Overriden <: Tuple] <: Tuple = + (Names, Overriden) match + case (aHead *: aTail, Null *: bTail) => aHead *: OverrideNames[aTail, bTail] + case (aHead *: aTail, str *: bTail) => str *: OverrideNames[aTail, bTail] + case (EmptyTuple, _) => EmptyTuple + + private[ninny] type WrappedSize[T <: Tuple] = T match + case a *: EmptyTuple => Added[Zero] + case a *: b => Added[WrappedSize[b]] + +} diff --git a/ninny/src-3/nrktkt/ninny/DefaultOptions.scala b/ninny/src-3/nrktkt/ninny/DefaultOptions.scala new file mode 100644 index 0000000..52475ec --- /dev/null +++ b/ninny/src-3/nrktkt/ninny/DefaultOptions.scala @@ -0,0 +1,39 @@ +package nrktkt.ninny +import scala.quoted._ + +object DefaultOptions { + type Aux[A, O] = DefaultOptions[A] { type Out = O } + + transparent inline given derived[T]: DefaultOptions[T] = + ${genDefaultsImpl[T]} + + def genDefaultsImpl[T: Type](using Quotes): Expr[DefaultOptions[T]] = { + import quotes.reflect._ + val classSymbol = TypeRepr.of[T].typeSymbol + val objectSymbol = classSymbol.companionModule + val tycons = TypeRepr.of[scala.*:[_, _]] match + case AppliedType(a, _) => a + val (tupleExpr, tupleType) = classSymbol.caseFields.zipWithIndex.reverse.foldLeft[(Term, TypeRepr)]((Expr(EmptyTuple).asTerm, TypeRepr.of[EmptyTuple.type])){ case ((tpleExpr, tpleTpe), (symbol, idx)) => + (tpleTpe.asType, TypeRepr.of[T].memberType(symbol).asType) match + case ('[t], '[g]) => + if symbol.flags.is(Flags.HasDefault) then + val value = objectSymbol.declaredMethod("$lessinit$greater$default$" + (idx + 1)).head + val expr = '{Some(${Select(Ref(objectSymbol), value).asExprOf[g]})} + (Apply(TypeApply(Select.unique(tpleExpr, "*:"), List(TypeTree.of[Option[g]], TypeTree.of[t])), List(expr.asTerm)), AppliedType(tycons, List(TypeRepr.of[Option[g]], tpleTpe))) + else + (Apply(TypeApply(Select.unique(tpleExpr, "*:"), List(TypeTree.of[Option[g]], TypeTree.of[t])), List('{None}.asTerm)), AppliedType(tycons, List(TypeRepr.of[Option[g]], tpleTpe))) + } + tupleType.asType match + case '[t] => + '{ + new DefaultOptions[T] { + type Out = t + def apply(): Out = ${tupleExpr.asExprOf[t]} + }.asInstanceOf[DefaultOptions.Aux[T, t]] + } + } +} +trait DefaultOptions[A] { + type Out <: Tuple + def apply(): Out +} diff --git a/ninny/src-3/nrktkt/ninny/FromJsonAutoImpl.scala b/ninny/src-3/nrktkt/ninny/FromJsonAutoImpl.scala index 625d6de..4251c5a 100644 --- a/ninny/src-3/nrktkt/ninny/FromJsonAutoImpl.scala +++ b/ninny/src-3/nrktkt/ninny/FromJsonAutoImpl.scala @@ -1,3 +1,59 @@ package nrktkt.ninny -trait FromJsonAutoImpl {} +import scala.annotation.nowarn +import scala.deriving.Mirror +import nrktkt.ninny.DefaultOptions._ +import scala.compiletime.ops.int +import scala.compiletime.ops.int._ + +trait FromJsonAutoImpl { + + implicit def useDefaults[A, O <: Tuple](implicit + defaults: DefaultOptions.Aux[A, O] + ): Defaults.Aux[A, O] = + new Defaults[A] { + type Out = O + def apply() = defaults() + } + + implicit inline def labelledGenericFromJson[ + A <: Product, + Values <: Tuple, + Defaults <: Tuple, + OverridingNames <: Tuple, + Size <: Numlike + ](implicit + mirror: Mirror.ProductOf[A], + ev: Size =:= Auto.WrappedSize[mirror.MirroredElemTypes], + defaults: Defaults.Aux[A, Defaults], + annotation: Annotation.Aux[A, OverridingNames], + from: (Sized[List[String], Size], Defaults) => FromJson[Values] + ): FromJsonAuto[A] = { + val names = Sized[A, OverridingNames, Size](mirror) + val fromJson = from(names, defaults()).map(mirror.fromProduct(_)) + new FromJsonAuto[A](fromJson) + } + +} + +trait Defaults[A] { + type Out <: Tuple + def apply(): Out +} + +object Defaults { + type Aux[A, O <: Tuple] = Defaults[A] { type Out = O } + + implicit def ignoreDefaults[A, O <: Tuple](implicit + defaults: DefaultOptions.Aux[A, O], + ): Defaults.Aux[A, O] = + new Defaults[A] { + type Out = O + def apply() = + def makeEmptyTuple(tuple: Tuple): Tuple = + tuple match + case head *: tail => None *: makeEmptyTuple(tail) + case EmptyTuple => EmptyTuple + makeEmptyTuple(defaults()).asInstanceOf[Out] + } +} diff --git a/ninny/src-3/nrktkt/ninny/JsonName.scala b/ninny/src-3/nrktkt/ninny/JsonName.scala new file mode 100644 index 0000000..4299a5b --- /dev/null +++ b/ninny/src-3/nrktkt/ninny/JsonName.scala @@ -0,0 +1,5 @@ +package nrktkt.ninny + +import scala.annotation.StaticAnnotation + +case class JsonName(name: String) extends StaticAnnotation diff --git a/ninny/src-3/nrktkt/ninny/ToJsonAutoImpl.scala b/ninny/src-3/nrktkt/ninny/ToJsonAutoImpl.scala index d6c0cf1..a9b591f 100644 --- a/ninny/src-3/nrktkt/ninny/ToJsonAutoImpl.scala +++ b/ninny/src-3/nrktkt/ninny/ToJsonAutoImpl.scala @@ -1,5 +1,19 @@ package nrktkt.ninny +import scala.deriving.Mirror +import scala.deriving.Mirror.Sum +import scala.collection.View.Zip + trait ToJsonAutoImpl { - + + implicit inline def labelledGenericToJson[A <: Product, OverridingNames <: Tuple](using mirror: Mirror.ProductOf[A], annotations: Annotation.Aux[A, OverridingNames]): ToJsonAuto[A] = { + new ToJsonAuto[A]((a: A) => { + val keys = scala.compiletime.constValueTuple[Auto.OverrideNames[mirror.MirroredElemLabels, OverridingNames]] + val values = Tuple.fromProductTyped(a) + val zip = keys.zip(values) + + scala.compiletime.summonInline[ToSomeJsonObject[Tuple.Zip[Auto.OverrideNames[mirror.MirroredElemLabels, OverridingNames], mirror.MirroredElemTypes]]].toSome(zip.asInstanceOf[Tuple.Zip[Auto.OverrideNames[mirror.MirroredElemLabels, OverridingNames], mirror.MirroredElemTypes]]) + }) + } + } diff --git a/ninny/src-3/nrktkt/ninny/VersionSpecificFromJsonInstances.scala b/ninny/src-3/nrktkt/ninny/VersionSpecificFromJsonInstances.scala index dcd173d..b616165 100644 --- a/ninny/src-3/nrktkt/ninny/VersionSpecificFromJsonInstances.scala +++ b/ninny/src-3/nrktkt/ninny/VersionSpecificFromJsonInstances.scala @@ -1,5 +1,75 @@ package nrktkt.ninny +import scala.util.Success +import scala.annotation.nowarn +import scala.compiletime.ops.int.{+, -, S} + +import nrktkt.ninny.ast.JsonValue +import scala.util.Failure +import scala.deriving.Mirror +import scala.util.Try + trait VersionSpecificFromJsonInstances { - + + implicit def recordFromJson[ + Head, + DefaultHead <: Option[Head], + Tail <: Tuple, + DefaultTail <: Tuple, + TailLen <: Numlike + ](implicit + headFromJson: FromJson[Head], + tailFromJson: ( + Sized[List[String], TailLen], + DefaultTail + ) => FromJson[Tail] + ): ( + Sized[List[String], Added[TailLen]], + DefaultHead *: DefaultTail + ) => FromJson[Head *: Tail] = (names, defaults) => { + val defaultHeadFromJson: FromJson[Head] = maybe => + (maybe, defaults.head) match { + case (maybe: Some[JsonValue], _) => headFromJson.from(maybe) + case (_, default: Some[Head]) => Success(default.value) + case _ => headFromJson.from(None) + } + + FromJson.fromSome { json => + for { + head <- defaultHeadFromJson + .from(json / Sized.value(names).head) + .recoverWith { + case e: JsonFieldException => + Failure( + new JsonFieldException( + e.message, + s"${Sized.value(names).head}.${e.field}", + e + ) + ) + case e: Exception => + Failure(new JsonFieldException(e.getMessage, Sized.value(names).head, e)) + } + tail <- tailFromJson(names.tail, defaults.tail).from(json) + } yield head *: tail + } + } + + implicit val emptyTupleFromJson: ((Sized[List[String], Zero]), EmptyTuple) => FromJson[EmptyTuple] = + (_, _) => _ => Success(EmptyTuple) } + +opaque type Sized[l <: List[String], Size] = List[String] + +object Sized { + def value[L <: List[String], Size](a: Sized[L, Size]): List[String] = a + inline def apply[T <: Product, OverridingNames <: Tuple, Size <: Numlike](mirror: Mirror.ProductOf[T]): Sized[List[String], Size] = + scala.compiletime.constValueTuple[Auto.OverrideNames[mirror.MirroredElemLabels, OverridingNames]].asInstanceOf[Tuple].toList.asInstanceOf[List[String]] +} + +extension [L <: List[String], Size <: Numlike] (s: Sized[L, Added[Size]]) def tail: Sized[L, Size] = s.tail + + +type Numlike +type Added[T] <: Numlike +type Zero <: Numlike \ No newline at end of file diff --git a/ninny/src-3/nrktkt/ninny/VersionSpecificToJsonInstances.scala b/ninny/src-3/nrktkt/ninny/VersionSpecificToJsonInstances.scala index 172909e..dcdf418 100644 --- a/ninny/src-3/nrktkt/ninny/VersionSpecificToJsonInstances.scala +++ b/ninny/src-3/nrktkt/ninny/VersionSpecificToJsonInstances.scala @@ -1,5 +1,29 @@ package nrktkt.ninny +import nrktkt.ninny.ast.JsonObject +import nrktkt.ninny.NullPointerBehavior +import nrktkt.ninny.NullPointerBehavior.Handle + trait VersionSpecificToJsonInstances { - + implicit def recordToJson[V, G <: String, Tail <: Tuple](implicit + valueToJson: ToJson[V], + tailToJson: ToSomeJsonObject[Tail], + nullPointerBehavior: NullPointerBehavior + ): ToSomeJsonObject[(G, V) *: Tail] = { + ToJson { case (name, value) *: tail => + val tailJson = tailToJson.toSome(tail) + nullPointerBehavior match { + case Handle(behavior) if value == null => + tailJson + (name -> behavior()) + case _ => + val maybeValueJson = valueToJson.to(value) + maybeValueJson match { + case Some(valueJson) => tailJson + (name -> valueJson) + case None => tailJson + } + } + } + } + + implicit val hNilToJson: ToSomeJsonObject[EmptyTuple] = _ => JsonObject(Map.empty) } diff --git a/ninny/src/nrktkt/ninny/FromJsonInstances.scala b/ninny/src/nrktkt/ninny/FromJsonInstances.scala index eaa09e3..3e30eb8 100644 --- a/ninny/src/nrktkt/ninny/FromJsonInstances.scala +++ b/ninny/src/nrktkt/ninny/FromJsonInstances.scala @@ -233,9 +233,6 @@ trait FromJsonInstances object FromJsonInstances extends FromJsonInstances trait LowPriorityFromJsonInstances { - // this roundabout way to import compiler flag for higher kinded types avoids a deprecation warning when building for 2.13 - protected implicit lazy val hkhack: languageFeature.higherKinds.type = - scala.languageFeature.higherKinds implicit def collectionFromJson[F[_], A](implicit factory: Factory[A, F[A]], diff --git a/ninny/src-2/nrktkt/ninny/NullPointerBehavior.scala b/ninny/src/nrktkt/ninny/NullPointerBehavior.scala similarity index 87% rename from ninny/src-2/nrktkt/ninny/NullPointerBehavior.scala rename to ninny/src/nrktkt/ninny/NullPointerBehavior.scala index 19600b8..57e5979 100644 --- a/ninny/src-2/nrktkt/ninny/NullPointerBehavior.scala +++ b/ninny/src/nrktkt/ninny/NullPointerBehavior.scala @@ -14,5 +14,5 @@ object NullPointerBehavior { val someNull = Some(JsonNull) () => someNull } - implicit def default = PassThrough + implicit def default: NullPointerBehavior = PassThrough } diff --git a/ninny/test/src-3/nrktkt/ninny/userguide/ForProductN.scala b/ninny/test/src-3/nrktkt/ninny/userguide/ForProductN.scala new file mode 100644 index 0000000..f7ae5a2 --- /dev/null +++ b/ninny/test/src-3/nrktkt/ninny/userguide/ForProductN.scala @@ -0,0 +1,25 @@ +package nrktkt.ninny.userguide + +import nrktkt.ninny._ + +object ForProductN extends App { +// format: Off + +case class Address(street: String, zip: String) + +val fromJson: FromJson[Address] = + FromJson.forProduct2("street", "zip_code")(Address.apply) + +implicit +val toJson: ToSomeJson[Address] = + ToJson.forProduct2("street", "zip_code")(Tuple.fromProductTyped) + +val toAndFromJson: ToAndFromJson[Address] = + ToAndFromJson.forProduct2("street", "zip_code")( + Address.apply, Tuple.fromProductTyped + ) + +Address(street = "710 Ashbury St", zip = "94117").toSomeJson +// {"street":"710 Ashbury St","zip_code":"94117"} + +} diff --git a/ninny/test/src-2/nrktkt/ninny/JsonSpec.scala b/ninny/test/src/nrktkt/ninny/JsonSpec.scala similarity index 90% rename from ninny/test/src-2/nrktkt/ninny/JsonSpec.scala rename to ninny/test/src/nrktkt/ninny/JsonSpec.scala index ea1f7f1..8466ab7 100644 --- a/ninny/test/src-2/nrktkt/ninny/JsonSpec.scala +++ b/ninny/test/src/nrktkt/ninny/JsonSpec.scala @@ -30,11 +30,11 @@ class JsonSpec val sampleValues = obj( "string" ~> """¯\_(ツ)_/¯""", "number" ~> 1.79e308, - "bool" ~> true, - "false" ~> false, - "null" ~> JsonNull, - "unit" ~> ((): Unit), - "some" ~> "str" + "bool" ~> true, + "false" ~> false, + "null" ~> JsonNull, + "unit" ~> ((): Unit), + "some" ~> "str" ) val sampleArray = @@ -44,7 +44,7 @@ class JsonSpec JsonObject( sampleValues.values ++ obj( "object" ~> sampleValues, - "array" ~> sampleArray + "array" ~> sampleArray ).values ) @@ -95,11 +95,11 @@ class JsonSpec val sampleValuesAst = obj( "string" ~> """¯\_(ツ)_/¯""", "number" ~> 1.79e308, - "bool" ~> true, - "false" ~> false, - "null" ~> JsonNull, - "unit" ~> ((): Unit), - "some" ~> Some("str") + "bool" ~> true, + "false" ~> false, + "null" ~> JsonNull, + "unit" ~> ((): Unit), + "some" ~> Some("str") ) val sampleValuesObj = SampleValues( @@ -184,39 +184,39 @@ class JsonSpec val exampleObjectAst = obj( "Image" ~> obj( - "Width" ~> 800, + "Width" ~> 800, "Height" ~> 600, - "Title" ~> "View from 15th Floor", + "Title" ~> "View from 15th Floor", "Thumbnail" ~> obj( - "Url" ~> "http://www.example.com/image/481989943", + "Url" ~> "http://www.example.com/image/481989943", "Height" ~> 125, - "Width" ~> 100 + "Width" ~> 100 ), "Animated" ~> false, - "IDs" ~> arr(116, 943, 234, 38793) + "IDs" ~> arr(116, 943, 234, 38793) ) ) val exampleArrayAst = arr( obj( "precision" ~> "zip", - "Latitude" ~> 37.7668, + "Latitude" ~> 37.7668, "Longitude" ~> -122.3959, - "Address" ~> "", - "City" ~> "SAN FRANCISCO", - "State" ~> "CA", - "Zip" ~> "94107", - "Country" ~> "US" + "Address" ~> "", + "City" ~> "SAN FRANCISCO", + "State" ~> "CA", + "Zip" ~> "94107", + "Country" ~> "US" ), obj( "precision" ~> "zip", - "Latitude" ~> 37.371991, + "Latitude" ~> 37.371991, "Longitude" ~> -122.026020, - "Address" ~> "", - "City" ~> "SUNNYVALE", - "State" ~> "CA", - "Zip" ~> "94085", - "Country" ~> "US" + "Address" ~> "", + "City" ~> "SUNNYVALE", + "State" ~> "CA", + "Zip" ~> "94085", + "Country" ~> "US" ) ) @@ -245,13 +245,13 @@ class JsonSpec implicit val toJson: ToSomeJson[Image] = a => obj( - "Width" ~> a.Width, - "Height" ~> a.Height, - "Title" ~> a.Title, + "Width" ~> a.Width, + "Height" ~> a.Height, + "Title" ~> a.Title, "Thumbnail" ~> a.Thumbnail, - "Url" ~> a.Url, - "Animated" ~> a.Animated, - "IDs" ~> (if (a.IDs.isEmpty) None else Some(a.IDs)) + "Url" ~> a.Url, + "Animated" ~> a.Animated, + "IDs" ~> (if (a.IDs.isEmpty) None else Some(a.IDs)) ) } @@ -282,13 +282,13 @@ class JsonSpec implicit val toJson: ToSomeJson[Address] = a => obj( "precision" ~> a.precision, - "Latitude" ~> a.Latitude, + "Latitude" ~> a.Latitude, "Longitude" ~> a.Longitude, - "Address" ~> a.Address, - "City" ~> a.City, - "State" ~> a.State, - "Zip" ~> a.Zip, - "Country" ~> a.Country + "Address" ~> a.Address, + "City" ~> a.City, + "State" ~> a.State, + "Zip" ~> a.Zip, + "Country" ~> a.Country ) } @@ -512,7 +512,6 @@ class JsonSpec head :++ tail shouldEqual arr(1, 2, 3, 4, 5, 6) } - "JsonBlob" should "encode and decode to base64" in { val bytes = new Array[Byte](16) Random.nextBytes(bytes) @@ -624,7 +623,7 @@ class JsonSpec y: Int, z: Int ) - implicit val json = ToAndFromJson.auto[Big] + implicit val json: ToAndFromJson[Big] = ToAndFromJson.auto val big = Big(1, 2, 2, 1234, 1234, 124, 1234, 1234, 1234, 1234, 123, 2134, 1324, 1234, 1234, 1, 234, 1234, 14, 23, 132, 13, 431, 31, 3412, 1432) Json @@ -636,7 +635,7 @@ class JsonSpec "annotations" should "work" in { case class Example(@JsonName("baz") foo: String, bop: Int) - implicit val toFromJson = ToAndFromJson.auto[Example] + implicit val toFromJson: ToAndFromJson[Example] = ToAndFromJson.auto val example = Example("bar", 1) val js = example.toSomeJson @@ -648,9 +647,9 @@ class JsonSpec case class Example(qix: Int, foo: String = "bar") "auto from json" should "read defaults for absent fields" in { - implicit val toFromJson = { + implicit val toFromJson: ToAndFromJson[Example] = { import FromJsonAuto.useDefaults - ToAndFromJson.auto[Example] + ToAndFromJson.auto } obj("qix" ~> 1).to[Example].success.value shouldEqual Example(1) @@ -676,17 +675,17 @@ class JsonSpec if (i == null) JsonNumber(0) else JsonDouble(i.toDouble) "NullPointerBehavior" should "pass null pointers to typeclass instances by default" in { - val example = Example(null) - implicit val exampleToJson = ToJson.auto[Example] - val json = example.toSomeJson + val example = Example(null) + implicit val exampleToJson: ToSomeJsonObject[Example] = ToJson.auto + val json = example.toSomeJson (json / "i").value shouldEqual JsonNumber(0) } it should "use behavior when in scope" in { val example = Example(null) implicit val nullPointerBehavior = NullPointerBehavior.WriteNull - implicit val exampleToJson = ToJson.auto[Example] - val json = example.toSomeJson + implicit val exampleToJson: ToSomeJsonObject[Example] = ToJson.auto + val json = example.toSomeJson (json / "i").value shouldEqual JsonNull } } diff --git a/ninny/test/src-2/nrktkt/ninny/example/Example.scala b/ninny/test/src/nrktkt/ninny/example/Example.scala similarity index 100% rename from ninny/test/src-2/nrktkt/ninny/example/Example.scala rename to ninny/test/src/nrktkt/ninny/example/Example.scala diff --git a/ninny/test/src-2/nrktkt/ninny/example/Userguide.scala b/ninny/test/src/nrktkt/ninny/example/Userguide.scala similarity index 93% rename from ninny/test/src-2/nrktkt/ninny/example/Userguide.scala rename to ninny/test/src/nrktkt/ninny/example/Userguide.scala index 76663cc..aa9394f 100644 --- a/ninny/test/src-2/nrktkt/ninny/example/Userguide.scala +++ b/ninny/test/src/nrktkt/ninny/example/Userguide.scala @@ -143,7 +143,7 @@ implicit val personToJson: ToSomeJson[Person] = p => "age" ~> p.age ) -implicit val personFromJson = FromJson.fromSome[Person](json => +implicit val personFromJson: FromJson[Person] = FromJson.fromSome(json => for { first <- json.firstName.to[String] last <- json.lastName.to[String] @@ -170,11 +170,11 @@ Json.parse("").to[Person]: Try[Person] implement ToSomeJson instead of ToJson if your object always produces some kind of JSON. this is a common case. */ -implicit val addressToJson = ToAndFromJson.auto[Address] +implicit val addressToJson: ToAndFromJson[Address] = ToAndFromJson.auto -implicit val personToJson = ToJson.auto[Person] +implicit val personToJson: ToSomeJsonObject[Person] = ToJson.auto -implicit val personFromJson = FromJson.auto[Person] +implicit val personFromJson: FromJson[Person] = FromJson.auto Person( "John", @@ -207,7 +207,7 @@ Json.parse("").to[Person]: Try[Person] implicit val nullPointerBehavior = NullPointerBehavior.Handle(() => Some(JsonString("unknown"))) -implicit val addressToJson = ToJson.auto[Address] +implicit val addressToJson: ToSomeJsonObject[Address] = ToJson.auto val address = Address(zip = "94117", street = null) address.toSomeJson // {"zip": "94117", "street":"unknown"} diff --git a/ninny/test/src-2/nrktkt/ninny/userguide/Annotations.scala b/ninny/test/src/nrktkt/ninny/userguide/Annotations.scala similarity index 81% rename from ninny/test/src-2/nrktkt/ninny/userguide/Annotations.scala rename to ninny/test/src/nrktkt/ninny/userguide/Annotations.scala index 12e5138..60a9362 100644 --- a/ninny/test/src-2/nrktkt/ninny/userguide/Annotations.scala +++ b/ninny/test/src/nrktkt/ninny/userguide/Annotations.scala @@ -13,7 +13,7 @@ case class Example( int: Int ) -implicit val toFromJson = ToAndFromJson.auto[Example] +implicit val toFromJson: ToAndFromJson[Example] = ToAndFromJson.auto Example("foo", 1).toSomeJson // {"string_field":"foo", "int":1} diff --git a/ninny/test/src-2/nrktkt/ninny/userguide/DefaultValues.scala b/ninny/test/src/nrktkt/ninny/userguide/DefaultValues.scala similarity index 83% rename from ninny/test/src-2/nrktkt/ninny/userguide/DefaultValues.scala rename to ninny/test/src/nrktkt/ninny/userguide/DefaultValues.scala index 591d678..379855e 100644 --- a/ninny/test/src-2/nrktkt/ninny/userguide/DefaultValues.scala +++ b/ninny/test/src/nrktkt/ninny/userguide/DefaultValues.scala @@ -8,9 +8,9 @@ object DefaultValues extends App { case class User(name: String, admin: Boolean = false) -implicit val userFromJson = { +implicit val userFromJson: FromJson[User] = { import nrktkt.ninny.FromJsonAuto.useDefaults - FromJson.auto[User] + FromJson.auto } obj("name" --> "Alice").to[User] diff --git a/ninny/test/src-2/nrktkt/ninny/userguide/DomainFrom.scala b/ninny/test/src/nrktkt/ninny/userguide/DomainFrom.scala similarity index 100% rename from ninny/test/src-2/nrktkt/ninny/userguide/DomainFrom.scala rename to ninny/test/src/nrktkt/ninny/userguide/DomainFrom.scala diff --git a/ninny/test/src-2/nrktkt/ninny/userguide/DomainTo.scala b/ninny/test/src/nrktkt/ninny/userguide/DomainTo.scala similarity index 100% rename from ninny/test/src-2/nrktkt/ninny/userguide/DomainTo.scala rename to ninny/test/src/nrktkt/ninny/userguide/DomainTo.scala diff --git a/ninny/test/src-2/nrktkt/ninny/userguide/FullAuto.scala b/ninny/test/src/nrktkt/ninny/userguide/FullAuto.scala similarity index 91% rename from ninny/test/src-2/nrktkt/ninny/userguide/FullAuto.scala rename to ninny/test/src/nrktkt/ninny/userguide/FullAuto.scala index dc94d39..9335de9 100644 --- a/ninny/test/src-2/nrktkt/ninny/userguide/FullAuto.scala +++ b/ninny/test/src/nrktkt/ninny/userguide/FullAuto.scala @@ -2,6 +2,7 @@ package nrktkt.ninny.userguide import nrktkt.ninny.userguide.DomainFrom.Person import nrktkt.ninny.userguide.DomainFrom.Address +import nrktkt.ninny.AnySyntax object FullAuto { // format: off diff --git a/ninny/test/src-2/nrktkt/ninny/userguide/Reading.scala b/ninny/test/src/nrktkt/ninny/userguide/Reading.scala similarity index 100% rename from ninny/test/src-2/nrktkt/ninny/userguide/Reading.scala rename to ninny/test/src/nrktkt/ninny/userguide/Reading.scala diff --git a/ninny/test/src-2/nrktkt/ninny/userguide/SemiAuto.scala b/ninny/test/src/nrktkt/ninny/userguide/SemiAuto.scala similarity index 61% rename from ninny/test/src-2/nrktkt/ninny/userguide/SemiAuto.scala rename to ninny/test/src/nrktkt/ninny/userguide/SemiAuto.scala index cc2182f..98cc96f 100644 --- a/ninny/test/src-2/nrktkt/ninny/userguide/SemiAuto.scala +++ b/ninny/test/src/nrktkt/ninny/userguide/SemiAuto.scala @@ -9,9 +9,9 @@ object SemiAuto { import nrktkt.ninny._ // generate ToJson and FromJson at the same time with ToAndFromJson -implicit val toAndFromJson = ToAndFromJson.auto[Address] +implicit val toAndFromJson: ToAndFromJson[Address] = ToAndFromJson.auto // or generate them separately -implicit val fromJson = FromJson.auto[Person] -implicit val toJson = ToJson.auto[Person] +implicit val fromJson: FromJson[Person] = FromJson.auto +implicit val toJson: ToJson[Person] = ToJson.auto } diff --git a/ninny/test/src-2/nrktkt/ninny/userguide/Updating.scala b/ninny/test/src/nrktkt/ninny/userguide/Updating.scala similarity index 100% rename from ninny/test/src-2/nrktkt/ninny/userguide/Updating.scala rename to ninny/test/src/nrktkt/ninny/userguide/Updating.scala diff --git a/ninny/test/src-2/nrktkt/ninny/userguide/Writing.scala b/ninny/test/src/nrktkt/ninny/userguide/Writing.scala similarity index 100% rename from ninny/test/src-2/nrktkt/ninny/userguide/Writing.scala rename to ninny/test/src/nrktkt/ninny/userguide/Writing.scala diff --git a/old-namespace/src-3/io/github/kag0/ninny/VersionSpecificPackage.scala b/old-namespace/src-3/io/github/kag0/ninny/VersionSpecificPackage.scala index e0ed395..c483e87 100644 --- a/old-namespace/src-3/io/github/kag0/ninny/VersionSpecificPackage.scala +++ b/old-namespace/src-3/io/github/kag0/ninny/VersionSpecificPackage.scala @@ -1,3 +1,9 @@ package io.github.kag0.ninny -trait VersionSpecificPackage {} +trait VersionSpecificPackage { + val Auto = nrktkt.ninny.Auto + val FromJsonAuto = nrktkt.ninny.FromJsonAuto + + type JsonName = nrktkt.ninny.JsonName + val JsonName = nrktkt.ninny.JsonName +}