-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
334 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
40 changes: 40 additions & 0 deletions
40
modules/upickle/src/main/scala/com/github/tarao/record4s/upickle/ArrayRecord.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
/* | ||
* Copyright 2023 record4s authors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package com.github.tarao | ||
package record4s | ||
package upickle | ||
|
||
import _root_.upickle.default.{ReadWriter, readwriter} | ||
import upickle.Record.{readDict, writeDict} | ||
|
||
object ArrayRecord { | ||
inline given readWriter[T <: Tuple](using | ||
r: RecordLike[record4s.ArrayRecord[T]], | ||
): ReadWriter[record4s.ArrayRecord[T]] = { | ||
type Types = r.ElemTypes | ||
type Labels = r.ElemLabels | ||
|
||
readwriter[ujson.Value].bimap[ArrayRecord[T]]( | ||
record => ujson.Obj(writeDict[Types, Labels](r.iterableOf(record).toMap)), | ||
json => { | ||
val dict = json.obj | ||
val iterable = readDict[Types, Labels](dict) | ||
record4s.ArrayRecord.newArrayRecord[ArrayRecord[T]](iterable) | ||
}, | ||
) | ||
} | ||
} |
89 changes: 89 additions & 0 deletions
89
modules/upickle/src/main/scala/com/github/tarao/record4s/upickle/Record.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
/* | ||
* Copyright 2023 record4s authors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package com.github.tarao | ||
package record4s | ||
package upickle | ||
|
||
import _root_.upickle.core.LinkedHashMap | ||
import _root_.upickle.default.{ | ||
ReadWriter, | ||
Reader, | ||
Writer, | ||
read, | ||
readwriter, | ||
writeJs, | ||
} | ||
|
||
import scala.compiletime.{constValue, erasedValue, summonInline} | ||
import scala.util.NotGiven | ||
|
||
object Record { | ||
private[upickle] inline def writeDict[Types, Labels]( | ||
record: Map[String, Any], | ||
res: LinkedHashMap[String, ujson.Value] = LinkedHashMap(), | ||
): LinkedHashMap[String, ujson.Value] = | ||
inline (erasedValue[Types], erasedValue[Labels]) match { | ||
case _: (EmptyTuple, EmptyTuple) => | ||
res | ||
|
||
case _: (tpe *: types, label *: labels) => | ||
val labelStr = constValue[label & String] | ||
val value = | ||
inline erasedValue[tpe] match { | ||
case _: ujson.Value => | ||
record(labelStr).asInstanceOf[ujson.Value] | ||
case _ => | ||
val writer = summonInline[Writer[tpe]] | ||
val elem = record(labelStr).asInstanceOf[tpe] | ||
writeJs[tpe](elem)(using writer) | ||
} | ||
writeDict[types, labels](record, res += (labelStr -> value)) | ||
} | ||
|
||
private[upickle] inline def readDict[Types, Labels]( | ||
dict: LinkedHashMap[String, ujson.Value], | ||
res: Vector[(String, Any)] = Vector.empty, | ||
): Vector[(String, Any)] = | ||
inline (erasedValue[Types], erasedValue[Labels]) match { | ||
case _: (EmptyTuple, EmptyTuple) => | ||
res | ||
|
||
case _: (tpe *: types, label *: labels) => | ||
val labelStr = constValue[label & String] | ||
val jsonElem = dict.getOrElse(labelStr, ujson.Null) | ||
val reader = summonInline[Reader[tpe]] | ||
val elem = read[tpe](jsonElem)(using reader) | ||
readDict[types, labels](dict, res :+ (labelStr, elem)) | ||
} | ||
|
||
inline given readWriter[R <: %](using | ||
r: RecordLike[R], | ||
nonTuple: NotGiven[R <:< Tuple], | ||
): ReadWriter[R] = { | ||
type Types = r.ElemTypes | ||
type Labels = r.ElemLabels | ||
|
||
readwriter[ujson.Value].bimap[R]( | ||
record => ujson.Obj(writeDict[Types, Labels](r.iterableOf(record).toMap)), | ||
json => { | ||
val dict = json.obj | ||
val iterable = readDict[Types, Labels](dict) | ||
record4s.Record.newMapRecord[R](iterable) | ||
}, | ||
) | ||
} | ||
} |
101 changes: 101 additions & 0 deletions
101
modules/upickle/src/test/scala/com/github/tarao/record4s/upickle/ArrayRecordSpec.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
/* | ||
* Copyright 2023 record4s authors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package com.github.tarao | ||
package record4s | ||
package upickle | ||
|
||
class ArrayRecordSpec extends helper.UnitSpec { | ||
describe("ArrayRecord.readWriter") { | ||
import upickle.ArrayRecord.readWriter | ||
|
||
describe("write") { | ||
import _root_.upickle.default.{write, writeJs} | ||
|
||
it("should write a record") { | ||
val r = record4s.ArrayRecord(name = "tarao", age = 3) | ||
write(r) shouldBe """{"name":"tarao","age":3}""" | ||
writeJs(r) shouldBe ujson.Obj( | ||
"name" -> ujson.Str("tarao"), | ||
"age" -> ujson.Num(3), | ||
) | ||
} | ||
|
||
it("should write a nested record") { | ||
val r = record4s.ArrayRecord( | ||
name = "tarao", | ||
age = 3, | ||
email = record4s.ArrayRecord(user = "tarao", domain = "example.com"), | ||
) | ||
write( | ||
r, | ||
) shouldBe """{"name":"tarao","age":3,"email":{"user":"tarao","domain":"example.com"}}""" | ||
writeJs(r) shouldBe ujson.Obj( | ||
"name" -> ujson.Str("tarao"), | ||
"age" -> ujson.Num(3), | ||
"email" -> ujson.Obj( | ||
"user" -> ujson.Str("tarao"), | ||
"domain" -> ujson.Str("example.com"), | ||
), | ||
) | ||
} | ||
} | ||
|
||
describe("read") { | ||
import _root_.upickle.default.read | ||
|
||
it("should read a record") { | ||
val json = """{"name":"tarao","age":3}""" | ||
val r = | ||
read[record4s.ArrayRecord[(("name", String), ("age", Int))]](json) | ||
r shouldStaticallyBe a[ | ||
record4s.ArrayRecord[(("name", String), ("age", Int))], | ||
] | ||
r.name shouldBe "tarao" | ||
r.age shouldBe 3 | ||
} | ||
|
||
it("should read a nested record") { | ||
val json = | ||
"""{"name":"tarao","age":3,"email":{"user":"tarao","domain":"example.com"}}""" | ||
val r = read[record4s.ArrayRecord[ | ||
( | ||
("name", String), | ||
("age", Int), | ||
( | ||
"email", | ||
record4s.ArrayRecord[(("user", String), ("domain", String))], | ||
), | ||
), | ||
]](json) | ||
r shouldStaticallyBe a[record4s.ArrayRecord[ | ||
( | ||
("name", String), | ||
("age", Int), | ||
( | ||
"email", | ||
record4s.ArrayRecord[(("user", String), ("domain", String))], | ||
), | ||
), | ||
]] | ||
r.name shouldBe "tarao" | ||
r.age shouldBe 3 | ||
r.email.user shouldBe "tarao" | ||
r.email.domain shouldBe "example.com" | ||
} | ||
} | ||
} | ||
} |
89 changes: 89 additions & 0 deletions
89
modules/upickle/src/test/scala/com/github/tarao/record4s/upickle/RecordSpec.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
/* | ||
* Copyright 2023 record4s authors | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package com.github.tarao.record4s | ||
package upickle | ||
|
||
class RecordSpec extends helper.UnitSpec { | ||
describe("Record.readWriter") { | ||
import upickle.Record.readWriter | ||
|
||
describe("write") { | ||
import _root_.upickle.default.{write, writeJs} | ||
|
||
it("should write a record") { | ||
val r = %(name = "tarao", age = 3) | ||
write(r) shouldBe """{"name":"tarao","age":3}""" | ||
writeJs(r) shouldBe ujson.Obj( | ||
"name" -> ujson.Str("tarao"), | ||
"age" -> ujson.Num(3), | ||
) | ||
} | ||
|
||
it("should write a nested record") { | ||
val r = %( | ||
name = "tarao", | ||
age = 3, | ||
email = %(user = "tarao", domain = "example.com"), | ||
) | ||
write( | ||
r, | ||
) shouldBe """{"name":"tarao","age":3,"email":{"user":"tarao","domain":"example.com"}}""" | ||
writeJs(r) shouldBe ujson.Obj( | ||
"name" -> ujson.Str("tarao"), | ||
"age" -> ujson.Num(3), | ||
"email" -> ujson.Obj( | ||
"user" -> ujson.Str("tarao"), | ||
"domain" -> ujson.Str("example.com"), | ||
), | ||
) | ||
} | ||
} | ||
|
||
describe("read") { | ||
import _root_.upickle.default.read | ||
|
||
it("should read a record") { | ||
val json = """{"name":"tarao","age":3}""" | ||
val r = read[% { val name: String; val age: Int }](json) | ||
r shouldStaticallyBe a[% { val name: String; val age: Int }] | ||
r.name shouldBe "tarao" | ||
r.age shouldBe 3 | ||
} | ||
|
||
it("should read a nested record") { | ||
val json = | ||
"""{"name":"tarao","age":3,"email":{"user":"tarao","domain":"example.com"}}""" | ||
val r = read[ | ||
% { | ||
val name: String; val age: Int; | ||
val email: % { val user: String; val domain: String } | ||
}, | ||
](json) | ||
r shouldStaticallyBe a[ | ||
% { | ||
val name: String; val age: Int; | ||
val email: % { val user: String; val domain: String } | ||
}, | ||
] | ||
r.name shouldBe "tarao" | ||
r.age shouldBe 3 | ||
r.email.user shouldBe "tarao" | ||
r.email.domain shouldBe "example.com" | ||
} | ||
} | ||
} | ||
} |