├── project ├── build.properties ├── plugins.sbt ├── GenFTupleNSyntax.scala └── Boilerplate.scala ├── .jvmopts ├── .git-blame-ignore-revs ├── docs ├── directory.conf ├── contributing-guide.md └── index.md ├── .gitignore ├── .scalafmt.conf ├── LICENSE ├── js └── src │ └── main │ ├── scala │ └── mouse │ │ └── js.scala │ ├── scala-3 │ └── mouse │ │ └── package.scala │ └── scala-2 │ └── mouse │ └── package.scala ├── jvm └── src │ ├── main │ ├── scala │ │ └── mouse │ │ │ ├── jvm.scala │ │ │ └── stringJvm.scala │ ├── scala-3 │ │ └── src │ │ │ └── main │ │ │ └── scala │ │ │ └── mouse │ │ │ └── package.scala │ └── scala-2 │ │ └── src │ │ └── main │ │ └── scala │ │ └── mouse │ │ └── package.scala │ └── test │ └── scala │ └── mouse │ └── StringJvmTests.scala ├── shared └── src │ ├── main │ ├── scala │ │ └── mouse │ │ │ ├── ftuple.scala │ │ │ ├── double.scala │ │ │ ├── MouseFunctions.scala │ │ │ ├── int.scala │ │ │ ├── partialFunction.scala │ │ │ ├── list.scala │ │ │ ├── long.scala │ │ │ ├── set.scala │ │ │ ├── try.scala │ │ │ ├── map.scala │ │ │ ├── any.scala │ │ │ ├── anyf.scala │ │ │ ├── option.scala │ │ │ ├── fnested.scala │ │ │ ├── fboolean.scala │ │ │ ├── boolean.scala │ │ │ ├── string.scala │ │ │ ├── foption.scala │ │ │ └── feither.scala │ ├── scala-2.12 │ │ └── mouse │ │ │ └── compat │ │ │ └── SortedSet.scala │ ├── scala-2.13 │ │ └── mouse │ │ │ └── compat │ │ │ └── SortedSet.scala │ ├── scala-3 │ │ └── src │ │ │ └── main │ │ │ └── scala │ │ │ └── mouse │ │ │ ├── compat │ │ │ └── SortedSet.scala │ │ │ └── all.scala │ └── scala-2 │ │ └── src │ │ └── main │ │ └── scala │ │ └── mouse │ │ └── all.scala │ └── test │ ├── scala │ └── mouse │ │ ├── PartialFunctionLiftTest.scala │ │ ├── DoubleSyntaxTest.scala │ │ ├── IntSyntaxTest.scala │ │ ├── LongSyntaxTest.scala │ │ ├── SetSyntaxTest.scala │ │ ├── ListSyntaxTest.scala │ │ ├── AnySyntaxTest.scala │ │ ├── MapSyntaxTest.scala │ │ ├── OptionSyntaxTest.scala │ │ ├── AnyFSyntaxTest.scala │ │ ├── FNestedSyntaxTest.scala │ │ ├── TrySyntaxTest.scala │ │ ├── FBooleanSyntaxTests.scala │ │ ├── BooleanSyntaxTest.scala │ │ ├── StringSyntaxTests.scala │ │ ├── FEitherSyntaxTest.scala │ │ ├── FOptionSyntaxTest.scala │ │ └── FTupleNSyntaxSuite.scala │ ├── scala-2 │ └── mouse │ │ └── MouseSuite.scala │ └── scala-3 │ └── mouse │ └── MouseSuite.scala ├── DEV.md ├── native └── src │ └── main │ ├── scala-3 │ └── mouse │ │ └── package.scala │ └── scala-2 │ └── mouse │ └── package.scala ├── .github └── workflows │ ├── clean.yml │ └── ci.yml └── README.md /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.11.7 2 | -------------------------------------------------------------------------------- /.jvmopts: -------------------------------------------------------------------------------- 1 | -Xms1G 2 | -Xmx4G 3 | -XX:+UseG1GC 4 | -Xss2m 5 | -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # Scala Steward: Reformat with scalafmt 3.9.10 2 | 6f30ea155a5441c5863a85cbcd595f1bdf40a27a 3 | -------------------------------------------------------------------------------- /docs/directory.conf: -------------------------------------------------------------------------------- 1 | laika.title = mouse 2 | laika.navigationOrder = [ 3 | index.md 4 | contributing-guide.md 5 | ] 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | *.log 3 | 4 | # sbt specific 5 | .cache 6 | .history 7 | .lib/ 8 | dist/* 9 | target/ 10 | lib_managed/ 11 | src_managed/ 12 | project/boot/ 13 | project/plugins/project/ 14 | .bsp/ 15 | 16 | #Intellij 17 | .idea 18 | 19 | #metals 20 | .bloop 21 | .metals 22 | .vscode 23 | 24 | # Scala-IDE specific 25 | .scala_dependencies 26 | .worksheet 27 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.20.1") 2 | addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.3.2") 3 | addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.9") 4 | addSbtPlugin("org.portable-scala" % "sbt-scala-native-crossproject" % "1.3.2") 5 | addSbtPlugin("org.typelevel" % "sbt-typelevel" % "0.8.3") 6 | addSbtPlugin("org.typelevel" % "sbt-typelevel-site" % "0.8.3") 7 | -------------------------------------------------------------------------------- /.scalafmt.conf: -------------------------------------------------------------------------------- 1 | version = 3.10.2 2 | align.openParenCallSite = true 3 | align.openParenDefnSite = true 4 | maxColumn = 120 5 | continuationIndent.defnSite = 2 6 | assumeStandardLibraryStripMargin = true 7 | danglingParentheses.preset = true 8 | rewrite.rules = [AvoidInfix, SortImports, RedundantParens, SortModifiers] 9 | newlines.afterCurlyLambda = preserve 10 | docstrings.style = Asterisk 11 | docstrings.oneline = unfold 12 | runner.dialect = "scala213source3" 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Typelevel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /js/src/main/scala/mouse/js.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | trait AllJsSyntax 25 | -------------------------------------------------------------------------------- /jvm/src/main/scala/mouse/jvm.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | trait AllJvmSyntax extends StringJvmSyntax 25 | -------------------------------------------------------------------------------- /shared/src/main/scala/mouse/ftuple.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | trait FTupleSyntax extends FTupleNSyntax 25 | 26 | object FTupleSyntax 27 | -------------------------------------------------------------------------------- /project/GenFTupleNSyntax.scala: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | 3 | import Boilerplate._ 4 | import Boilerplate.{Template, TemplateVals} 5 | import sbt.File 6 | 7 | object GenFTupleNSyntax extends Template { 8 | // we generate syntax for Tuple3..22 because already there is [[cats.syntax.FunctorTuple2Ops]]. 9 | override def range = 3 to maxArity 10 | override def filename(root: sbt.File): File = 11 | root / "mouse" / "FTupleNSyntax.scala" 12 | 13 | override def content(tv: TemplateVals): String = { 14 | import tv._ 15 | 16 | val generatedFunctions: String = 17 | (1 to arity) 18 | .map { n => 19 | s""" 20 | - /** 21 | - * Lifts [[Tuple$arity._$n]] into `F[_]`. 22 | - */ 23 | - def _${n}F(implicit F: Functor[F]): F[A${n - 1}] = F.map(ftuple)(_._$n) 24 | - 25 | """ 26 | } 27 | .mkString("\n") 28 | 29 | block""" 30 | | 31 | |import cats.Functor 32 | | 33 | |trait FTupleNSyntax { 34 | - implicit final def mouseSyntaxFTuple${arity}Ops[F[_], ${`A..N`}](ftuple: F[(${`A..N`})]): FTuple${arity}Ops[F, ${`A..N`}] = new FTuple${arity}Ops[F, ${`A..N`}](ftuple) 35 | - 36 | - private[mouse] final class FTuple${arity}Ops[F[_], ${`A..N`}](ftuple: F[(${`A..N`})]) extends Serializable { 37 | $generatedFunctions 38 | - } 39 | - 40 | |}""" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /shared/src/main/scala-2.12/mouse/compat/SortedSet.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse.compat 23 | 24 | import scala.collection.immutable 25 | 26 | private[mouse] object SortedSet { 27 | def from[A: Ordering](set: Set[A]): immutable.SortedSet[A] = 28 | set.to[immutable.SortedSet] 29 | } 30 | -------------------------------------------------------------------------------- /shared/src/main/scala-2.13/mouse/compat/SortedSet.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse.compat 23 | 24 | import scala.collection.immutable 25 | 26 | private[mouse] object SortedSet { 27 | def from[A: Ordering](set: Set[A]): immutable.SortedSet[A] = 28 | immutable.SortedSet.from(set) 29 | } 30 | -------------------------------------------------------------------------------- /shared/src/main/scala-3/src/main/scala/mouse/compat/SortedSet.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse.compat 23 | 24 | import scala.collection.immutable 25 | 26 | private[mouse] object SortedSet { 27 | def from[A: Ordering](set: Set[A]): immutable.SortedSet[A] = 28 | immutable.SortedSet.from(set) 29 | } 30 | -------------------------------------------------------------------------------- /shared/src/test/scala/mouse/PartialFunctionLiftTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | class PartialFunctionLiftTest extends MouseSuite { 25 | private val f = liftEither[Option[Int]]({ case Some(n) => n }, a => s"Unexpected: $a") 26 | 27 | test("PartialFunctionLift.liftEither") { 28 | assertEquals(f(Some(6)), Right(6)) 29 | assertEquals(f(None), Left("Unexpected: None")) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /shared/src/main/scala/mouse/double.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | trait DoubleSyntax { 25 | implicit final def doubleSyntaxMouse(n: Double): DoubleOps = new DoubleOps(n) 26 | } 27 | 28 | final class DoubleOps(private val n: Double) extends AnyVal { 29 | 30 | @inline def toByteArray: Array[Byte] = java.nio.ByteBuffer.allocate(8).putDouble(n).array() 31 | 32 | @inline def squared: Double = n * n 33 | 34 | } 35 | -------------------------------------------------------------------------------- /shared/src/test/scala/mouse/DoubleSyntaxTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | class DoubleSyntaxTest extends MouseSuite { 25 | test("DoubleSyntax.toByteArray") { 26 | assertEquals( 27 | 123456789.123456789.toByteArray.toSeq, 28 | Array[Byte](65, -99, 111, 52, 84, 126, 107, 117).toSeq 29 | ) 30 | } 31 | 32 | test("DoubleSyntax.squared") { 33 | assertEquals(1.5.squared, 2.25) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /shared/src/test/scala/mouse/IntSyntaxTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | class IntSyntaxTest extends MouseSuite { 25 | test("IntSyntax.toByteArray") { 26 | assertEquals(123456789.toByteArray.toSeq, Array[Byte](7, 91, -51, 21).toSeq) 27 | } 28 | 29 | test("IntSyntax.toBase64") { 30 | assertEquals(123456789.toBase64, "B1vNFQ") 31 | } 32 | 33 | test("IntSyntax.squared") { 34 | assertEquals(7.squared, 49) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /shared/src/main/scala/mouse/MouseFunctions.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | import scala.annotation.nowarn 25 | 26 | trait MouseFunctions { 27 | 28 | /** 29 | * Evaluate but ignore the provided argument. This function makes value discarding an explicit operation, helpful when 30 | * the `-Ywarn-discard-values` compiler flag is enable to explicitly satisfy warnings. 31 | * 32 | * @param a 33 | * - the value to be evaluated and ignored. 34 | */ 35 | def ignore(@nowarn a: Any): Unit = (): Unit 36 | } 37 | -------------------------------------------------------------------------------- /shared/src/test/scala/mouse/LongSyntaxTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | class LongSyntaxTest extends MouseSuite { 25 | test("LongSyntax.toByteArray") { 26 | assertEquals( 27 | 123456789123456789L.toByteArray.toSeq, 28 | Array[Byte](1, -74, -101, 75, -84, -48, 95, 21).toSeq 29 | ) 30 | } 31 | 32 | test("LongSyntax.toBase64") { 33 | assertEquals(123456789123456789L.toBase64, "AbabS6zQXxU") 34 | } 35 | 36 | test("LongSyntax.squared") { 37 | assertEquals(7L.squared, 49L) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /shared/src/main/scala-3/src/main/scala/mouse/all.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | trait AllSharedSyntax 25 | extends AnySyntax 26 | with AnyFSyntax 27 | with BooleanSyntax 28 | with DoubleSyntax 29 | with FBooleanSyntax 30 | with FEitherSyntax 31 | with FNestedSyntax 32 | with FOptionSyntax 33 | with FTupleSyntax 34 | with IntSyntax 35 | with ListSyntax 36 | with LongSyntax 37 | with MapSyntax 38 | with OptionSyntax 39 | with PartialFunctionLift 40 | with SetSyntax 41 | with StringSyntax 42 | with TrySyntax 43 | -------------------------------------------------------------------------------- /shared/src/main/scala-2/src/main/scala/mouse/all.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | trait AllSharedSyntax 25 | extends AnySyntax 26 | with AnyFSyntax 27 | with BooleanSyntax 28 | with DoubleSyntax 29 | with FBooleanSyntax 30 | with FEitherSyntax 31 | with FNestedSyntax 32 | with FOptionSyntax 33 | with FTupleSyntax 34 | with IntSyntax 35 | with ListSyntax 36 | with LongSyntax 37 | with MapSyntax 38 | with OptionSyntax 39 | with PartialFunctionLift 40 | with SetSyntax 41 | with StringSyntax 42 | with TrySyntax 43 | with TupleSyntax 44 | -------------------------------------------------------------------------------- /shared/src/test/scala-2/mouse/MouseSuite.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | import cats.Eq 25 | import cats.instances.AllInstances 26 | import munit.FunSuite 27 | import munit.ScalaCheckSuite 28 | 29 | trait MouseSuite extends FunSuite with ScalaCheckSuite with AllSharedSyntax with AllInstances { 30 | implicit val eq0: Eq[NumberFormatException] = 31 | (x: NumberFormatException, y: NumberFormatException) => x.getMessage == y.getMessage 32 | 33 | implicit val eq1: Eq[IllegalArgumentException] = 34 | (x: IllegalArgumentException, y: IllegalArgumentException) => x.getMessage == y.getMessage 35 | } 36 | -------------------------------------------------------------------------------- /shared/src/test/scala-3/mouse/MouseSuite.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | import cats.Eq 25 | import cats.instances.AllInstances 26 | import munit.FunSuite 27 | import munit.ScalaCheckSuite 28 | 29 | trait MouseSuite extends FunSuite with ScalaCheckSuite with AllSharedSyntax with AllInstances { 30 | implicit val eq0: Eq[NumberFormatException] = 31 | (x: NumberFormatException, y: NumberFormatException) => x.getMessage == y.getMessage 32 | 33 | implicit val eq1: Eq[IllegalArgumentException] = 34 | (x: IllegalArgumentException, y: IllegalArgumentException) => x.getMessage == y.getMessage 35 | } 36 | -------------------------------------------------------------------------------- /shared/src/test/scala/mouse/SetSyntaxTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | import cats.data.NonEmptySet 25 | 26 | class SetSyntaxTest extends MouseSuite { 27 | test("SetSyntax.tailOrEmpty") { 28 | assertEquals(Set.empty[Int].tailOrEmpty, Set.empty[Int]) 29 | assertEquals(Set(0).tailOrEmpty, Set.empty[Int]) 30 | assertEquals(Set(0, 1, 2).tailOrEmpty, Set(1, 2)) 31 | } 32 | 33 | test("SetSyntax.tailOption") { 34 | assertEquals(Set.empty[Int].tailOption, None) 35 | assertEquals(Set(0).tailOption, None) 36 | assertEquals(Set(0, 1, 2).tailOption, Some(NonEmptySet.of(1, 2))) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /shared/src/test/scala/mouse/ListSyntaxTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | import cats.data.NonEmptyList 25 | 26 | class ListSyntaxTest extends MouseSuite { 27 | test("ListSyntax.tailOrEmpty") { 28 | assertEquals(List.empty[Int].tailOrEmpty, List.empty[Int]) 29 | assertEquals(List(0).tailOrEmpty, List.empty[Int]) 30 | assertEquals(List(0, 1, 2).tailOrEmpty, List(1, 2)) 31 | } 32 | 33 | test("ListSyntax.tailOption") { 34 | assertEquals(List.empty[Int].tailOption, None) 35 | assertEquals(List(0).tailOption, None) 36 | assertEquals(List(0, 1, 2).tailOption, Some(NonEmptyList.of(1, 2))) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /shared/src/main/scala/mouse/int.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | trait IntSyntax { 25 | implicit final def intSyntaxMouse(n: Int): IntOps = new IntOps(n) 26 | } 27 | 28 | final class IntOps(private val n: Int) extends AnyVal { 29 | 30 | @inline def toByteArray: Array[Byte] = java.nio.ByteBuffer.allocate(4).putInt(n).array() 31 | 32 | /** 33 | * Base64 encoding (without final terminator). Shortcut for 34 | * `java.util.Base64.getEncoder.withoutPadding.encodeToString` 35 | */ 36 | @inline def toBase64: String = java.util.Base64.getEncoder.withoutPadding.encodeToString(toByteArray) 37 | 38 | @inline def squared: Int = n * n 39 | 40 | } 41 | -------------------------------------------------------------------------------- /shared/src/main/scala/mouse/partialFunction.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | trait PartialFunctionLift { 25 | 26 | def liftEither[A]: PartialFunctionLift.LiftEitherPartiallyApplied[A] = 27 | new PartialFunctionLift.LiftEitherPartiallyApplied() 28 | 29 | } 30 | object PartialFunctionLift { 31 | 32 | // https://typelevel.org/cats/guidelines.html#partially-applied-type-params 33 | final private[mouse] class LiftEitherPartiallyApplied[A](private val dummy: Boolean = true) extends AnyVal { 34 | def apply[B, C](pf: PartialFunction[A, B], orElse: A => C): A => Either[C, B] = 35 | (a: A) => pf.lift(a).toRight(orElse(a)) 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /shared/src/main/scala/mouse/list.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | import cats.data.NonEmptyList 25 | 26 | trait ListSyntax { 27 | implicit final def listSyntaxMouse[A](list: List[A]): ListOps[A] = new ListOps[A](list) 28 | } 29 | 30 | final class ListOps[A](private val list: List[A]) extends AnyVal { 31 | 32 | /** 33 | * A safe counterpart to `List.tail` that returns `Nil` for an empty list. 34 | */ 35 | @inline def tailOrEmpty: List[A] = list.drop(1) 36 | 37 | /** 38 | * Returns `Some` if the tail is non-empty, otherwise `None`. 39 | */ 40 | @inline def tailOption: Option[NonEmptyList[A]] = NonEmptyList.fromList(list.drop(1)) 41 | 42 | } 43 | -------------------------------------------------------------------------------- /shared/src/main/scala/mouse/long.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | trait LongSyntax { 25 | implicit final def longSyntaxMouse(n: Long): LongOps = new LongOps(n) 26 | } 27 | 28 | final class LongOps(private val n: Long) extends AnyVal { 29 | 30 | @inline def toByteArray: Array[Byte] = java.nio.ByteBuffer.allocate(8).putLong(n).array() 31 | 32 | /** 33 | * Base64 encoding (without final terminator). Shortcut for 34 | * `java.util.Base64.getEncoder.withoutPadding.encodeToString` 35 | */ 36 | @inline def toBase64: String = java.util.Base64.getEncoder.withoutPadding.encodeToString(toByteArray) 37 | 38 | @inline def squared: Long = n * n 39 | 40 | } 41 | -------------------------------------------------------------------------------- /DEV.md: -------------------------------------------------------------------------------- 1 | ## Design Process for Adding a Feature 2 | 3 | * Check the feature isn't already available or readily achievable with existing operations 4 | 5 | * Consider and minimize risk of naming collisions for users who have imported mouse into existing code scopes. Dont use overly 6 | short or generic names if possible. 7 | 8 | * Add a test and test the feature manually. Use the simplest test code that covers the added behavior - simple features do not need complex tests. 9 | 10 | * Mention in the example section and in the release notes in README.md 11 | 12 | ## Conventions 13 | 14 | * When an operation is provided in two variants that do the same thing, but differ in whether the arguments are evaluated lazily ("by-name") or eagerly, we have used the suffix `L` (for Lazy) to distinguish the names (see #247)[https://github.com/typelevel/mouse/issues/247] for example). 15 | 16 | However, for new operations, simply providing a lazy by-name variant is often the best choice unless there are clear reasons (eg performance impact) for needing an eager variant. 17 | 18 | ## Release Process 19 | 20 | Mouse uses Github Actions, https://github.com/djspiewak/sbt-github-actions and https://github.com/typelevel/sbt-typelevel for CI releases. Use the Github Create Release feature to tag a release, and it will publish to Sonatype automatically (using @benhutchison credentials). 21 | 22 | sbt-release and sbt-ci-release is no longer in use. 23 | 24 | ## Choosing an Appropriate Base Branch 25 | 26 | There are two options for choosing a base branch for your PRs: 27 | 28 | * Use the `main` branch if you would like to deliver changes within the `1.x` series. It's for binary-compatible changes only. 29 | 30 | * Use the `series/2.x` branch if you would like to deliver changes within the `2.x` series. It's for non-binary-compatible changes and basically stands for the next major `mouse` release. 31 | -------------------------------------------------------------------------------- /shared/src/main/scala/mouse/set.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | import cats.data.NonEmptySet 25 | import mouse.compat.SortedSet 26 | 27 | trait SetSyntax { 28 | implicit final def setSyntaxMouse[A](set: Set[A]): SetOps[A] = new SetOps[A](set) 29 | } 30 | 31 | final class SetOps[A](private val sa: Set[A]) extends AnyVal { 32 | 33 | /** 34 | * A safe counterpart to `Set.tail` that returns empty `Set[A]` for an empty set. 35 | */ 36 | @inline def tailOrEmpty: Set[A] = sa.drop(1) 37 | 38 | /** 39 | * Returns `Some` if the tail is non-empty, otherwise `None`. 40 | */ 41 | @inline def tailOption(implicit ordering: Ordering[A]): Option[NonEmptySet[A]] = 42 | NonEmptySet.fromSet(SortedSet.from(tailOrEmpty)) 43 | 44 | } 45 | -------------------------------------------------------------------------------- /docs/contributing-guide.md: -------------------------------------------------------------------------------- 1 | # Contributing guide 2 | 3 | ### Design Process for Adding a Feature 4 | 5 | * Check the feature isn't already available or readily achievable with existing operations 6 | 7 | * Consider and minimize risk of naming collisions for users who have imported mouse into existing code scopes. Don't use overly 8 | short or generic names if possible. 9 | 10 | * Add a test and test the feature manually. Use the simplest test code that covers the added behavior - simple features do not need complex tests. 11 | 12 | * Mention in the example section and in the release notes in README.md 13 | 14 | ### Conventions 15 | 16 | * When an operation is provided in two variants that do the same thing, but differ in whether the arguments are evaluated lazily ("by-name") or eagerly, we have used the suffix `L` (for Lazy) to distinguish the names (see [#247]) for example). 17 | 18 | However, for new operations, simply providing a lazy by-name variant is often the best choice unless there are clear reasons (eg performance impact) for needing an eager variant. 19 | 20 | ### Release Process 21 | 22 | Mouse uses Github Actions, [sbt-github-actions] and [sbt-typelevel] for CI releases. 23 | Use the Github Create Release feature to tag a release, 24 | and it will publish to Sonatype automatically (using @benhutchison credentials). 25 | 26 | ### Choosing an Appropriate Base Branch 27 | 28 | There are two options for choosing a base branch for your PRs: 29 | 30 | * Use the `main` branch if you would like to deliver changes within the `1.x` series. It's for binary-compatible changes only. 31 | 32 | * Use the `series/2.x` branch if you would like to deliver changes within the `2.x` series. It's for non-binary-compatible changes and basically stands for the next major `mouse` release. 33 | 34 | 35 | [sbt-github-actions]: https://github.com/djspiewak/sbt-github-actions 36 | [sbt-typelevel]: https://github.com/typelevel/sbt-typelevel 37 | [#247]: https://github.com/typelevel/mouse/issues/247 -------------------------------------------------------------------------------- /native/src/main/scala-3/mouse/package.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package object mouse extends MouseFunctions { 23 | object all extends AllSharedSyntax 24 | object any extends AnySyntax 25 | object anyf extends AnyFSyntax 26 | object boolean extends BooleanSyntax 27 | object double extends DoubleSyntax 28 | object fboolean extends FBooleanSyntax 29 | object feither extends FEitherSyntax 30 | object fnested extends FNestedSyntax 31 | object foption extends FOptionSyntax 32 | object ftuple extends FTupleSyntax 33 | object int extends IntSyntax 34 | object list extends ListSyntax 35 | object long extends LongSyntax 36 | object map extends MapSyntax 37 | object option extends OptionSyntax 38 | object set extends SetSyntax 39 | object string extends StringSyntax 40 | object `try` extends TrySyntax 41 | } 42 | -------------------------------------------------------------------------------- /js/src/main/scala-3/mouse/package.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package object mouse extends MouseFunctions { 23 | object all extends AllSharedSyntax with AllJsSyntax 24 | object any extends AnySyntax 25 | object anyf extends AnyFSyntax 26 | object boolean extends BooleanSyntax 27 | object double extends DoubleSyntax 28 | object fboolean extends FBooleanSyntax 29 | object feither extends FEitherSyntax 30 | object fnested extends FNestedSyntax 31 | object foption extends FOptionSyntax 32 | object ftuple extends FTupleSyntax 33 | object int extends IntSyntax 34 | object list extends ListSyntax 35 | object long extends LongSyntax 36 | object map extends MapSyntax 37 | object option extends OptionSyntax 38 | object set extends SetSyntax 39 | object string extends StringSyntax 40 | object `try` extends TrySyntax 41 | } 42 | -------------------------------------------------------------------------------- /jvm/src/main/scala-3/src/main/scala/mouse/package.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package object mouse extends MouseFunctions { 23 | object all extends AllSharedSyntax with AllJvmSyntax 24 | object any extends AnySyntax 25 | object anyf extends AnyFSyntax 26 | object boolean extends BooleanSyntax 27 | object double extends DoubleSyntax 28 | object fboolean extends FBooleanSyntax 29 | object feither extends FEitherSyntax 30 | object fnested extends FNestedSyntax 31 | object foption extends FOptionSyntax 32 | object ftuple extends FTupleSyntax 33 | object int extends IntSyntax 34 | object list extends ListSyntax 35 | object long extends LongSyntax 36 | object map extends MapSyntax 37 | object option extends OptionSyntax 38 | object set extends SetSyntax 39 | object string extends StringSyntax with StringJvmSyntax 40 | object `try` extends TrySyntax 41 | } 42 | -------------------------------------------------------------------------------- /native/src/main/scala-2/mouse/package.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | object `package` extends MouseFunctions { 25 | object all extends AllSharedSyntax 26 | object any extends AnySyntax 27 | object anyf extends AnyFSyntax 28 | object boolean extends BooleanSyntax 29 | object double extends DoubleSyntax 30 | object fboolean extends FBooleanSyntax 31 | object feither extends FEitherSyntax 32 | object fnested extends FNestedSyntax 33 | object foption extends FOptionSyntax 34 | object ftuple extends FTupleSyntax 35 | object int extends IntSyntax 36 | object list extends ListSyntax 37 | object long extends LongSyntax 38 | object map extends MapSyntax 39 | object option extends OptionSyntax 40 | object set extends SetSyntax 41 | object string extends StringSyntax 42 | object `try` extends TrySyntax 43 | object tuple extends TupleSyntax 44 | } 45 | -------------------------------------------------------------------------------- /js/src/main/scala-2/mouse/package.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | object `package` extends MouseFunctions { 25 | object all extends AllSharedSyntax with AllJsSyntax 26 | object any extends AnySyntax 27 | object anyf extends AnyFSyntax 28 | object boolean extends BooleanSyntax 29 | object double extends DoubleSyntax 30 | object fboolean extends FBooleanSyntax 31 | object feither extends FEitherSyntax 32 | object fnested extends FNestedSyntax 33 | object foption extends FOptionSyntax 34 | object ftuple extends FTupleSyntax 35 | object int extends IntSyntax 36 | object list extends ListSyntax 37 | object long extends LongSyntax 38 | object map extends MapSyntax 39 | object option extends OptionSyntax 40 | object set extends SetSyntax 41 | object string extends StringSyntax 42 | object `try` extends TrySyntax 43 | object tuple extends TupleSyntax 44 | } 45 | -------------------------------------------------------------------------------- /shared/src/main/scala/mouse/try.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | import cats.Applicative 25 | import cats.data.EitherT 26 | 27 | import scala.util.{Failure, Success, Try} 28 | 29 | trait TrySyntax { 30 | @inline implicit final def trySyntaxMouse[A](ta: Try[A]): TryOps[A] = new TryOps(ta) 31 | } 32 | 33 | final class TryOps[A](private val ta: Try[A]) extends AnyVal { 34 | 35 | @inline def cata[B](success: A => B, failure: Throwable => B): B = ta match { 36 | case Success(value) => success(value) 37 | case Failure(error) => failure(error) 38 | } 39 | 40 | @inline def toEither: Either[Throwable, A] = cata[Either[Throwable, A]](Right(_), Left(_)) 41 | 42 | /** 43 | * Converts a `Try[A]` to a `EitherT[F, Throwable, A]`. 44 | */ 45 | @inline def toEitherT[F[_]](implicit F: Applicative[F]): EitherT[F, Throwable, A] = 46 | EitherT.fromEither[F](toEither) 47 | } 48 | -------------------------------------------------------------------------------- /shared/src/main/scala/mouse/map.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | import cats.{Applicative, Semigroup} 25 | 26 | trait MapSyntax { 27 | implicit final def mapSyntaxMouse[A, B](map: Map[A, B]): MapOps[A, B] = new MapOps(map) 28 | } 29 | 30 | final class MapOps[A, B](private val map: Map[A, B]) extends AnyVal { 31 | 32 | def mapKeys[C](f: A => C): Map[C, B] = map.map { case (a, b) => (f(a), b) } 33 | 34 | def updateAtKey(key: A, f: B => B): Map[A, B] = map.get(key).fold(map)(value => map.updated(key, f(value))) 35 | 36 | def updateAtKeyF[F[_]](key: A, f: B => F[B])(implicit F: Applicative[F]): F[Map[A, B]] = 37 | map.get(key).fold(F.pure(map))(value => F.map(f(value))(result => map.updated(key, result))) 38 | 39 | def updateAtKeyCombine(key: A, add: B)(implicit sg: Semigroup[B]): Map[A, B] = 40 | map.get(key).fold(map + (key -> add))(value => map.updated(key, sg.combine(value, add))) 41 | } 42 | -------------------------------------------------------------------------------- /jvm/src/main/scala-2/src/main/scala/mouse/package.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | object `package` extends MouseFunctions { 25 | object all extends AllSharedSyntax with AllJvmSyntax 26 | object any extends AnySyntax 27 | object anyf extends AnyFSyntax 28 | object boolean extends BooleanSyntax 29 | object double extends DoubleSyntax 30 | object fboolean extends FBooleanSyntax 31 | object feither extends FEitherSyntax 32 | object fnested extends FNestedSyntax 33 | object foption extends FOptionSyntax 34 | object ftuple extends FTupleSyntax 35 | object int extends IntSyntax 36 | object list extends ListSyntax 37 | object long extends LongSyntax 38 | object map extends MapSyntax 39 | object option extends OptionSyntax 40 | object set extends SetSyntax 41 | object string extends StringSyntax with StringJvmSyntax 42 | object `try` extends TrySyntax 43 | object tuple extends TupleSyntax 44 | } 45 | -------------------------------------------------------------------------------- /shared/src/main/scala/mouse/any.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | import cats.Applicative 25 | 26 | trait AnySyntax { 27 | implicit final def anySyntaxMouse[A](oa: A): AnyOps[A] = new AnyOps(oa) 28 | } 29 | 30 | final class AnyOps[A](private val a: A) extends AnyVal { 31 | @inline def |>[B](f: A => B): B = f(a) 32 | 33 | @inline def thrush[B](f: A => B): B = f(a) 34 | 35 | @inline def <|(f: A => Unit): A = { 36 | f(a) 37 | a 38 | } 39 | 40 | @inline def unsafeTap(f: A => Unit): A = { 41 | f(a) 42 | a 43 | } 44 | 45 | @inline def someF[F[_]](implicit F: Applicative[F]): F[Option[A]] = 46 | FOptionSyntax.someF[F, A](a) 47 | 48 | @inline def asLeftF[F[_], R](implicit F: Applicative[F]): F[Either[A, R]] = 49 | FEitherSyntax.asLeftF[F, A, R](a) 50 | 51 | @inline def asRightF[F[_], L](implicit F: Applicative[F]): F[Either[L, A]] = 52 | FEitherSyntax.asRightF[F, L, A](a) 53 | } 54 | -------------------------------------------------------------------------------- /shared/src/test/scala/mouse/AnySyntaxTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | class AnySyntaxTest extends MouseSuite with MouseFunctions { 25 | test("AnySyntax.|>") { 26 | assertEquals(true |> (!_), false) 27 | 28 | assertEquals(5 |> (_ + 44), 49) 29 | 30 | assertEquals(Some("thing") |> (_.getOrElse("that")), "thing") 31 | 32 | assertEquals( 33 | "This" |> Function.const("that") |> (_.capitalize) |> Function.const("at bat"), 34 | "at bat" 35 | ) 36 | 37 | assertEquals(ignore(true), ()) 38 | 39 | assertEquals(1200 |> (_ * 2) |> (_ - 5) |> (_ / 3), ((1200 * 2) - 5) / 3) 40 | 41 | assertEquals("anythingAtAll" |> ignore, ()) 42 | } 43 | 44 | test("AnySyntax.someF") { 45 | assertEquals(42.someF[List], List(Option(42))) 46 | } 47 | 48 | test("AnySyntax.asLeftF") { 49 | assertEquals("".asLeftF[List, Int], List(Left(""))) 50 | } 51 | 52 | test("AnySyntax.asRightF") { 53 | assertEquals(42.asRightF[List, String], List(Right(42))) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /shared/src/main/scala/mouse/anyf.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | import cats.data.EitherT 25 | import cats.data.OptionT 26 | import cats.~> 27 | import cats.Functor 28 | 29 | trait AnyFSyntax { 30 | implicit final def anyfSyntaxMouse[F[_], A](fa: F[A]): AnyFOps[F, A] = new AnyFOps(fa) 31 | } 32 | 33 | final class AnyFOps[F[_], A](private val fa: F[A]) extends AnyVal { 34 | @inline def ||>[G[_]](f: F ~> G): G[A] = f(fa) 35 | @inline def thrushK[G[_]](f: F ~> G): G[A] = f(fa) 36 | 37 | def mapAsRight[L](implicit F: Functor[F]): F[Either[L, A]] = 38 | Functor[F].map(fa)(Right(_)) 39 | 40 | def mapAsLeft[R](implicit F: Functor[F]): F[Either[A, R]] = 41 | Functor[F].map(fa)(Left(_)) 42 | 43 | def mapAsSome(implicit F: Functor[F]): F[Option[A]] = 44 | Functor[F].map(fa)(Some(_)) 45 | 46 | def liftEitherT[E](implicit F: Functor[F]): EitherT[F, E, A] = 47 | EitherT.right[E](fa) 48 | 49 | def liftOptionT(implicit F: Functor[F]): OptionT[F, A] = 50 | OptionT.liftF(fa) 51 | 52 | } 53 | -------------------------------------------------------------------------------- /shared/src/main/scala/mouse/option.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | import scala.util.{Failure, Success, Try} 25 | 26 | trait OptionSyntax { 27 | implicit final def optionSyntaxMouse[A](oa: Option[A]): OptionOps[A] = new OptionOps(oa) 28 | } 29 | 30 | final class OptionOps[A](private val oa: Option[A]) extends AnyVal { 31 | 32 | @inline def cata[B](some: A => B, none: => B): B = oa.fold[B](none)(some) 33 | 34 | @inline def toTry(ex: => Throwable): Try[A] = cata(Success(_), Failure(ex)) 35 | 36 | @inline def toTryMsg(msg: => String): Try[A] = toTry(new RuntimeException(msg)) 37 | 38 | /** 39 | * Same as oa.toRight except that it fixes the type to Either[B, A] On Scala prior to 2.12, toRight returns 40 | * `Serializable with Product with Either[B, A]` 41 | */ 42 | @deprecated("Use toRight instead", "1.2.0") 43 | @inline def right[B](b: => B): Either[B, A] = oa.toRight(b) 44 | 45 | /** 46 | * Same as oa.toLeft except that it fixes the type to Either[A, B] On Scala prior to 2.12, toLeft returns 47 | * `Serializable with Product with Either[A, B]` 48 | */ 49 | @deprecated("Use toLeft instead", "1.2.0") 50 | @inline def left[B](b: => B): Either[A, B] = oa.toLeft(b) 51 | } 52 | -------------------------------------------------------------------------------- /shared/src/main/scala/mouse/fnested.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | import cats.{FlatMap, Functor} 25 | 26 | trait FNestedSyntax { 27 | implicit final def fnested2SyntaxMouse[F[_], G[_], A](fga: F[G[A]]): FNested2SyntaxOps[F, G, A] = 28 | new FNested2SyntaxOps[F, G, A](fga) 29 | 30 | implicit final def fnested3SyntaxMouse[F[_], G[_], H[_], A](fgha: F[G[H[A]]]): FNested3SyntaxOps[F, G, H, A] = 31 | new FNested3SyntaxOps[F, G, H, A](fgha) 32 | } 33 | 34 | final class FNested2SyntaxOps[F[_], G[_], A](private val fga: F[G[A]]) extends AnyVal { 35 | def mapNested2[B](f: A => B)(implicit F: Functor[F], G: Functor[G]): F[G[B]] = 36 | F.map(fga)(ga => G.map(ga)(f)) 37 | 38 | def flatMapNested2[B](f: A => G[B])(implicit F: Functor[F], G: FlatMap[G]): F[G[B]] = 39 | F.map(fga)(ga => G.flatMap(ga)(f)) 40 | } 41 | 42 | final class FNested3SyntaxOps[F[_], G[_], H[_], A](private val fgha: F[G[H[A]]]) extends AnyVal { 43 | def mapNested3[B](f: A => B)(implicit F: Functor[F], G: Functor[G], H: Functor[H]): F[G[H[B]]] = 44 | F.map(fgha)(gha => G.map(gha)(ha => H.map(ha)(f))) 45 | 46 | def flatMapNested3[B](f: A => H[B])(implicit F: Functor[F], G: Functor[G], H: FlatMap[H]): F[G[H[B]]] = 47 | F.map(fgha)(gha => G.map(gha)(ha => H.flatMap(ha)(f))) 48 | } 49 | -------------------------------------------------------------------------------- /shared/src/test/scala/mouse/MapSyntaxTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | class MapSyntaxTest extends MouseSuite { 25 | test("MapSyntax.mapKeys") { 26 | assertEquals(Map(1 -> 2, 3 -> 4).mapKeys(_ * 2), Map(2 -> 2, 6 -> 4)) 27 | assertEquals(Map(1 -> 2, 3 -> 4).mapKeys(identity), Map(1 -> 2, 3 -> 4)) 28 | assertEquals(Map[Int, Int]().mapKeys(identity), Map[Int, Int]()) 29 | } 30 | 31 | test("MapSyntax.updateAtKey") { 32 | assertEquals(Map(1 -> 2, 3 -> 4).updateAtKey(3, _ * 2), Map(1 -> 2, 3 -> 8)) 33 | assertEquals(Map(1 -> 2, 3 -> 4).updateAtKey(42, _ * 2), Map(1 -> 2, 3 -> 4)) 34 | assertEquals(Map(1 -> 2, 3 -> 4).updateAtKey(3, identity), Map(1 -> 2, 3 -> 4)) 35 | } 36 | 37 | test("MapSyntax.updateAtKeyCombine") { 38 | assertEquals(Map(1 -> 2, 3 -> 4).updateAtKeyCombine(3, 5), Map(1 -> 2, 3 -> 9)) 39 | assertEquals(Map(1 -> 2, 3 -> 4).updateAtKeyCombine(42, 5), Map(1 -> 2, 3 -> 4, 42 -> 5)) 40 | } 41 | 42 | test("MapSyntax.updateAtKeyF") { 43 | assertEquals(Map(1 -> 2, 3 -> 4).updateAtKeyF(3, Option(_)), Some(Map(1 -> 2, 3 -> 4))) 44 | assertEquals(Map(1 -> 2, 3 -> 4).updateAtKeyF(3, i => Option(i * 2)), Some(Map(1 -> 2, 3 -> 8))) 45 | assertEquals(Map(1 -> 2, 3 -> 4).updateAtKeyF(42, Option(_)), Some(Map(1 -> 2, 3 -> 4))) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /jvm/src/main/scala/mouse/stringJvm.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | import java.net.{MalformedURLException, URI, URISyntaxException, URL} 25 | import cats.data.Validated 26 | import cats.syntax.either._ 27 | 28 | import java.util.UUID 29 | 30 | trait StringJvmSyntax { 31 | implicit def stringJvmSyntaxMouse(s: String): JvmStringOps = new JvmStringOps(s) 32 | } 33 | 34 | final class JvmStringOps(private val s: String) extends AnyVal { 35 | 36 | @inline def parseURL: MalformedURLException Either URL = Either.catchOnly[MalformedURLException](new URL(s)) 37 | 38 | @inline def parseURLValidated: Validated[MalformedURLException, URL] = parseURL.toValidated 39 | 40 | @inline def parseURLOption: Option[URL] = parseURL.toOption 41 | 42 | @inline def parseURI: URISyntaxException Either URI = Either.catchOnly[URISyntaxException](new URI(s)) 43 | 44 | @inline def parseURIValidated: Validated[URISyntaxException, URI] = parseURI.toValidated 45 | 46 | @inline def parseURIOption: Option[URI] = parseURI.toOption 47 | 48 | @inline def parseUUID: IllegalArgumentException Either UUID = 49 | Either.catchOnly[IllegalArgumentException](UUID.fromString(s)) 50 | 51 | @inline def parseUUIDValidated: Validated[IllegalArgumentException, UUID] = parseUUID.toValidated 52 | 53 | @inline def parseUUIDOption: Option[UUID] = parseUUID.toOption 54 | 55 | } 56 | -------------------------------------------------------------------------------- /.github/workflows/clean.yml: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by sbt-github-actions using the 2 | # githubWorkflowGenerate task. You should add and commit this file to 3 | # your git repository. It goes without saying that you shouldn't edit 4 | # this file by hand! Instead, if you wish to make changes, you should 5 | # change your sbt build configuration to revise the workflow description 6 | # to meet your needs, then regenerate this file. 7 | 8 | name: Clean 9 | 10 | on: push 11 | 12 | jobs: 13 | delete-artifacts: 14 | name: Delete Artifacts 15 | runs-on: ubuntu-latest 16 | env: 17 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 18 | steps: 19 | - name: Delete artifacts 20 | run: | 21 | # Customize those three lines with your repository and credentials: 22 | REPO=${GITHUB_API_URL}/repos/${{ github.repository }} 23 | 24 | # A shortcut to call GitHub API. 25 | ghapi() { curl --silent --location --user _:$GITHUB_TOKEN "$@"; } 26 | 27 | # A temporary file which receives HTTP response headers. 28 | TMPFILE=/tmp/tmp.$$ 29 | 30 | # An associative array, key: artifact name, value: number of artifacts of that name. 31 | declare -A ARTCOUNT 32 | 33 | # Process all artifacts on this repository, loop on returned "pages". 34 | URL=$REPO/actions/artifacts 35 | while [[ -n "$URL" ]]; do 36 | 37 | # Get current page, get response headers in a temporary file. 38 | JSON=$(ghapi --dump-header $TMPFILE "$URL") 39 | 40 | # Get URL of next page. Will be empty if we are at the last page. 41 | URL=$(grep '^Link:' "$TMPFILE" | tr ',' '\n' | grep 'rel="next"' | head -1 | sed -e 's/.*.*//') 42 | rm -f $TMPFILE 43 | 44 | # Number of artifacts on this page: 45 | COUNT=$(( $(jq <<<$JSON -r '.artifacts | length') )) 46 | 47 | # Loop on all artifacts on this page. 48 | for ((i=0; $i < $COUNT; i++)); do 49 | 50 | # Get name of artifact and count instances of this name. 51 | name=$(jq <<<$JSON -r ".artifacts[$i].name?") 52 | ARTCOUNT[$name]=$(( $(( ${ARTCOUNT[$name]} )) + 1)) 53 | 54 | id=$(jq <<<$JSON -r ".artifacts[$i].id?") 55 | size=$(( $(jq <<<$JSON -r ".artifacts[$i].size_in_bytes?") )) 56 | printf "Deleting '%s' #%d, %'d bytes\n" $name ${ARTCOUNT[$name]} $size 57 | ghapi -X DELETE $REPO/actions/artifacts/$id 58 | done 59 | done 60 | -------------------------------------------------------------------------------- /shared/src/test/scala/mouse/OptionSyntaxTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | import org.typelevel.scalaccompat.annotation._ 25 | import scala.annotation.nowarn 26 | import scala.util.{Failure, Success} 27 | 28 | class OptionSyntaxTest extends MouseSuite { 29 | implicit class ExtraTest[A](@nowarn3 a: A) { 30 | def shouldBeA[T](implicit @nowarn212 ev: T =:= A): Unit = () 31 | } 32 | 33 | test("OptionSyntax.cata") { 34 | assertEquals(Option(1).cata(_.toString, ""), "1") 35 | assertEquals(Option.empty[Int].cata(_.toString, ""), "") 36 | } 37 | 38 | test("OptionSyntax.toTry") { 39 | assertEquals(Option(1).toTry(new RuntimeException("Err")), Success(1)) 40 | 41 | val ex = new RuntimeException("Err") 42 | assertEquals(Option.empty[Int].toTry(ex), Failure(ex)) 43 | } 44 | 45 | @nowarn("cat=deprecation") 46 | private def rightTest(): Unit = 47 | test("OptionSyntax.right") { 48 | assertEquals(Option(3).right("S"), Option(3).toRight("S")) 49 | assertEquals(None.right("S"), None.toRight("S")) 50 | Option(3).right("S").shouldBeA[Either[String, Int]] 51 | } 52 | 53 | rightTest() 54 | 55 | @nowarn("cat=deprecation") 56 | private def leftTest(): Unit = test("OptionSyntax.left") { 57 | assertEquals(Option(3).left("S"), Option(3).toLeft("S")) 58 | assertEquals(None.left("S"), None.toLeft("S")) 59 | Option(3).left("S").shouldBeA[Either[Int, String]] 60 | } 61 | 62 | leftTest() 63 | } 64 | -------------------------------------------------------------------------------- /project/Boilerplate.scala: -------------------------------------------------------------------------------- 1 | import sbt._ 2 | 3 | import scala.annotation.tailrec 4 | import scala.collection.immutable 5 | 6 | /** 7 | * Copied, with some modifications, from https://github.com/milessabin/shapeless/blob/main/project/Boilerplate.scala 8 | * 9 | * Generate a range of boilerplate classes, those offering alternatives with 0-22 params and would be tedious to craft 10 | * by hand 11 | * 12 | * @author 13 | * Miles Sabin 14 | * @author 15 | * Kevin Wright 16 | */ 17 | object Boilerplate { 18 | 19 | import scala.StringContext._ 20 | 21 | implicit final class BlockHelper(private val sc: StringContext) extends AnyVal { 22 | def block(args: Any*): String = { 23 | val interpolated = sc.standardInterpolator(treatEscapes, args) 24 | val rawLines = interpolated.split('\n') 25 | val trimmedLines = rawLines.map(_.dropWhile(_.isWhitespace)) 26 | trimmedLines.mkString("\n") 27 | } 28 | } 29 | 30 | val templates: Seq[Template] = Seq(GenFTupleNSyntax) 31 | 32 | val header: String = 33 | """// auto-generated boilerplate by /project/Boilerplate.scala 34 | |package mouse 35 | |""".stripMargin 36 | 37 | /** 38 | * Returns a seq of the generated files. As a side-effect, it actually generates them... 39 | */ 40 | def gen(dir: File) = 41 | for (t <- templates) yield { 42 | val tgtFile = t.filename(dir) 43 | IO.write(tgtFile, t.body) 44 | tgtFile 45 | } 46 | 47 | val maxArity: Int = 22 48 | 49 | final class TemplateVals(val arity: Int) { 50 | val synTypes: immutable.Seq[String] = (0 until arity).map(n => s"A$n") 51 | val `A..N`: String = synTypes.mkString(", ") 52 | } 53 | 54 | trait Template { 55 | def filename(root: File): File 56 | 57 | def content(tv: TemplateVals): String 58 | 59 | def range = 1 to maxArity 60 | 61 | def body: String = { 62 | @tailrec 63 | def expandInstances(contents: IndexedSeq[Array[String]], acc: Array[String] = Array.empty): Array[String] = 64 | if (!contents.exists(_.exists(_.startsWith("-")))) 65 | acc.map(_.tail) 66 | else { 67 | val pre = contents.head.takeWhile(_.startsWith("|")) 68 | val instances = contents.flatMap(_.dropWhile(_.startsWith("|")).takeWhile(_.startsWith("-"))) 69 | val next = contents.map(_.dropWhile(_.startsWith("|")).dropWhile(_.startsWith("-"))) 70 | expandInstances(next, acc ++ pre ++ instances) 71 | } 72 | 73 | val rawContents = range.map { n => 74 | content(new TemplateVals(n)).split('\n').filterNot(_.isEmpty) 75 | } 76 | val headerLines = header.split('\n') 77 | val instances = expandInstances(rawContents) 78 | val footerLines = rawContents.head.reverse.takeWhile(_.startsWith("|")).map(_.tail).reverse 79 | (headerLines ++ instances ++ footerLines).mkString("\n") 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /jvm/src/test/scala/mouse/StringJvmTests.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | import java.net.{MalformedURLException, URI, URISyntaxException, URL} 25 | import cats.syntax.all._ 26 | 27 | import java.util.UUID 28 | 29 | class StringJvmTests extends MouseSuite with StringJvmSyntax { 30 | test("parseFloat") { 31 | assertEquals("123.1".parseFloat, 123.1f.asRight[NumberFormatException]) 32 | } 33 | 34 | test("parseURL") { 35 | assertEquals( 36 | "http://example.com".parseURL, 37 | new URL("http://example.com").asRight[MalformedURLException] 38 | ) 39 | 40 | assertEquals("blah".parseURL.leftMap(_.getMessage), "no protocol: blah".asLeft) 41 | } 42 | 43 | test("parseURI") { 44 | assertEquals("http://example.com".parseURI, new URI("http://example.com").asRight[URISyntaxException]) 45 | 46 | assertEquals( 47 | "invalid uri".parseURI.leftMap(e => e.getInput -> e.getReason), 48 | ("invalid uri", "Illegal character in path").asLeft 49 | ) 50 | } 51 | 52 | test("parseUUID") { 53 | val validUUIDStr = "00000000-0000-0000-0000-000000000000" 54 | val invalidUUIDStr = "invalid" 55 | 56 | assertEquals(validUUIDStr.parseUUID, UUID.fromString(validUUIDStr).asRight[IllegalArgumentException]) 57 | assertEquals(validUUIDStr.parseUUID.toValidated, validUUIDStr.parseUUIDValidated) 58 | assertEquals(validUUIDStr.parseUUID.toOption, validUUIDStr.parseUUIDOption) 59 | 60 | assertEquals( 61 | invalidUUIDStr.parseUUID.leftMap(_.getMessage), 62 | "Invalid UUID string: invalid".asLeft[UUID] 63 | ) 64 | 65 | assertEquals( 66 | invalidUUIDStr.parseUUID.toValidated.leftMap(_.getMessage), 67 | invalidUUIDStr.parseUUIDValidated.leftMap(_.getMessage) 68 | ) 69 | 70 | assertEquals(invalidUUIDStr.parseUUID.toOption, None) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /shared/src/test/scala/mouse/AnyFSyntaxTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | import cats.data.EitherT 25 | import cats.data.OptionT 26 | import cats.syntax.option._ 27 | import cats.syntax.either._ 28 | import cats.{~>, Id} 29 | 30 | class AnyFSyntaxTest extends MouseSuite { 31 | private val emptyK = new (List ~> List) { 32 | def apply[A](x: List[A]) = Nil 33 | } 34 | 35 | private val double = new (List ~> List) { 36 | def apply[A](x: List[A]) = x ::: x 37 | } 38 | 39 | private type E[B] = Either[String, B] 40 | 41 | private val toRight = new (Option ~> E) { 42 | def apply[A](x: Option[A]) = x.toRight("foo") 43 | } 44 | 45 | private val headOption = new (List ~> Option) { 46 | def apply[A](x: List[A]) = x.headOption 47 | } 48 | 49 | private val optionGet = new (Option ~> Id) { 50 | def apply[A](x: Option[A]) = x.get 51 | } 52 | 53 | test("AnyFSyntax.thrushK") { 54 | assertEquals(List(1, 2, 3).thrushK(emptyK), List.empty) 55 | 56 | assertEquals(List(5, 10).thrushK(double), List(5, 10, 5, 10)) 57 | 58 | assertEquals("thing".some.thrushK(toRight), Right("thing")) 59 | 60 | assertEquals( 61 | List("This") ||> double ||> headOption ||> optionGet, 62 | "This" 63 | ) 64 | } 65 | 66 | test("AnyFSyntax.asRightIn") { 67 | assertEquals(List(1).mapAsRight[String], List(1.asRight)) 68 | } 69 | 70 | test("AnyFSyntax.asLeftIn") { 71 | assertEquals(List(1).mapAsLeft[String], List(1.asLeft)) 72 | } 73 | 74 | test("AnyFSyntax.asSomeIn") { 75 | assertEquals(List(1).mapAsSome, List(1.some)) 76 | } 77 | 78 | test("AnyFSyntax.liftEitherT") { 79 | assertEquals(List(1).liftEitherT[String], EitherT(List(1.asRight[String]))) 80 | } 81 | 82 | test("AnyFSyntax.liftOptionT") { 83 | assertEquals(List(1).liftOptionT, OptionT(List(Option(1)))) 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /shared/src/main/scala/mouse/fboolean.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | import cats.{Functor, Monad} 25 | 26 | trait FBooleanSyntax { 27 | implicit final def FBooleanSyntaxMouse[F[_]](fBoolean: F[Boolean]): FBooleanOps[F] = 28 | new FBooleanOps[F](fBoolean) 29 | } 30 | 31 | final class FBooleanOps[F[_]](private val fBoolean: F[Boolean]) extends AnyVal { 32 | 33 | /** 34 | * Transforms this `F[Boolean]` by negating the `Boolean` 35 | */ 36 | def not(implicit F: Functor[F]): F[Boolean] = 37 | F.map(fBoolean)(b => !b) 38 | 39 | /** 40 | * Behaves like `&&` but inside the `F` context. 41 | * 42 | * Wont evaluate `other` unless this evaluates to `true` 43 | */ 44 | def andM(other: => F[Boolean])(implicit F: Monad[F]): F[Boolean] = 45 | F.flatMap(fBoolean) { 46 | case false => F.pure(false) 47 | case true => other 48 | } 49 | 50 | /** 51 | * Behaves like `||` but inside the `F` context. 52 | * 53 | * Wont evaluate `other` unless this evaluates to `false` 54 | */ 55 | def orM(other: => F[Boolean])(implicit F: Monad[F]): F[Boolean] = 56 | F.flatMap(fBoolean) { 57 | case true => F.pure(true) 58 | case false => other 59 | } 60 | 61 | /** 62 | * Evaluates the given effect if the boolean is `true`. 63 | * 64 | * Wont evaluate `f` unless this evaluates to `true`. 65 | */ 66 | def whenA[A](f: => F[A])(implicit F: Monad[F]): F[Unit] = F.flatMap(fBoolean) { 67 | case true => F.flatMap(f)(_ => F.unit) 68 | case false => F.unit 69 | } 70 | 71 | /** 72 | * Evaluates the given effect if the boolean is `false`. 73 | * 74 | * Wont evaluate `f` unless this evaluates to `false`. 75 | */ 76 | def unlessA[A](f: => F[A])(implicit F: Monad[F]): F[Unit] = F.flatMap(fBoolean) { 77 | case true => F.unit 78 | case false => F.flatMap(f)(_ => F.unit) 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /shared/src/test/scala/mouse/FNestedSyntaxTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | class FNestedSyntaxTest extends MouseSuite { 25 | private val listOption: List[Option[Int]] = 26 | List(Option(0), Option(1), Option(2)) 27 | 28 | private val listListOption: List[List[Option[Int]]] = 29 | List(List(Option(0), Option(1), Option(2)), List.empty[Option[Int]]) 30 | 31 | test("FNestedSyntax.mapNested2") { 32 | assertEquals( 33 | listOption.mapNested2(_ * 2), 34 | List(Option(0), Option(2), Option(4)) 35 | ) 36 | 37 | assertEquals( 38 | List.empty[Option[Int]].mapNested2(_ * 2), 39 | List.empty[Option[Int]] 40 | ) 41 | 42 | assertEquals( 43 | listListOption.mapNested2(_.toList), 44 | List(List(List(0), List(1), List(2)), List.empty[List[Int]]) 45 | ) 46 | } 47 | 48 | test("FNestedSyntax.flatMapNested2") { 49 | assertEquals( 50 | listOption.flatMapNested2(x => Option(x * 2)), 51 | List(Option(0), Option(2), Option(4)) 52 | ) 53 | 54 | assertEquals( 55 | List.empty[Option[Int]].flatMapNested2(x => Option(x * 2)), 56 | List.empty[Option[Int]] 57 | ) 58 | 59 | assertEquals( 60 | listListOption.flatMapNested2(x => List(x.toList)), 61 | List(List(List(0), List(1), List(2)), List.empty[List[Int]]) 62 | ) 63 | } 64 | 65 | test("FNestedSyntax.mapNested3") { 66 | val expected: List[List[Option[Int]]] = 67 | List(List(Option(0), Option(2), Option(4)), List.empty[Option[Int]]) 68 | 69 | assertEquals( 70 | listListOption.mapNested3(_ * 2), 71 | expected 72 | ) 73 | 74 | assertEquals( 75 | List.empty[List[Option[Int]]].mapNested3(_ * 2), 76 | List.empty[List[Option[Int]]] 77 | ) 78 | } 79 | 80 | test("FNestedSyntax.flatMapNested3") { 81 | val expected: List[List[Option[Int]]] = 82 | List(List(Option(0), Option(2), Option(4)), List.empty[Option[Int]]) 83 | 84 | assertEquals( 85 | listListOption.flatMapNested3(x => Option(x * 2)), 86 | expected 87 | ) 88 | 89 | assertEquals( 90 | List.empty[List[Option[Int]]].flatMapNested3(x => Option(x * 2)), 91 | List.empty[List[Option[Int]]] 92 | ) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /shared/src/test/scala/mouse/TrySyntaxTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | import cats.syntax.eq._ 25 | 26 | import org.typelevel.scalaccompat.annotation._ 27 | import org.scalacheck.Gen 28 | import org.scalacheck.Prop._ 29 | 30 | import scala.util.{Failure, Success, Try} 31 | 32 | class TrySyntaxTest extends MouseSuite { 33 | private val exceptionList = 34 | Seq( 35 | new RuntimeException(_: String), 36 | new IllegalArgumentException(_: String), 37 | new UnsupportedOperationException(_: String), 38 | new ArrayIndexOutOfBoundsException(_: String), 39 | new NumberFormatException(_: String) 40 | ) 41 | 42 | private def genThrowable: Gen[Throwable] = for { 43 | message <- Gen.alphaStr 44 | throwable <- Gen.oneOf(exceptionList) 45 | } yield throwable(message) 46 | 47 | private def genTrySuccess[T](genT: => Gen[T]): Gen[(T, Try[T])] = for { 48 | n <- genT 49 | tr <- Gen.oneOf(Try(n), Success(n)) 50 | } yield (n, tr) 51 | 52 | private def genTryInt = genTrySuccess[Int](Gen.choose(-10000, 10000)) 53 | 54 | private def genTryString = genTrySuccess[String](Gen.alphaStr) 55 | 56 | private def genTryBoolean = genTrySuccess[Boolean](Gen.oneOf(true, false)) 57 | 58 | private def genTryFailure[T]: Gen[(Throwable, Try[T])] = for { 59 | th <- genThrowable 60 | tr <- Gen.oneOf(Try[T](throw th), Failure[T](th)) 61 | } yield (th, tr) 62 | 63 | private def randomNumber(min: Int, max: Int): Int = Gen.choose(min, max).sample.get 64 | 65 | property("cata") { 66 | forAll(genTryInt) { case (n, t) => 67 | t.cata(identity, _ => n * n) === n 68 | } 69 | 70 | forAll(genTryFailure[Int]) { case (_, tr) => 71 | val rnd = randomNumber(50000, 60000) 72 | tr.cata(identity, _ => rnd) === rnd 73 | } 74 | } 75 | 76 | implicit class ExtraTest[A](@nowarn3 a: A) { 77 | def shouldBeA[T](implicit @nowarn212 ev: T =:= A): Unit = () 78 | } 79 | 80 | property("toEither") { 81 | forAll(genTryString) { case (msg, tr) => 82 | tr.toEither == Right(msg) 83 | } 84 | 85 | forAll(genTryFailure[String]) { case (th, tr) => 86 | tr.toEither == Left(th) 87 | } 88 | 89 | forAll(genTryBoolean) { case (_, t) => 90 | t.toEither.shouldBeA[Either[Throwable, Boolean]] 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /shared/src/main/scala/mouse/boolean.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | import cats.data.{EitherNel, NonEmptyList, Validated, ValidatedNec, ValidatedNel} 25 | import cats.{Applicative, ApplicativeError, Monoid} 26 | import mouse.BooleanSyntax.LiftToPartiallyApplied 27 | 28 | trait BooleanSyntax { 29 | implicit final def booleanSyntaxMouse(b: Boolean): BooleanOps = new BooleanOps(b) 30 | } 31 | 32 | class ApplyIfPartiallyApplied[A](b: Boolean, a: A) { 33 | @inline def apply[B >: A](f: B => B): B = if (b) f(a) else a 34 | } 35 | 36 | final class BooleanOps(private val b: Boolean) extends AnyVal { 37 | 38 | @inline def option[A](a: => A): Option[A] = fold(Some(a), None) 39 | 40 | @deprecated("Use `either` instead", "0.6") 41 | @inline def xor[L, R](l: => L, r: => R): Either[L, R] = either(l, r) 42 | 43 | @inline def either[L, R](l: => L, r: => R): Either[L, R] = fold(Right(r), Left(l)) 44 | 45 | @inline def eitherNel[L, R](ifFalse: => L, ifTrue: => R): EitherNel[L, R] = either(NonEmptyList.one(ifFalse), ifTrue) 46 | 47 | @inline def validated[L, R](ifFalse: => L, ifTrue: => R): Validated[L, R] = 48 | fold(Validated.valid(ifTrue), Validated.invalid(ifFalse)) 49 | 50 | @inline def validatedNec[L, R](ifFalse: => L, ifTrue: => R): ValidatedNec[L, R] = 51 | fold(Validated.validNec(ifTrue), Validated.invalidNec(ifFalse)) 52 | 53 | @inline def validatedNel[L, R](ifFalse: => L, ifTrue: => R): ValidatedNel[L, R] = 54 | fold(Validated.validNel(ifTrue), Validated.invalidNel(ifFalse)) 55 | 56 | @inline def fold[A](t: => A, f: => A): A = if (b) t else f 57 | 58 | @inline def valueOrZero[A](a: => A)(implicit M: Monoid[A]): A = if (b) a else M.empty 59 | 60 | @inline def zeroOrValue[A](a: => A)(implicit M: Monoid[A]): A = if (b) M.empty else a 61 | 62 | @inline def ??[A](a: => A)(implicit M: Monoid[A]): A = valueOrZero(a) 63 | 64 | @inline def !?[A](a: => A)(implicit M: Monoid[A]): A = zeroOrValue(a) 65 | 66 | @inline def valueOrPure[F[_], A](fa: => F[A])(a: => A)(implicit F: Applicative[F]): F[A] = if (b) fa else F.pure(a) 67 | 68 | @inline def applyIf[A](a: A): ApplyIfPartiallyApplied[A] = new ApplyIfPartiallyApplied[A](b, a) 69 | 70 | /** 71 | * That method has the by-value parameter `F[A]`. For by-name semantic on the `F[A]` parameter use [[whenAL]]. 72 | */ 73 | @inline def whenA[F[_], A](fa: F[A])(implicit F: Applicative[F]): F[Unit] = F.whenA(b)(fa) 74 | 75 | /** 76 | * The same as [[whenA]] except for by-name parameter `F[A]`. 77 | */ 78 | @inline def whenAL[F[_], A](fa: => F[A])(implicit F: Applicative[F]): F[Unit] = F.whenA(b)(fa) 79 | 80 | /** 81 | * That method has the by-value parameter `F[A]`. For by-name semantic on the `F[A]` parameter use [[unlessAL]]. 82 | */ 83 | @inline def unlessA[F[_], A](fa: F[A])(implicit F: Applicative[F]): F[Unit] = F.unlessA(b)(fa) 84 | 85 | /** 86 | * The same as [[unlessA]] except for by-name parameter `F[A]`. 87 | */ 88 | @inline def unlessAL[F[_], A](fa: => F[A])(implicit F: Applicative[F]): F[Unit] = F.unlessA(b)(fa) 89 | 90 | @inline def liftTo[F[_]]: LiftToPartiallyApplied[F] = new LiftToPartiallyApplied(b) 91 | 92 | } 93 | 94 | object BooleanSyntax { 95 | final private[mouse] class LiftToPartiallyApplied[F[_]](private val b: Boolean) extends AnyVal { 96 | def apply[E](ifFalse: => E)(implicit F: ApplicativeError[F, _ >: E]): F[Unit] = 97 | if (b) F.unit else F.raiseError(ifFalse) 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /shared/src/main/scala/mouse/string.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | import cats.data.Validated 25 | import cats.syntax.either._ 26 | 27 | trait StringSyntax { 28 | implicit def stringSyntaxMouse(s: String): StringOps = new StringOps(s) 29 | } 30 | 31 | final class StringOps(private val s: String) extends AnyVal { 32 | 33 | @inline def parseBoolean: IllegalArgumentException Either Boolean = 34 | Either.catchOnly[IllegalArgumentException](s.toBoolean) 35 | 36 | @inline def parseBooleanValidated: Validated[IllegalArgumentException, Boolean] = parseBoolean.toValidated 37 | 38 | @inline def parseBooleanOption: Option[Boolean] = parseBoolean.toOption 39 | 40 | @inline def parseByte: NumberFormatException Either Byte = parse[Byte](_.toByte) 41 | 42 | @inline def parseByteValidated: Validated[NumberFormatException, Byte] = parseByte.toValidated 43 | 44 | @inline def parseByteOption: Option[Byte] = parseByte.toOption 45 | 46 | @inline def parseDouble: NumberFormatException Either Double = parse[Double](_.toDouble) 47 | 48 | @inline def parseDoubleValidated: Validated[NumberFormatException, Double] = parseDouble.toValidated 49 | 50 | @inline def parseDoubleOption: Option[Double] = parseDouble.toOption 51 | 52 | @inline def parseFloat: NumberFormatException Either Float = parse[Float](_.toFloat) 53 | 54 | @inline def parseFloatValidated: Validated[NumberFormatException, Float] = parseFloat.toValidated 55 | 56 | @inline def parseFloatOption: Option[Float] = parseFloat.toOption 57 | 58 | @inline def parseInt: NumberFormatException Either Int = parse[Int](_.toInt) 59 | 60 | @inline def parseIntValidated: Validated[NumberFormatException, Int] = parseInt.toValidated 61 | 62 | @inline def parseIntOption: Option[Int] = parseInt.toOption 63 | 64 | @inline def parseLong: NumberFormatException Either Long = parse[Long](_.toLong) 65 | 66 | @inline def parseLongValidated: Validated[NumberFormatException, Long] = parseLong.toValidated 67 | 68 | @inline def parseLongOption: Option[Long] = parseLong.toOption 69 | 70 | @inline def parseShort: NumberFormatException Either Short = parse[Short](_.toShort) 71 | 72 | @inline def parseShortValidated: Validated[NumberFormatException, Short] = parseShort.toValidated 73 | 74 | @inline def parseShortOption: Option[Short] = parseShort.toOption 75 | 76 | @inline def parseBigInt: NumberFormatException Either BigInt = parse(BigInt.apply) 77 | 78 | @inline def parseBigIntValidated: Validated[NumberFormatException, BigInt] = parseBigInt.toValidated 79 | 80 | @inline def parseBigIntOption: Option[BigInt] = parseBigInt.toOption 81 | 82 | @inline def parseBigDecimal: NumberFormatException Either BigDecimal = parse(BigDecimal.apply) 83 | 84 | @inline def parseBigDecimalValidated: Validated[NumberFormatException, BigDecimal] = parseBigDecimal.toValidated 85 | 86 | @inline def parseBigDecimalOption: Option[BigDecimal] = parseBigDecimal.toOption 87 | 88 | private def parse[A](f: String => A): NumberFormatException Either A = Either.catchOnly[NumberFormatException](f(s)) 89 | 90 | /** 91 | * Wraps a `String` in `Throwable`. 92 | */ 93 | @inline def asThrowable: Throwable = new Throwable(s) 94 | 95 | /** 96 | * Wraps a `String` in `Error`. 97 | */ 98 | @inline def asError: Error = new Error(s) 99 | 100 | /** 101 | * Wraps a `String` in `Exception`. 102 | */ 103 | @inline def asException: Exception = new Exception(s) 104 | } 105 | -------------------------------------------------------------------------------- /shared/src/test/scala/mouse/FBooleanSyntaxTests.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | import cats.{Eval, Id} 25 | 26 | class FBooleanSyntaxTests extends MouseSuite { 27 | test("FBooleanSyntax.not") { 28 | assertEquals(Id(true).not, Id(false)) 29 | assertEquals(Id(false).not, Id(true)) 30 | } 31 | 32 | test("FBooleanSyntax.andM") { 33 | // Boolean logic. 34 | assertEquals(Id(true).andM(Id(true)), Id(true)) 35 | assertEquals(Id(true).andM(Id(false)), Id(false)) 36 | assertEquals(Id(false).andM(Id(true)), Id(false)) 37 | assertEquals(Id(false).andM(Id(false)), Id(false)) 38 | 39 | // Short-circuit. 40 | var evalWasEvaluated = false 41 | var functionWasCalled = false 42 | def other = { 43 | functionWasCalled = true 44 | Eval.later { 45 | evalWasEvaluated = true 46 | true 47 | } 48 | } 49 | 50 | Eval.now(false).andM(other) 51 | assertEquals(evalWasEvaluated, false) 52 | assertEquals(functionWasCalled, false) 53 | } 54 | 55 | test("FBooleanSyntax.orM") { 56 | // Boolean logic. 57 | assertEquals(Id(true).orM(Id(true)), Id(true)) 58 | assertEquals(Id(true).orM(Id(false)), Id(true)) 59 | assertEquals(Id(false).orM(Id(true)), Id(true)) 60 | assertEquals(Id(false).orM(Id(false)), Id(false)) 61 | 62 | // Short-circuit. 63 | var evalWasEvaluated = false 64 | var functionWasCalled = false 65 | def other = { 66 | functionWasCalled = true 67 | Eval.later { 68 | evalWasEvaluated = true 69 | true 70 | } 71 | } 72 | 73 | Eval.now(true).orM(other) 74 | assertEquals(evalWasEvaluated, false) 75 | assertEquals(functionWasCalled, false) 76 | } 77 | 78 | test("FBooleanSyntax.whenA (when true)") { 79 | var evalWasEvaluated = false 80 | var functionWasCalled = false 81 | def other = { 82 | functionWasCalled = true 83 | Eval.later { 84 | evalWasEvaluated = true 85 | } 86 | } 87 | 88 | Eval.now(true).whenA(other).value 89 | assertEquals(evalWasEvaluated, true) 90 | assertEquals(functionWasCalled, true) 91 | } 92 | 93 | test("FBooleanSyntax.whenA (when false)") { 94 | var evalWasEvaluated = false 95 | var functionWasCalled = false 96 | def other = { 97 | functionWasCalled = true 98 | Eval.later { 99 | evalWasEvaluated = true 100 | } 101 | } 102 | 103 | Eval.now(false).whenA(other).value 104 | assertEquals(evalWasEvaluated, false) 105 | assertEquals(functionWasCalled, false) 106 | } 107 | 108 | test("FBooleanSyntax.unlessA (when true)") { 109 | var evalWasEvaluated = false 110 | var functionWasCalled = false 111 | def other = { 112 | functionWasCalled = true 113 | Eval.later { 114 | evalWasEvaluated = true 115 | } 116 | } 117 | 118 | Eval.now(false).unlessA(other).value 119 | assertEquals(evalWasEvaluated, true) 120 | assertEquals(functionWasCalled, true) 121 | } 122 | 123 | test("FBooleanSyntax.unlessA (when false)") { 124 | var evalWasEvaluated = false 125 | var functionWasCalled = false 126 | def other = { 127 | functionWasCalled = true 128 | Eval.later { 129 | evalWasEvaluated = true 130 | } 131 | } 132 | 133 | Eval.now(true).unlessA(other).value 134 | assertEquals(evalWasEvaluated, false) 135 | assertEquals(functionWasCalled, false) 136 | } 137 | 138 | } 139 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Mouse 2 | 3 | ![Continuous Integration](https://github.com/typelevel/mouse/workflows/Continuous%20Integration/badge.svg) [![Maven Central](https://img.shields.io/maven-central/v/org.typelevel/mouse_2.12.svg)](https://maven-badges.herokuapp.com/maven-central/org.typelevel/mouse_2.12) 4 | 5 | Mouse is a small companion to the [Cats] functional programming library and the Scala standard library. 6 | 7 | The library arose from this [Cats issue] and is a [Typelevel member]. 8 | 9 | Mouse is published for Scala 2.12, 2.13 and 3.0. For Scala.jvm: 10 | ```scala 11 | "org.typelevel" %% "mouse" % "@VERSION@" 12 | ``` 13 | 14 | For scala.js 1.x: 15 | ```scala 16 | "org.typelevel" %%% "mouse" % "@VERSION@" 17 | ``` 18 | 19 | ### Scope of Library 20 | 21 | Provide enrichments to classes from the Scala standard library that convert to Cats datatypes, 22 | or otherwise improve the functional programming experience. 23 | 24 | ### Content 25 | 26 | Mouse includes enrichments for: 27 | 28 | - [Any](https://www.javadoc.io/doc/org.typelevel/mouse_2.13/latest/mouse/AnyOps.html) 29 | - [Boolean](https://www.javadoc.io/doc/org.typelevel/mouse_2.13/latest/mouse/BooleanOps.html) 30 | - [Double](https://www.javadoc.io/doc/org.typelevel/mouse_2.13/latest/mouse/DoubleOps.html) 31 | - [F\[A\]](https://www.javadoc.io/doc/org.typelevel/mouse_2.13/latest/mouse/AnyFOps.html) 32 | - [F\[Either\[A, B\]\]](https://www.javadoc.io/doc/org.typelevel/mouse_2.13/latest/mouse/FEitherOps.html) 33 | - [F\[Option\[A\]\]](https://www.javadoc.io/doc/org.typelevel/mouse_2.13/latest/mouse/FOptionOps.html) 34 | - [F\[G\[A\]\]](https://www.javadoc.io/doc/org.typelevel/mouse_2.13/latest/mouse/FNested2SyntaxOps.html) 35 | - [F\[G\[H\[A\]\]\]](https://www.javadoc.io/doc/org.typelevel/mouse_2.13/latest/mouse/FNested3SyntaxOps.html) 36 | - [F\[TupleN\]](https://www.javadoc.io/doc/org.typelevel/mouse_2.13/latest/mouse/FTupleNSyntax.html) 37 | - [Int](https://www.javadoc.io/doc/org.typelevel/mouse_2.13/latest/mouse/IntOps.html) 38 | - [Long](https://www.javadoc.io/doc/org.typelevel/mouse_2.13/latest/mouse/LongOps.html) 39 | - [Map\[K, V\]](https://www.javadoc.io/doc/org.typelevel/mouse_2.13/latest/mouse/MapOps.html) 40 | - [Option\[A\]](https://www.javadoc.io/doc/org.typelevel/mouse_2.13/latest/mouse/OptionOps.html) 41 | - [String](https://www.javadoc.io/doc/org.typelevel/mouse_2.13/latest/mouse/StringOps.html) 42 | - [Try\[A\]](https://www.javadoc.io/doc/org.typelevel/mouse_2.13/latest/mouse/TryOps.html) 43 | - [Tuple](https://www.javadoc.io/doc/org.typelevel/mouse_2.13/latest/mouse/TupleSyntax.html) 44 | 45 | #### Example: 46 | 47 | ```scala mdoc 48 | import mouse.all._ 49 | 50 | true.option("Its true!") 51 | 52 | def makeEven(i: Int) = (i % 2 == 1).applyIf(i)(_ - 1) 53 | 54 | val e1: Either[String, Int] = Left("failed") 55 | 56 | true.whenA(e1) 57 | 58 | false.whenA(e1) 59 | 60 | res0.cata(msg => s"Message received: ${msg}", "No message") 61 | 62 | "1.0".parseFloat 63 | 64 | "foo".parseIntValidated 65 | 66 | val t1 = scala.util.Try(new java.net.URL("https://www.github.com")) 67 | 68 | t1.cata(_ => s"URL is valid!", error => s"URL is invalid: ${error.getMessage}") 69 | 70 | t1.toEither 71 | 72 | val t2 = scala.util.Try(new java.net.URL("https//www.github.com")) 73 | 74 | t2.cata(_ => s"URL is valid!", error => s"URL is invalid: ${error.getMessage}") 75 | 76 | t2.toEither 77 | 78 | val intToBytes = 123456789.toByteArray 79 | 80 | val longToBase64 = 123456789L.toBase64 81 | 82 | 5.squared 83 | 84 | 1.5 |> (_.toInt) |> (_.toString) 85 | 86 | //lift a partial function into a total function to an Either, when you want to treat unhandled input cases as an error 87 | liftEither[Option[Int]]({case Some(n) => n}, a => s"Unexpected: $a")(Some(6)) 88 | 89 | val mapped = Map(1 -> 2, 3 -> 4).mapKeys(_ * 2) 90 | 91 | val foption = List(Option(1), Option(2), Option(4)).mapIn(_ * 2) 92 | 93 | val feither = List(Either.cond(true, 1, "0")).mapIn(_ * 2) 94 | 95 | val listOption = List(Option(1), Option(2)).mapNested2(_ * 2) 96 | 97 | val listOptionList = List(Option(List(1)), Option(List(2))).mapNested3(_ * 2) 98 | 99 | val tupleHead = (1, 2, 4, 8).head 100 | 101 | val tupleLast = (1, 2, 4, 8).last 102 | ``` 103 | 104 | ### Contributing 105 | 106 | Mouse is maintained by [@benhutchison] and [@danicheg]. 107 | 108 | Issues and pull requests are welcome. Code contributions should be aligned with the above scope to be included, and include unit tests. 109 | See [contributing guide] for more details. 110 | 111 | This project supports the [Scala code of conduct] and aims that its channels 112 | (mailing list, Gitter, Github, etc.) to be welcoming environments for everyone. 113 | 114 | 115 | [Cats]: https://github.com/typelevel/cats 116 | [Cats issue]: https://github.com/typelevel/cats/issues/791 117 | [@benhutchison]: https://github.com/benhutchison 118 | [@danicheg]: https://github.com/danicheg 119 | [Typelevel member]: http://typelevel.org/projects/ 120 | [contributing-guide]: ../contributing-guide/ 121 | [Scala code of conduct]: https://www.scala-lang.org/conduct/ -------------------------------------------------------------------------------- /shared/src/test/scala/mouse/BooleanSyntaxTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | import cats.data.{NonEmptyList, Validated} 25 | import cats.syntax.either._ 26 | 27 | class BooleanSyntaxTest extends MouseSuite { 28 | test("BooleanSyntax.option") { 29 | assertEquals(true.option(1), Option(1)) 30 | assertEquals(false.option(1), Option.empty[Int]) 31 | } 32 | 33 | test("BooleanSyntax.either") { 34 | assertEquals(true.either("error", 1), Either.right(1)) 35 | assertEquals(false.either("error", 1), Either.left("error")) 36 | } 37 | 38 | test("BooleanSyntax.eitherNel") { 39 | assertEquals(true.eitherNel("error", 1), Either.right(1)) 40 | assertEquals(false.eitherNel("error", 1), Either.left(NonEmptyList.one("error"))) 41 | } 42 | 43 | test("BooleanSyntax.validated") { 44 | assertEquals(true.validated("error", 1), Validated.valid(1)) 45 | assertEquals(false.validated("error", 1), Validated.invalid("error")) 46 | } 47 | 48 | test("BooleanSyntax.validatedNec") { 49 | assertEquals(true.validatedNec("error", 1), Validated.validNec(1)) 50 | assertEquals(false.validatedNec("error", 1), Validated.invalidNec("error")) 51 | } 52 | 53 | test("BooleanSyntax.validatedNel") { 54 | assertEquals(true.validatedNel("error", 1), Validated.validNel(1)) 55 | assertEquals(false.validatedNel("error", 1), Validated.invalidNel("error")) 56 | } 57 | 58 | test("BooleanSyntax.liftTo") { 59 | type F[A] = Either[String, A] 60 | 61 | assertEquals(true.liftTo[F]("error"), Either.right(())) 62 | assertEquals(false.liftTo[F]("error"), Either.left("error")) 63 | } 64 | 65 | test("BooleanSyntax.fold") { 66 | assertEquals(true.fold("t", "f"), "t") 67 | assertEquals(false.fold("t", "f"), "f") 68 | } 69 | 70 | test("BooleanSyntax.valueOrZero") { 71 | assertEquals(true.valueOrZero(Option(())), Option(())) 72 | assertEquals(false.valueOrZero(Option(())), Option.empty[Unit]) 73 | assertEquals(true.valueOrZero("Yellow"), "Yellow") 74 | assertEquals(false.valueOrZero("Yellow"), "") 75 | } 76 | 77 | test("BooleanSyntax.zeroOrValue") { 78 | assertEquals(true.zeroOrValue("Yellow"), "") 79 | assertEquals(false.zeroOrValue("Yellow"), "Yellow") 80 | } 81 | 82 | test("BooleanSyntax.??") { 83 | assertEquals(true.??("Yellow"), "Yellow") 84 | } 85 | 86 | test("BooleanSyntax.!?") { 87 | assertEquals(true.!?("Yellow"), "") 88 | } 89 | 90 | test("BooleanSyntax.valueOrPure") { 91 | assertEquals(true.valueOrPure(Option(1))(2), Some(1)) 92 | assertEquals(false.valueOrPure(Option(1))(2), Some(2)) 93 | } 94 | 95 | test("BooleanSyntax.applyIf") { 96 | def mutilate(x: CharSequence): CharSequence = x.subSequence(1, 2) 97 | 98 | assertEquals(true.applyIf("foo")(mutilate), "o") 99 | assertEquals(false.applyIf("foo")(mutilate), "foo") 100 | } 101 | 102 | test("BooleanSyntax.whenA") { 103 | assertEquals(true.whenA("foo".asLeft[Int]), Left("foo")) 104 | assertEquals(false.whenA("foo".asLeft[Int]), Right(())) 105 | } 106 | 107 | test("BooleanSyntax.unlessA") { 108 | assertEquals(true.unlessA("foo".asLeft[Int]), Right(())) 109 | assertEquals(false.unlessA("foo".asLeft[Int]), Left("foo")) 110 | } 111 | 112 | test("BooleanSyntax.whenAL") { 113 | var lazinessChecker: Int = 0 114 | 115 | assertEquals(true.whenAL("foo".asLeft[Int]), Left("foo")) 116 | assertEquals( 117 | false.whenAL(Either.left { 118 | lazinessChecker = 1 119 | "foo" 120 | }), 121 | Right(()) 122 | ) 123 | assertEquals(lazinessChecker, 0) 124 | } 125 | 126 | test("BooleanSyntax.unlessAL") { 127 | var lazinessChecker: Int = 0 128 | 129 | assertEquals(false.unlessAL("foo".asLeft[Int]), Left("foo")) 130 | assertEquals( 131 | true.unlessAL(Either.left { 132 | lazinessChecker = 1 133 | "foo" 134 | }), 135 | Right(()) 136 | ) 137 | assertEquals(lazinessChecker, 0) 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mouse ![Continuous Integration](https://github.com/typelevel/mouse/workflows/Continuous%20Integration/badge.svg) [![Maven Central](https://img.shields.io/maven-central/v/org.typelevel/mouse_2.12.svg)](https://maven-badges.herokuapp.com/maven-central/org.typelevel/mouse_2.12) Cats friendly 2 | 3 | Mouse is a small companion to the [Cats](https://github.com/typelevel/cats) functional programming library and the Scala standard library. 4 | 5 | The library arose from this [Cats issue](https://github.com/typelevel/cats/issues/791) and is a [Typelevel member](http://typelevel.org/projects/). 6 | 7 | Mouse is published for Scala 2.12, 2.13 and 3.0. For Scala.jvm: 8 | 9 | `"org.typelevel" %% "mouse" % version` 10 | 11 | For scala.js 1.x: 12 | 13 | `"org.typelevel" %%% "mouse" % version` 14 | 15 | Learn more about Mouse at https://typelevel.org/mouse/. 16 | 17 | ## Content 18 | 19 | Mouse includes enrichments for: 20 | 21 | - [Any](./shared/src/main/scala/mouse/any.scala) 22 | - [Boolean](./shared/src/main/scala/mouse/boolean.scala) 23 | - [Double](./shared/src/main/scala/mouse/double.scala) 24 | - [F\[A\] for any F, A](./shared/src/main/scala/mouse/anyf.scala) 25 | - [F\[Either\[A, B\]\]](./shared/src/main/scala/mouse/feither.scala) 26 | - [F\[Option\[A\]\]](./shared/src/main/scala/mouse/foption.scala) 27 | - [F\[G\[A\]\]](./shared/src/main/scala/mouse/fnested.scala) 28 | - [F\[G\[H\[A\]\]\]](./shared/src/main/scala/mouse/fnested.scala) 29 | - [F\[TupleN\]](./shared/src/main/scala/mouse/ftuple.scala) 30 | - [Int](./shared/src/main/scala/mouse/int.scala) 31 | - [List](./shared/src/main/scala/mouse/list.scala) 32 | - [Long](./shared/src/main/scala/mouse/long.scala) 33 | - [Map](./shared/src/main/scala/mouse/map.scala) 34 | - [Option](./shared/src/main/scala/mouse/option.scala) 35 | - [Set](./shared/src/main/scala/mouse/set.scala) 36 | - [String](./shared/src/main/scala/mouse/string.scala) 37 | - [Try](./shared/src/main/scala/mouse/try.scala) 38 | - [Tuple](./shared/src/main/scala-2/src/main/scala/mouse/tuple.scala) 39 | 40 | #### Example: 41 | 42 | ```scala 43 | scala> import mouse.all._ 44 | import mouse.all._ 45 | 46 | scala> true.option("Its true!") 47 | res0: Option[String] = Some(Its true!) 48 | 49 | scala> def makeEven(i: Int) = (i % 2 == 1).applyIf(i)(_ - 1) 50 | def makeEven(i: Int): Int 51 | 52 | scala> val e1: Either[String, Int] = Left("failed") 53 | e1: Either[String,Int] = Left(failed) 54 | 55 | scala> true.whenA(e1) 56 | res0: Either[String,Unit] = Left(failed) 57 | 58 | scala> false.whenA(e1) 59 | res1: Either[String,Unit] = Right(()) 60 | 61 | scala> res0.cata(msg => s"Message received: ${msg}", "No message") 62 | res1: String = Message received: Its true! 63 | 64 | scala> "1.0".parseFloat 65 | res0: Either[NumberFormatException, Float] = Right(1.0F) 66 | 67 | scala> "foo".parseIntValidated 68 | res1: cats.data.Validated[NumberFormatException,Int] = Invalid(java.lang.NumberFormatException: For input string: "foo") 69 | 70 | scala> val t1 = scala.util.Try(new java.net.URL("https://www.github.com")) 71 | t1: scala.util.Try[java.net.URL] = Success(https://www.github.com) 72 | 73 | scala> t1.cata(msg => s"URL is valid!", error => s"URL is invalid: ${error.getMessage}") 74 | res1: String = URL is valid! 75 | 76 | scala> t1.toEither 77 | res2: Either[Throwable,java.net.URL] = Right(https://www.github.com) 78 | 79 | scala> val t2 = scala.util.Try(new java.net.URL("https//www.github.com")) 80 | t2: scala.util.Try[java.net.URL] = Failure(java.net.MalformedURLException: no protocol: https//www.github.com) 81 | 82 | scala> t2.cata(msg => s"URL is valid!", error => s"URL is invalid: ${error.getMessage}") 83 | res3: String = URL is invalid: no protocol: https//www.github.com 84 | 85 | scala> t2.toEither 86 | res4: Either[Throwable,java.net.URL] = Left(java.net.MalformedURLException: no protocol: https//www.github.com) 87 | 88 | scala> val intToBytes = 123456789.toByteArray 89 | intToBytes: Array[Byte] = Array(7, 91, -51, 21) 90 | 91 | scala> val longToBase64 = 123456789L.toBase64 92 | longToBase64: String = AAAAAAdbzRU 93 | 94 | scala> 5.squared 95 | res0: Int = 25 96 | 97 | scala> 1.5 |> (_.toInt) |> (_.toString) 98 | res0: String = 1 99 | 100 | //lift a partial function into a total function to an Either, when you want to treat unhandled input cases as an error 101 | scala> liftEither[Option[Int]]({case Some(n) => n}, a => s"Unexpected: $a")(Some(6)) 102 | res0: Either[String,Int] = Right(6) 103 | 104 | scala> val mapped = Map(1 -> 2, 3 -> 4).mapKeys(_ * 2) 105 | mapped: Map[Int,Int] = Map(2 -> 2, 6 -> 4) 106 | 107 | scala> val foption = List(Option(1), Option(2), Option(4)).mapIn(_ * 2) 108 | foption: List[Option[Int]] = List(Some(2), Some(4), Some(8)) 109 | 110 | scala> val feither = List(Either.cond(true, 1, "0")).mapIn(_ * 2) 111 | feither: List[Either[String, Int]] = List(Right(2)) 112 | 113 | scala> val listOption = List(Option(1), Option(2)).mapNested2(_ * 2) 114 | listOption: List[Option[Int]] = List(Some(2), Some(4)) 115 | 116 | scala> val listOptionList = List(Option(List(1)), Option(List(2))).mapNested3(_ * 2) 117 | listOptionList: List[Option[List[Int]]] = List(Some(List(2)), Some(List(4))) 118 | 119 | scala> val tupleHead = (1, 2, 4, 8).head 120 | tupleHead: Int = 1 121 | 122 | scala> val tupleLast = (1, 2, 4, 8).last 123 | tupleLast: Int = 8 124 | 125 | scala> Set(0).tailOrEmpty 126 | val res3: Set[Int] = Set() 127 | 128 | scala> Nil.tailOrEmpty 129 | val res1: List[Nothing] = List() 130 | 131 | scala> Set(0).tailOption 132 | val res4: Option[cats.data.NonEmptySet[Int]] = None 133 | 134 | scala> List(0,1,2).tailOption 135 | val res5: Option[cats.data.NonEmptyList[Int]] = Some(NonEmptyList(1, 2)) 136 | ``` 137 | 138 | ## Scope of Library 139 | 140 | - Provide enrichments to classes from the Scala standard library that convert to Cats datatypes, 141 | or otherwise improve the functional programming experience. 142 | 143 | ## Contributing 144 | 145 | Mouse is maintained by @benhutchison and @danicheg. 146 | 147 | Issues and pull requests are welcome. Code contributions should be aligned with the above scope to be included, and include unit tests. 148 | See [contributing guide](./DEV.md) for more details. 149 | 150 | This project supports the [Scala code of conduct](https://www.scala-lang.org/conduct/) and aims that its channels 151 | (mailing list, Gitter, github, etc.) to be welcoming environments for everyone. 152 | -------------------------------------------------------------------------------- /shared/src/main/scala/mouse/foption.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | import cats.data.EitherT 25 | import cats.data.OptionT 26 | import cats.{Applicative, FlatMap, Functor, Monad, MonadError, MonadThrow, Traverse} 27 | 28 | trait FOptionSyntax { 29 | implicit final def FOptionSyntaxMouse[F[_], A](foa: F[Option[A]]): FOptionOps[F, A] = new FOptionOps(foa) 30 | 31 | def noneF[F[_], A](implicit F: Applicative[F]): F[Option[A]] = 32 | F.pure(None) 33 | 34 | def someF[F[_], A](a: => A)(implicit F: Applicative[F]): F[Option[A]] = 35 | F.pure(Some(a)) 36 | } 37 | 38 | private[mouse] object FOptionSyntax extends FOptionSyntax 39 | 40 | final class FOptionOps[F[_], A](private val foa: F[Option[A]]) extends AnyVal { 41 | 42 | def cata[B](default: => B, f: A => B)(implicit F: Functor[F]): F[B] = 43 | F.map(foa)(_.fold(default)(f)) 44 | 45 | def cataF[B](default: => F[B], f: A => F[B])(implicit F: FlatMap[F]): F[B] = 46 | F.flatMap(foa)(_.fold(default)(f)) 47 | 48 | def existsIn(f: A => Boolean)(implicit F: Functor[F]): F[Boolean] = 49 | F.map(foa)(_.exists(f)) 50 | 51 | def existsF(f: A => F[Boolean])(implicit F: Monad[F]): F[Boolean] = 52 | F.flatMap(foa) { 53 | case None => F.pure(false) 54 | case Some(a) => f(a) 55 | } 56 | 57 | def filterIn(f: A => Boolean)(implicit F: Functor[F]): F[Option[A]] = 58 | F.map(foa)(_.filter(f)) 59 | 60 | def filterF(f: A => F[Boolean])(implicit F: Monad[F]): F[Option[A]] = 61 | F.flatMap(foa) { 62 | case None => F.pure(None) 63 | case s @ Some(v) => 64 | F.map(f(v)) { 65 | case true => s 66 | case _ => None 67 | } 68 | } 69 | 70 | def flatMapIn[B](f: A => Option[B])(implicit F: Functor[F]): F[Option[B]] = 71 | F.map(foa)(_.flatMap(f)) 72 | 73 | def flatMapOrKeepIn[B >: A](pfa: PartialFunction[A, Option[B]])(implicit F: Functor[F]): F[Option[B]] = 74 | F.map(foa)(_.flatMap(a => pfa.applyOrElse(a, Option[B](_)))) 75 | 76 | def flatMapF[B](f: A => F[Option[B]])(implicit F: Monad[F]): F[Option[B]] = 77 | F.flatMap(foa)(_.fold(F.pure(Option.empty[B]))(f)) 78 | 79 | def foldIn[B](default: => B)(f: A => B)(implicit F: Functor[F]): F[B] = 80 | cata(default, f) 81 | 82 | def foldF[B](default: => F[B])(f: A => F[B])(implicit F: FlatMap[F]): F[B] = 83 | cataF(default, f) 84 | 85 | def forallIn(f: A => Boolean)(implicit F: Functor[F]): F[Boolean] = 86 | F.map(foa)(_.forall(f)) 87 | 88 | def forallF(f: A => F[Boolean])(implicit F: Monad[F]): F[Boolean] = 89 | F.flatMap(foa) { 90 | case None => F.pure(true) 91 | case Some(a) => f(a) 92 | } 93 | 94 | def getOrElse[B >: A](a: => B)(implicit F: Functor[F]): F[B] = 95 | F.map(foa)(_.fold(a)(identity)) 96 | 97 | def getOrRaise[E](e: => E)(implicit F: MonadError[F, _ >: E]): F[A] = 98 | getOrElseF(F.raiseError(e)) 99 | 100 | def getOrRaiseMsg(msg: => String)(implicit F: MonadThrow[F]): F[A] = 101 | getOrRaise(new RuntimeException(msg)) 102 | 103 | def getOrElseF[B >: A](fa: => F[B])(implicit F: Monad[F]): F[B] = 104 | F.flatMap(foa)(_.fold(fa)(F.pure)) 105 | 106 | def mapIn[B](f: A => B)(implicit F: Functor[F]): F[Option[B]] = 107 | F.map(foa)(_.map(f)) 108 | 109 | def mapOrKeepIn[B >: A](pf: PartialFunction[A, B])(implicit F: Functor[F]): F[Option[B]] = 110 | F.map(foa)(_.map(a => pf.applyOrElse(a, identity[B]))) 111 | 112 | def asIn[B](b: => B)(implicit F: Functor[F]): F[Option[B]] = 113 | mapIn(_ => b) 114 | 115 | def voidIn(implicit F: Functor[F]): F[Option[Unit]] = 116 | asIn(()) 117 | 118 | def orElseIn(default: Option[A])(implicit F: Functor[F]): F[Option[A]] = 119 | F.map(foa) { 120 | case None => default 121 | case x => x 122 | } 123 | 124 | def orElseF(defaultF: => F[Option[A]])(implicit F: Monad[F]): F[Option[A]] = 125 | F.flatMap(foa) { 126 | case None => defaultF 127 | case x => F.pure(x) 128 | } 129 | 130 | def toLeftIn[R](right: => R)(implicit F: Functor[F]): F[Either[A, R]] = 131 | F.map(foa) { 132 | case None => Right(right) 133 | case Some(a) => Left(a) 134 | } 135 | 136 | def toLeftInF[R](right: => F[R])(implicit F: Monad[F]): F[Either[A, R]] = 137 | F.flatMap(foa) { 138 | case None => F.map(right)(Right(_)) 139 | case Some(a) => F.pure(Left(a)) 140 | } 141 | 142 | def toRightIn[L](left: => L)(implicit F: Functor[F]): F[Either[L, A]] = 143 | F.map(foa) { 144 | case None => Left(left) 145 | case Some(a) => Right(a) 146 | } 147 | 148 | def toRightInF[L](left: => F[L])(implicit F: Monad[F]): F[Either[L, A]] = 149 | F.flatMap(foa) { 150 | case None => F.map(left)(Left(_)) 151 | case Some(a) => F.pure(Right(a)) 152 | } 153 | 154 | def traverseIn[G[_]: Applicative, B](f: A => G[B])(implicit F: Functor[F]): F[G[Option[B]]] = 155 | F.map(foa)(a => Traverse[Option].traverse(a)(f)) 156 | 157 | def traverseF[G[_]: Applicative, B](f: A => G[B])(implicit F: Traverse[F]): G[F[Option[B]]] = 158 | F.traverse(foa)(a => Traverse[Option].traverse(a)(f)) 159 | 160 | def liftOptionT: OptionT[F, A] = 161 | OptionT(foa) 162 | 163 | def liftEitherT[E](ifNone: => E)(implicit F: Functor[F]): EitherT[F, E, A] = 164 | EitherT.fromOptionF(foa, ifNone) 165 | } 166 | -------------------------------------------------------------------------------- /shared/src/main/scala/mouse/feither.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | import cats.data.EitherT 25 | import cats.instances.either._ 26 | import cats.{Applicative, FlatMap, Functor, Monad, MonadError, MonadThrow, Traverse} 27 | 28 | trait FEitherSyntax { 29 | implicit final def FEitherSyntaxMouse[F[_], L, R](felr: F[Either[L, R]]): FEitherOps[F, L, R] = 30 | new FEitherOps(felr) 31 | 32 | def asLeftF[F[_], L, R](left: => L)(implicit F: Applicative[F]): F[Either[L, R]] = 33 | F.pure(Left(left)) 34 | 35 | def asRightF[F[_], L, R](right: => R)(implicit F: Applicative[F]): F[Either[L, R]] = 36 | F.pure(Right(right)) 37 | } 38 | 39 | private[mouse] object FEitherSyntax extends FEitherSyntax 40 | 41 | final class FEitherOps[F[_], L, R](private val felr: F[Either[L, R]]) extends AnyVal { 42 | def cata[A](left: L => A, right: R => A)(implicit F: Functor[F]): F[A] = 43 | F.map(felr)(_.fold(left, right)) 44 | 45 | def cataF[A](left: L => F[A], right: R => F[A])(implicit F: FlatMap[F]): F[A] = 46 | F.flatMap(felr)(_.fold(left, right)) 47 | 48 | def flatMapIn[A >: L, B](f: R => Either[A, B])(implicit F: Functor[F]): F[Either[A, B]] = 49 | F.map(felr)(_.flatMap(f)) 50 | 51 | def flatMapOrKeepIn[A >: L, B >: R](pfa: PartialFunction[R, Either[A, B]])(implicit F: Functor[F]): F[Either[A, B]] = 52 | F.map(felr)(_.flatMap(a => pfa.applyOrElse(a, Right[A, B](_)))) 53 | 54 | def flatMapF[A >: L, B](f: R => F[Either[A, B]])(implicit F: Monad[F]): F[Either[A, B]] = 55 | F.flatMap(felr) { 56 | case l @ Left(_) => F.pure(l.asInstanceOf[Left[A, B]]) 57 | case Right(value) => f(value) 58 | } 59 | 60 | def foldIn[A](left: L => A)(right: R => A)(implicit F: Functor[F]): F[A] = 61 | cata(left, right) 62 | 63 | def foldF[A](left: L => F[A])(right: R => F[A])(implicit F: FlatMap[F]): F[A] = 64 | cataF(left, right) 65 | 66 | def getOrElseIn[A >: R](right: => A)(implicit F: Functor[F]): F[A] = 67 | F.map(felr)(_.fold(_ => right, identity)) 68 | 69 | def getOrRaise[E](e: => E)(implicit F: MonadError[F, _ >: E]): F[R] = 70 | getOrElseF(F.raiseError(e)) 71 | 72 | def getOrRaiseMsg(msg: => String)(implicit F: MonadThrow[F]): F[R] = 73 | getOrRaise[Throwable](new RuntimeException(msg)) 74 | 75 | def getOrElseF[A >: R](right: => F[A])(implicit F: Monad[F]): F[A] = 76 | F.flatMap(felr)(_.fold(_ => right, F.pure)) 77 | 78 | def leftFlatMapIn[A, B >: R](f: L => Either[A, B])(implicit F: Functor[F]): F[Either[A, B]] = 79 | F.map(felr) { 80 | case Left(value) => f(value) 81 | case r @ Right(_) => r.asInstanceOf[Right[A, B]] 82 | } 83 | 84 | def leftFlatMapOrKeepIn[LL >: L, RR >: R]( 85 | pf: PartialFunction[L, Either[LL, RR]] 86 | )(implicit F: Functor[F]): F[Either[LL, RR]] = 87 | F.map(felr) { 88 | case l @ Left(a) => pf.applyOrElse(a, (_: L) => l) 89 | case r: Right[L, R] => r.asInstanceOf[Right[LL, RR]] 90 | } 91 | 92 | def leftFlatMapF[A, B >: R](f: L => F[Either[A, B]])(implicit F: Monad[F]): F[Either[A, B]] = 93 | F.flatMap(felr) { 94 | case Left(left) => f(left) 95 | case r @ Right(_) => F.pure(r.asInstanceOf[Right[A, B]]) 96 | } 97 | 98 | def leftMapIn[A](f: L => A)(implicit F: Functor[F]): F[Either[A, R]] = 99 | F.map(felr) { 100 | case Left(value) => Left(f(value)) 101 | case r @ Right(_) => r.asInstanceOf[Right[A, R]] 102 | } 103 | 104 | def leftMapOrKeepIn[LL >: L](pf: PartialFunction[L, LL])(implicit F: Functor[F]): F[Either[LL, R]] = 105 | F.map(felr) { 106 | case Left(a) => Left(pf.applyOrElse(a, identity[LL])) 107 | case r: Right[L, R] => r.asInstanceOf[Right[LL, R]] 108 | } 109 | 110 | def leftAsIn[B](b: => B)(implicit F: Functor[F]): F[Either[B, R]] = 111 | leftMapIn(_ => b) 112 | 113 | def leftVoidIn(implicit F: Functor[F]): F[Either[Unit, R]] = 114 | leftAsIn(()) 115 | 116 | def leftWidenIn[A >: L](implicit F: Functor[F]): F[Either[A, R]] = 117 | F.map(felr) { 118 | case l @ Left(_) => l.asInstanceOf[Left[A, R]] 119 | case r @ Right(_) => r.asInstanceOf[Right[A, R]] 120 | } 121 | 122 | def leftTraverseIn[G[_], A](f: L => G[A])(implicit F: Functor[F], G: Applicative[G]): F[G[Either[A, R]]] = 123 | F.map(felr) { 124 | case Left(left) => G.map(f(left))(Left(_)) 125 | case r @ Right(_) => G.pure(r.asInstanceOf[Right[A, R]]) 126 | } 127 | 128 | def leftTraverseF[G[_], A](f: L => G[A])(implicit F: Traverse[F], G: Applicative[G]): G[F[Either[A, R]]] = 129 | F.traverse(felr) { 130 | case Left(left) => G.map(f(left))(Left(_)) 131 | case r @ Right(_) => G.pure(r.asInstanceOf[Right[A, R]]) 132 | } 133 | 134 | def mapIn[A](f: R => A)(implicit F: Functor[F]): F[Either[L, A]] = 135 | F.map(felr)(_.map(f)) 136 | 137 | def mapOrKeepIn[A >: R](pf: PartialFunction[R, A])(implicit F: Functor[F]): F[Either[L, A]] = 138 | F.map(felr)(_.map(a => pf.applyOrElse(a, identity[A]))) 139 | 140 | def asIn[B](b: => B)(implicit F: Functor[F]): F[Either[L, B]] = 141 | mapIn(_ => b) 142 | 143 | def voidIn(implicit F: Functor[F]): F[Either[L, Unit]] = 144 | asIn(()) 145 | 146 | def bimapIn[A, B](left: L => A, right: R => B)(implicit F: Functor[F]): F[Either[A, B]] = 147 | F.map(felr) { 148 | case Left(value) => Left(left(value)) 149 | case Right(value) => Right(right(value)) 150 | } 151 | 152 | def swapIn(implicit F: Functor[F]): F[Either[R, L]] = 153 | F.map(felr)(_.swap) 154 | 155 | def mergeIn[A >: L](implicit ev: R <:< A, F: Functor[F]): F[A] = 156 | F.map(felr)(_.fold(identity, ev)) 157 | 158 | def orElseIn[A >: L, B >: R](f: => Either[A, B])(implicit F: Functor[F]): F[Either[A, B]] = 159 | F.map(felr) { 160 | case r: Right[L, R] => r: Either[A, B] 161 | case _ => f 162 | } 163 | 164 | def orElseF[A >: L, B >: R](f: => F[Either[A, B]])(implicit F: Monad[F]): F[Either[A, B]] = 165 | F.flatMap(felr) { 166 | case r: Right[L, R] => F.pure[Either[A, B]](r) 167 | case _ => f 168 | } 169 | 170 | def toOptionIn(implicit F: Functor[F]): F[Option[R]] = 171 | F.map(felr)(_.toOption) 172 | 173 | def traverseIn[G[_]: Applicative, A](f: R => G[A])(implicit F: Functor[F]): F[G[Either[L, A]]] = 174 | F.map(felr)(elr => catsStdInstancesForEither[L].traverse(elr)(f)) 175 | 176 | def traverseF[G[_]: Applicative, A](f: R => G[A])(implicit F: Traverse[F]): G[F[Either[L, A]]] = 177 | F.traverse(felr)(elr => catsStdInstancesForEither[L].traverse(elr)(f)) 178 | 179 | def liftEitherT: EitherT[F, L, R] = 180 | EitherT(felr) 181 | 182 | def widenIn[A >: R](implicit F: Functor[F]): F[Either[L, A]] = 183 | F.map(felr) { 184 | case l @ Left(_) => l.asInstanceOf[Left[L, A]] 185 | case r @ Right(_) => r.asInstanceOf[Right[L, A]] 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /shared/src/test/scala/mouse/StringSyntaxTests.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | import cats.syntax.all._ 25 | import org.scalacheck.Arbitrary._ 26 | import org.scalacheck.Prop._ 27 | import org.scalacheck.{Arbitrary, Gen} 28 | 29 | class StringSyntaxTests extends MouseSuite { 30 | test("StringOps.parseInt") { 31 | assertEquals("123".parseInt, 123.asRight[NumberFormatException]) 32 | assertEquals("blah".parseInt.leftMap(_.getMessage.replace("string ", "string: ")), 33 | "For input string: \"blah\"".asLeft 34 | ) 35 | } 36 | 37 | property("scalacheck-suite: StringOps.parseInt") { 38 | forAll { (i: Int) => 39 | assertEquals(i.toString.parseInt, i.asRight[NumberFormatException]) 40 | } 41 | 42 | forAll { (i: Int) => 43 | assertEquals(i.toString.parseInt.toOption, i.toString.parseIntOption) 44 | } 45 | 46 | forAll { (i: Int) => 47 | assertEquals(i.toString.parseInt.toValidated, i.toString.parseIntValidated) 48 | } 49 | } 50 | 51 | test("StringOps.parseLong") { 52 | assertEquals("123".parseLong, 123L.asRight[NumberFormatException]) 53 | assertEquals("blah".parseLong.leftMap(_.getMessage.replace("string ", "string: ")), 54 | "For input string: \"blah\"".asLeft 55 | ) 56 | } 57 | 58 | property("scalacheck-suite: StringOps.parseLong") { 59 | forAll { (i: Long) => 60 | assertEquals(i.toString.parseLong, i.asRight[NumberFormatException]) 61 | } 62 | 63 | forAll { (i: Long) => 64 | assertEquals(i.toString.parseLong.toOption, i.toString.parseLongOption) 65 | } 66 | 67 | forAll { (i: Long) => 68 | assertEquals(i.toString.parseLong.toValidated, i.toString.parseLongValidated) 69 | } 70 | } 71 | 72 | test("StringOps.parseShort") { 73 | assertEquals("123".parseShort, 123.toShort.asRight[NumberFormatException]) 74 | assertEquals("blah".parseShort.leftMap(_.getMessage.replace("string ", "string: ")), 75 | "For input string: \"blah\"".asLeft 76 | ) 77 | } 78 | 79 | property("scalacheck-suite: StringOps.parseShort") { 80 | forAll { (i: Short) => 81 | assertEquals(i.toString.parseShort, i.asRight[NumberFormatException]) 82 | } 83 | 84 | forAll { (i: Short) => 85 | assertEquals(i.toString.parseShort.toOption, i.toString.parseShortOption) 86 | } 87 | 88 | forAll { (i: Short) => 89 | assertEquals(i.toString.parseShort.toValidated, i.toString.parseShortValidated) 90 | } 91 | } 92 | 93 | test("StringOps.parseDouble") { 94 | assertEquals("123.1".parseDouble, 123.1.asRight[NumberFormatException]) 95 | assertEquals("blah".parseDouble.leftMap(_.getMessage.replace("string ", "string: ")), 96 | "For input string: \"blah\"".asLeft 97 | ) 98 | } 99 | 100 | property("scalacheck-suite: StringOps.parseDouble") { 101 | forAll { (i: Double) => 102 | assertEquals(i.toString.parseDouble, i.asRight[NumberFormatException]) 103 | } 104 | 105 | forAll { (i: Double) => 106 | assertEquals(i.toString.parseDouble.toOption, i.toString.parseDoubleOption) 107 | } 108 | 109 | forAll { (i: Double) => 110 | assertEquals(i.toString.parseDouble.toValidated, i.toString.parseDoubleValidated) 111 | } 112 | } 113 | 114 | test("StringOps.parseFloat") { 115 | assertEquals("blah".parseFloat.leftMap(_.getMessage.replace("string ", "string: ")), 116 | "For input string: \"blah\"".asLeft 117 | ) 118 | } 119 | 120 | property("scalacheck-suite: StringOps.parseFloat") { 121 | forAll { (i: Float) => 122 | assertEquals(i.toString.parseFloat, i.asRight[NumberFormatException]) 123 | } 124 | 125 | forAll { (i: Float) => 126 | assertEquals(i.toString.parseFloat.toOption, i.toString.parseFloatOption) 127 | } 128 | 129 | forAll { (i: Float) => 130 | assertEquals(i.toString.parseFloat.toValidated, i.toString.parseFloatValidated) 131 | } 132 | } 133 | 134 | test("StringOps.parseByte") { 135 | assertEquals("123".parseByte, 123.toByte.asRight[NumberFormatException]) 136 | assertEquals("blah".parseByte.leftMap(_.getMessage.replace("string ", "string: ")), 137 | "For input string: \"blah\"".asLeft 138 | ) 139 | } 140 | 141 | property("scalacheck-suite: StringOps.parseByte") { 142 | forAll { (i: Byte) => 143 | assertEquals(i.toString.parseByte, i.asRight[NumberFormatException]) 144 | } 145 | 146 | forAll { (i: Byte) => 147 | assertEquals(i.toString.parseByte.toOption, i.toString.parseByteOption) 148 | } 149 | 150 | forAll { (i: Byte) => 151 | assertEquals(i.toString.parseByte.toValidated, i.toString.parseByteValidated) 152 | } 153 | } 154 | 155 | test("StringOps.parseBigInt") { 156 | assertEquals("123".parseBigInt, BigInt("123").asRight[NumberFormatException]) 157 | assertEquals("blah".parseBigInt.leftMap(_.getMessage.replace("string ", "string: ")), 158 | "For input string: \"blah\"".asLeft 159 | ) 160 | } 161 | 162 | property("scalacheck-suite: StringOps.parseBigInt") { 163 | forAll { (i: BigInt) => 164 | assertEquals(i.toString.parseBigInt, i.asRight[NumberFormatException]) 165 | } 166 | 167 | forAll { (i: BigInt) => 168 | assertEquals(i.toString.parseBigInt.toOption, i.toString.parseBigIntOption) 169 | } 170 | 171 | forAll { (i: BigInt) => 172 | assertEquals(i.toString.parseBigInt.toValidated, i.toString.parseBigIntValidated) 173 | } 174 | } 175 | 176 | test("StringOps.parseBigDecimal") { 177 | assertEquals("123.45".parseBigDecimal, BigDecimal("123.45").asRight[NumberFormatException]) 178 | 179 | // We assert the class of the exception because the error message differs between the JVM and JS 180 | assertEquals("blah".parseBigDecimal.leftMap(_.getClass.toString), classOf[NumberFormatException].toString.asLeft) 181 | } 182 | 183 | property("scalacheck-suite: StringOps.parseBigDecimal") { 184 | forAll { (i: BigDecimal) => 185 | assertEquals(i.toString.parseBigDecimal, i.asRight[NumberFormatException]) 186 | } 187 | 188 | forAll { (i: BigDecimal) => 189 | assertEquals(i.toString.parseBigDecimal.toOption, i.toString.parseBigDecimalOption) 190 | } 191 | 192 | forAll { (i: BigDecimal) => 193 | assertEquals(i.toString.parseBigDecimal.toValidated, i.toString.parseBigDecimalValidated) 194 | } 195 | } 196 | 197 | test("StringOps.parseBoolean") { 198 | assertEquals("true".parseBoolean, true.asRight[IllegalArgumentException]) 199 | assertEquals("true".parseBoolean.toOption, "true".parseBooleanOption) 200 | assertEquals("true".parseBoolean.toValidated, "true".parseBooleanValidated) 201 | assertEquals("false".parseBoolean, false.asRight[IllegalArgumentException]) 202 | assertEquals("false".parseBoolean.toOption, "false".parseBooleanOption) 203 | assertEquals("false".parseBoolean.toValidated, "false".parseBooleanValidated) 204 | assertEquals("TRUE".parseBoolean, "true".parseBoolean) 205 | assertEquals("FALSE".parseBoolean, "false".parseBoolean) 206 | } 207 | 208 | property("scalacheck-suite: StringOps.parseBoolean") { 209 | val stringGen: Gen[String] = 210 | Arbitrary.arbString.arbitrary.filter(s => !s.equalsIgnoreCase("true") && !s.equalsIgnoreCase("false")) 211 | 212 | forAll(stringGen) { (s: String) => 213 | assertEquals(s.parseBoolean.leftMap(_.getMessage.replace("string ", "string: ")), 214 | ("For input string: \"" + s + "\"").asLeft 215 | ) 216 | } 217 | } 218 | 219 | property("asThrowable, asError and asException") { 220 | forAll { (s: String) => 221 | assertEquals(s.asThrowable.toString, new Throwable(s).toString) 222 | assertEquals(s.asError.toString, new Error(s).toString) 223 | assertEquals(s.asException.toString, new java.lang.Exception(s).toString) 224 | } 225 | } 226 | } 227 | 228 | final class EitherIdOps[A](val obj: A) extends AnyVal { 229 | 230 | /** 231 | * Wrap a value in `Left`. 232 | */ 233 | def asLeft[B]: Either[A, B] = Left(obj) 234 | 235 | /** 236 | * Wrap a value in `Right`. 237 | */ 238 | def asRight[B]: Either[B, A] = Right(obj) 239 | } 240 | -------------------------------------------------------------------------------- /shared/src/test/scala/mouse/FEitherSyntaxTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | import cats.data.EitherT 25 | import cats.syntax.either._ 26 | 27 | import scala.util.{Failure, Success, Try} 28 | 29 | class FEitherSyntaxTest extends MouseSuite { 30 | private val rightValue = List(42.asRight[String]) 31 | private val leftValue = List("42".asLeft[Int]) 32 | 33 | test("FEitherSyntax.cata") { 34 | assertEquals(rightValue.cata(_ => 0, _ => 1), List(1)) 35 | assertEquals(leftValue.cata(_ => 0, _ => 1), List(0)) 36 | } 37 | 38 | test("FEitherSyntax.cataF") { 39 | assertEquals(rightValue.cataF(_ => List(0), _ => List(1)), List(1)) 40 | assertEquals(leftValue.cataF(_ => List(0), _ => List(1)), List(0)) 41 | } 42 | 43 | test("FEitherSyntax.flatMapIn") { 44 | assertEquals(rightValue.flatMapIn(i => (i * 2).asRight[String]), List(84.asRight[String])) 45 | assertEquals(leftValue.flatMapIn(i => (i * 2).asRight[String]), leftValue) 46 | } 47 | 48 | test("FEitherSyntax.flatMapOrKeepIn") { 49 | assertEquals(rightValue.flatMapOrKeepIn { case 42 => 84.asRight[String] }, List(84.asRight[String])) 50 | assertEquals(rightValue.flatMapOrKeepIn { case 84 => 42.asRight[String] }, rightValue) 51 | assertEquals(leftValue.flatMapOrKeepIn { case 42 => 84.asRight[String] }, leftValue) 52 | } 53 | 54 | test("FEitherSyntax.leftWidenIn") { 55 | val initial: Option[Either[List[Int], String]] = Option(List(1).asLeft[String]) 56 | val expected: Option[Either[Seq[Int], String]] = Option(Seq(1).asLeft[String]) 57 | 58 | assertEquals(initial.leftWidenIn[Seq[Int]], expected) 59 | } 60 | 61 | test("FEitherSyntax.flatMapF") { 62 | assertEquals(rightValue.flatMapF(i => List((i * 2).asRight[String])), List(84.asRight[String])) 63 | assertEquals(leftValue.flatMapF(i => List((i * 2).asRight[String])), leftValue) 64 | } 65 | 66 | test("FEitherSyntax.foldIn") { 67 | assertEquals(rightValue.foldIn(_ => 0)(_ => 1), List(1)) 68 | assertEquals(leftValue.foldIn(_ => 0)(_ => 1), List(0)) 69 | } 70 | 71 | test("FEitherSyntax.foldF") { 72 | assertEquals(rightValue.foldF(_ => List(0))(_ => List(1)), List(1)) 73 | assertEquals(leftValue.foldF(_ => List(0))(_ => List(1)), List(0)) 74 | } 75 | 76 | test("FEitherSyntax.getOrElseIn") { 77 | assertEquals(rightValue.getOrElseIn(0), List(42)) 78 | assertEquals(leftValue.getOrElseIn(0), List(0)) 79 | } 80 | 81 | test("FEitherSyntax.getOrRaise") { 82 | val ex1 = new RuntimeException("BOOM 1!") 83 | val ex2 = new RuntimeException("BOOM 2!") 84 | 85 | assertEquals(Try(1.asRight).getOrRaise(ex1), Success(1)) 86 | assertEquals(Try("ERROR".asLeft).getOrRaise(ex1), Failure(ex1)) 87 | assertEquals(Try[Either[String, Int]](throw ex1).getOrRaise(ex2), Failure(ex1)) 88 | } 89 | 90 | test("FEitherSyntax.getOrRaiseMsg") { 91 | val ex1 = new RuntimeException("BOOM 1!") 92 | assertEquals(Try(1.asRight).getOrRaiseMsg("BOOM!"), Success(1)) 93 | assertEquals(Try("ERROR".asLeft).getOrRaiseMsg("BOOM!").failed.map(_.getMessage), Success("BOOM!")) 94 | assertEquals(Try[Either[String, Int]](throw ex1).getOrRaiseMsg("BOOM!"), Failure(ex1)) 95 | } 96 | 97 | test("FEitherSyntax.getOrElseF") { 98 | assertEquals(rightValue.getOrElseF(List(0)), List(42)) 99 | assertEquals(leftValue.getOrElseF(List(0)), List(0)) 100 | } 101 | 102 | test("FEitherSyntax.leftFlatMapIn") { 103 | assertEquals(rightValue.leftFlatMapIn(_ => "".asLeft[Int]), rightValue) 104 | assertEquals(leftValue.leftFlatMapIn(_ => "".asLeft[Int]), List("".asLeft[Int])) 105 | } 106 | 107 | test("FEitherSyntax.leftFlatMapOrKeepIn") { 108 | assertEquals(rightValue.leftFlatMapOrKeepIn { case "42" => Left("") }, rightValue) 109 | assertEquals(leftValue.leftFlatMapOrKeepIn { case "4242" => Right(42) }, leftValue) 110 | assertEquals(leftValue.leftFlatMapOrKeepIn { case "42" => Left("") }, List(Left(""))) 111 | assertEquals(leftValue.leftFlatMapOrKeepIn { case "42" => Right(42) }, rightValue) 112 | } 113 | 114 | test("FEitherSyntax.leftFlatMapF") { 115 | assertEquals(rightValue.leftFlatMapF(_ => List("".asLeft[Int])), rightValue) 116 | assertEquals(leftValue.leftFlatMapF(_ => List("".asLeft[Int])), List("".asLeft[Int])) 117 | } 118 | 119 | test("FEitherSyntax.leftMapIn") { 120 | assertEquals(rightValue.leftMapIn(_ => ""), rightValue) 121 | assertEquals(leftValue.leftMapIn(_ => ""), List("".asLeft[Int])) 122 | } 123 | 124 | test("FEitherSyntax.leftMapOrKeepIn") { 125 | assertEquals(rightValue.leftMapOrKeepIn { case "42" => "" }, rightValue) 126 | assertEquals(leftValue.leftMapOrKeepIn { case "4242" => "" }, leftValue) 127 | assertEquals(leftValue.leftMapOrKeepIn { case "42" => "" }, List(Left(""))) 128 | } 129 | 130 | test("FEitherSyntax.leftAsIn") { 131 | assertEquals(rightValue.leftAsIn(""), rightValue) 132 | assertEquals(leftValue.leftAsIn(""), List("".asLeft[Int])) 133 | } 134 | 135 | test("FEitherSyntax.leftVoidIn") { 136 | assertEquals(rightValue.leftVoidIn, rightValue.leftMapIn(_ => ())) 137 | assertEquals(leftValue.leftVoidIn, List(().asLeft[Int])) 138 | } 139 | 140 | test("FEitherSyntax.leftTraverseIn") { 141 | assertEquals(rightValue.leftTraverseIn(_ => Option("")), List(Option(42.asRight[String]))) 142 | assertEquals(leftValue.leftTraverseIn(_ => Option("")), List(Option("".asLeft[Int]))) 143 | } 144 | 145 | test("FEitherSyntax.leftTraverseF") { 146 | assertEquals(rightValue.leftTraverseF(_ => Option("")), Option(List(42.asRight[String]))) 147 | assertEquals(leftValue.leftTraverseF(_ => Option("")), Option(List("".asLeft[Int]))) 148 | } 149 | 150 | test("FEitherSyntax.mapIn") { 151 | assertEquals(rightValue.mapIn(_ * 2), List(84.asRight[String])) 152 | assertEquals(leftValue.mapIn(_ * 2), leftValue) 153 | } 154 | 155 | test("FEitherSyntax.mapOrKeepIn") { 156 | assertEquals(rightValue.mapOrKeepIn { case 42 => 84 }, List(84.asRight[String])) 157 | assertEquals(rightValue.mapOrKeepIn { case 84 => 42 }, rightValue) 158 | assertEquals(leftValue.mapOrKeepIn { case 42 => 84 }, leftValue) 159 | } 160 | 161 | test("FEitherSyntax.asIn") { 162 | assertEquals(rightValue.asIn(2), List(2.asRight[String])) 163 | assertEquals(leftValue.asIn(2), leftValue) 164 | } 165 | 166 | test("FEitherSyntax.voidIn") { 167 | assertEquals(rightValue.voidIn, List(().asRight[String])) 168 | assertEquals(leftValue.voidIn, leftValue.mapIn(_ => ())) 169 | } 170 | 171 | test("FEitherSyntax.orElseIn") { 172 | assertEquals(rightValue.orElseIn(0.asRight[String]), rightValue) 173 | assertEquals(leftValue.orElseIn(0.asRight[String]), List(0.asRight[String])) 174 | } 175 | 176 | test("FEitherSyntax.orElseF") { 177 | assertEquals(rightValue.orElseF(List(0.asRight[String])), rightValue) 178 | assertEquals(leftValue.orElseF(List(0.asRight[String])), List(0.asRight[String])) 179 | } 180 | 181 | test("FEitherSyntax.toOptionIn") { 182 | assertEquals(rightValue.toOptionIn, List(Option(42))) 183 | assertEquals(leftValue.toOptionIn, List(Option.empty[Int])) 184 | } 185 | 186 | test("FEitherSyntax.traverseIn") { 187 | assertEquals(rightValue.traverseIn(_ => Option(1)), List(Option(1.asRight[String]))) 188 | assertEquals(leftValue.traverseIn(_ => Option(0)), List(Option("42".asLeft[Int]))) 189 | } 190 | 191 | test("FEitherSyntax.traverseF") { 192 | assertEquals(rightValue.traverseF(_ => Option(1)), Option(List(1.asRight[String]))) 193 | assertEquals(leftValue.traverseF(_ => Option(0)), Option(List("42".asLeft[Int]))) 194 | } 195 | 196 | test("FEitherSyntax.liftEitherT") { 197 | assertEquals(rightValue.liftEitherT, EitherT(rightValue)) 198 | assertEquals(leftValue.liftEitherT, EitherT(leftValue)) 199 | } 200 | 201 | test("FEitherSyntax.asLeftF") { 202 | assertEquals(FEitherSyntax.asLeftF[List, String, Int](""), List(Left(""))) 203 | } 204 | 205 | test("FEitherSyntax.asRightF") { 206 | assertEquals(FEitherSyntax.asRightF[List, String, Int](42), List(Right(42))) 207 | } 208 | 209 | test("FEitherSyntax.bimapIn") { 210 | assertEquals(rightValue.bimapIn(_.toInt, _.toString), List("42".asRight[Int])) 211 | assertEquals(leftValue.bimapIn(_.toInt, _.toString), List(42.asLeft[String])) 212 | } 213 | 214 | test("FEitherSyntax.swapIn") { 215 | assertEquals(rightValue.swapIn, List(42.asLeft[String])) 216 | assertEquals(leftValue.swapIn, List("42".asRight[Int])) 217 | } 218 | 219 | test("FEitherSyntax.mergeIn") { 220 | assertEquals(List(42.asRight[Int]).mergeIn, List(42)) 221 | assertEquals(List("42".asLeft[String]).mergeIn, List("42")) 222 | } 223 | 224 | test("FEitherSyntax.widenIn") { 225 | val initial: Option[Either[String, List[Int]]] = Option(List(1).asRight[String]) 226 | val expected: Option[Either[String, Seq[Int]]] = Option(Seq(1).asRight[String]) 227 | 228 | assertEquals(initial.widenIn[Seq[Int]], expected) 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /shared/src/test/scala/mouse/FOptionSyntaxTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | import cats.data.OptionT 25 | import cats.data.EitherT 26 | import cats.syntax.either._ 27 | 28 | import scala.util.{Failure, Success, Try} 29 | 30 | class FOptionSyntaxTest extends MouseSuite { 31 | test("FOptionSyntax.cata") { 32 | assertEquals(List(Option(1)).cata(0, _ => 2), List(2)) 33 | assertEquals(List(Option.empty[Int]).cata(0, _ => 2), List(0)) 34 | } 35 | 36 | test("FOptionSyntax.cataF") { 37 | assertEquals(List(Option(1)).cataF(List.empty[Int], _ => List(2)), List(2)) 38 | assertEquals(List(Option.empty[Int]).cataF(List.empty[Int], _ => List(2)), List.empty[Int]) 39 | } 40 | 41 | test("FOptionSyntax.existsIn") { 42 | assertEquals(Option(Option(1)).existsIn(_ > 2), Option(false)) 43 | assertEquals(Option(Option(1)).existsIn(_ == 1), Option(true)) 44 | assertEquals(Option(Option.empty[Int]).existsIn(_ > 2), Option(false)) 45 | } 46 | 47 | test("FOptionSyntax.existsF") { 48 | assertEquals(Option(Option(1)).existsF(a => Option(a > 2)), Option(false)) 49 | assertEquals(Option(Option(1)).existsF(a => Option(a == 1)), Option(true)) 50 | assertEquals(Option(Option.empty[Int]).existsF(a => Option(a > 2)), Option(false)) 51 | } 52 | 53 | test("FOptionSyntax.filterIn") { 54 | assertEquals(Option(Option(1)).filterIn(_ > 2), Option(Option.empty[Int])) 55 | assertEquals(Option(Option(1)).filterIn(_ == 1), Option(Option(1))) 56 | assertEquals(Option(Option.empty[Int]).filterIn(_ > 2), Option(Option.empty)) 57 | } 58 | 59 | test("FOptionSyntax.filterF") { 60 | assertEquals(Option(Option(1)).filterF(a => Option(a > 2)), Option(Option.empty[Int])) 61 | assertEquals(Option(Option(1)).filterF(a => Option(a == 1)), Option(Option(1))) 62 | assertEquals(Option(Option.empty[Int]).filterF(a => Option(a > 2)), Option(Option.empty[Int])) 63 | } 64 | 65 | test("FOptionSyntax.flatMapIn") { 66 | assertEquals(List(Option(1)).flatMapIn(a => Option(a * 2)), List(Option(2))) 67 | assertEquals(List(Option.empty[Int]).flatMapIn(a => Option(a * 2)), List(Option.empty[Int])) 68 | } 69 | 70 | test("FOptionSyntax.flatMapOrKeepIn") { 71 | assertEquals(List(Option(1)).flatMapOrKeepIn { case 1 => Option(2) }, List(Option(2))) 72 | assertEquals(List(Option(1)).flatMapOrKeepIn { case 2 => Option(1) }, List(Option(1))) 73 | assertEquals(List(Option.empty[Int]).flatMapOrKeepIn { case 1 => Option(2) }, List(Option.empty[Int])) 74 | } 75 | 76 | test("FOptionSyntax.flatMapF") { 77 | assertEquals(List(Option(1)).flatMapF(a => List(Option(a * 2))), List(Option(2))) 78 | assertEquals(List(Option.empty[Int]).flatMapF(a => List(Option(a * 2))), List(Option.empty[Int])) 79 | } 80 | 81 | test("FOptionSyntax.foldIn") { 82 | assertEquals(List(Option(1)).foldIn(false)(_ => true), List(true)) 83 | assertEquals(List(Option.empty[Int]).foldIn(false)(_ => true), List(false)) 84 | } 85 | 86 | test("FOptionSyntax.foldF") { 87 | assertEquals(List(Option(1)).foldF(List(false))(_ => List(true)), List(true)) 88 | assertEquals(List(Option.empty[Int]).foldF(List(false))(_ => List(true)), List(false)) 89 | } 90 | 91 | test("FOptionSyntax.forallIn") { 92 | assertEquals(Option(Option(1)).forallIn(_ > 2), Option(false)) 93 | assertEquals(Option(Option(1)).forallIn(_ == 1), Option(true)) 94 | assertEquals(Option(Option.empty[Int]).forallIn(_ > 2), Option(true)) 95 | } 96 | 97 | test("FOptionSyntax.forallF") { 98 | assertEquals(Option(Option(1)).forallF(a => Option(a > 2)), Option(false)) 99 | assertEquals(Option(Option(1)).forallF(a => Option(a == 1)), Option(true)) 100 | assertEquals(Option(Option.empty[Int]).forallF(a => Option(a > 2)), Option(true)) 101 | } 102 | 103 | test("FOptionSyntax.getOrElse") { 104 | assertEquals(List(Option(1)).getOrElse(0), List(1)) 105 | assertEquals(List(Option.empty[Int]).getOrElse(0), List(0)) 106 | } 107 | 108 | test("FOptionSyntax.getOrRaise") { 109 | val ex1 = new RuntimeException("BOOM 1!") 110 | val ex2 = new RuntimeException("BOOM 2!") 111 | 112 | assertEquals(Try(Option(1)).getOrRaise(ex1), Success(1)) 113 | assertEquals(Try(Option.empty[Int]).getOrRaise(ex1), Failure(ex1)) 114 | assertEquals(Try[Option[Int]](throw ex1).getOrRaise(ex2), Failure(ex1)) 115 | } 116 | 117 | test("FOptionSyntax.getOrRaiseMsg") { 118 | val ex1 = new RuntimeException("BOOM 1!") 119 | assertEquals(Try(Option(1)).getOrRaiseMsg("BOOM!"), Success(1)) 120 | assertEquals(Try(Option.empty[Int]).getOrRaiseMsg("BOOM!").failed.map(_.getMessage), Success("BOOM!")) 121 | assertEquals(Try[Option[Int]](throw ex1).getOrRaiseMsg("BOOM!"), Failure(ex1)) 122 | } 123 | 124 | test("FOptionSyntax.getOrElseF") { 125 | assertEquals(List(Option(1)).getOrElseF(List(0)), List(1)) 126 | assertEquals(List(Option.empty[Int]).getOrElseF(List(0)), List(0)) 127 | } 128 | 129 | test("FOptionSyntax.mapIn") { 130 | assertEquals(List(Option(1)).mapIn(_ + 1), List(Option(2))) 131 | assertEquals(List(Option.empty[Int]).mapIn(_ + 1), List(Option.empty[Int])) 132 | } 133 | 134 | test("FOptionSyntax.mapOrKeepIn") { 135 | assertEquals(List(Option(1)).mapOrKeepIn { case 1 => 2 }, List(Option(2))) 136 | assertEquals(List(Option(1)).mapOrKeepIn { case 2 => 1 }, List(Option(1))) 137 | assertEquals(List(Option.empty[Int]).mapOrKeepIn { case 1 => 2 }, List(Option.empty[Int])) 138 | } 139 | 140 | test("FOptionSyntax.asIn") { 141 | assertEquals(List(Option(1)).asIn(1), List(Option(1))) 142 | assertEquals(List(Option.empty[Int]).asIn(1), List(Option.empty[Int])) 143 | } 144 | 145 | test("FOptionSyntax.voidIn") { 146 | assertEquals(List(Option(1)).voidIn, List(Option(()))) 147 | assertEquals(List(Option.empty[Int]).voidIn, List(Option.empty[Unit])) 148 | } 149 | 150 | test("FOptionSyntax.orElseIn") { 151 | assertEquals(List(Option(1)).orElseIn(Option(0)), List(Option(1))) 152 | assertEquals(List(Option.empty[Int]).orElseIn(Option(0)), List(Option(0))) 153 | } 154 | 155 | test("FOptionSyntax.orElseF") { 156 | assertEquals(List(Option(1)).orElseF(List(Option(0))), List(Option(1))) 157 | assertEquals(List(Option.empty[Int]).orElseF(List(Option(0))), List(Option(0))) 158 | } 159 | 160 | test("FOptionSyntax.toLeftIn") { 161 | assertEquals(List(Option(1)).toLeftIn("right"), List(1.asLeft[String])) 162 | assertEquals(List.empty[Option[Int]].toLeftIn("right"), List.empty[Either[Int, String]]) 163 | assertEquals(List(Option.empty[Int]).toLeftIn("right"), List("right".asRight[Int])) 164 | } 165 | 166 | test("FOptionSyntax.toLeftInF") { 167 | assertEquals(List(Option(1)).toLeftInF(List("right")), List(1.asLeft[String])) 168 | assertEquals(List.empty[Option[Int]].toLeftInF(List("right")), List.empty[Either[Int, String]]) 169 | assertEquals(List(Option.empty[Int]).toLeftInF(List("right")), List("right".asRight[Int])) 170 | } 171 | 172 | test("FOptionSyntax.toRightIn") { 173 | assertEquals(List(Option(1)).toRightIn("none"), List(1.asRight[String])) 174 | assertEquals(List.empty[Option[Int]].toRightIn("none"), List.empty[Either[String, Int]]) 175 | assertEquals(List(Option.empty[Int]).toRightIn("none"), List("none".asLeft[Int])) 176 | } 177 | 178 | test("FOptionSyntax.toRightInF") { 179 | assertEquals(List(Option(1)).toRightInF(List("none")), List(1.asRight[String])) 180 | assertEquals(List.empty[Option[Int]].toRightInF(List("none")), List.empty[Either[String, Int]]) 181 | assertEquals(List(Option.empty[Int]).toRightInF(List("none")), List("none".asLeft[Int])) 182 | } 183 | 184 | test("FOptionSyntax.traverseIn") { 185 | assertEquals(List(Option(1)).traverseIn(a => List(a * 2)), List(List(Option(2)))) 186 | assertEquals(List.empty[Option[Int]].traverseIn(a => List(a * 2)), List.empty[List[Option[Int]]]) 187 | assertEquals(List(Option.empty[Int]).traverseIn(a => List(a * 2)), List(List(Option.empty[Int]))) 188 | } 189 | 190 | test("FOptionSyntax.traverseF") { 191 | assertEquals(List(Option(1)).traverseF(a => Option(a * 2)), Option(List(Option(2)))) 192 | assertEquals(List.empty[Option[Int]].traverseF(a => Option(a * 2)), Option(List.empty[Option[Int]])) 193 | assertEquals(List(Option.empty[Int]).traverseF(a => Option(a * 2)), Option(List(Option.empty[Int]))) 194 | } 195 | 196 | test("FOptionSyntax.liftOptionT") { 197 | assertEquals(List(Option(1)).liftOptionT, OptionT(List(Option(1)))) 198 | assertEquals(List(Option.empty[Int]).liftOptionT, OptionT(List(Option.empty[Int]))) 199 | } 200 | 201 | test("FOptionSyntax.liftEitherT") { 202 | assertEquals(List(Option(1)).liftEitherT("none"), EitherT(List(1.asRight[String]))) 203 | assertEquals(List(Option.empty[Int]).liftEitherT("none"), EitherT(List("none".asLeft[Int]))) 204 | } 205 | 206 | test("FOptionSyntax.noneF") { 207 | assertEquals(FOptionSyntax.noneF[List, Int], List(None)) 208 | } 209 | 210 | test("FOptionSyntax.someF") { 211 | assertEquals(FOptionSyntax.someF[List, Int](42), List(Some(42))) 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by sbt-github-actions using the 2 | # githubWorkflowGenerate task. You should add and commit this file to 3 | # your git repository. It goes without saying that you shouldn't edit 4 | # this file by hand! Instead, if you wish to make changes, you should 5 | # change your sbt build configuration to revise the workflow description 6 | # to meet your needs, then regenerate this file. 7 | 8 | name: Continuous Integration 9 | 10 | on: 11 | pull_request: 12 | branches: ['**', '!update/**', '!pr/**'] 13 | push: 14 | branches: ['**', '!update/**', '!pr/**'] 15 | tags: [v*] 16 | 17 | env: 18 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 19 | 20 | 21 | concurrency: 22 | group: ${{ github.workflow }} @ ${{ github.ref }} 23 | cancel-in-progress: true 24 | 25 | jobs: 26 | build: 27 | name: Test 28 | strategy: 29 | matrix: 30 | os: [ubuntu-22.04] 31 | scala: [2.12, 3, 2.13] 32 | java: [temurin@8, temurin@17] 33 | project: [mouseJVM, mouseNative, mouseJS] 34 | exclude: 35 | - scala: 2.12 36 | java: temurin@17 37 | - scala: 3 38 | java: temurin@17 39 | - project: mouseNative 40 | java: temurin@17 41 | - project: mouseJS 42 | java: temurin@17 43 | runs-on: ${{ matrix.os }} 44 | timeout-minutes: 60 45 | steps: 46 | - name: Checkout current branch (full) 47 | uses: actions/checkout@v5 48 | with: 49 | fetch-depth: 0 50 | 51 | - name: Setup sbt 52 | uses: sbt/setup-sbt@v1 53 | 54 | - name: Setup Java (temurin@8) 55 | id: setup-java-temurin-8 56 | if: matrix.java == 'temurin@8' 57 | uses: actions/setup-java@v5 58 | with: 59 | distribution: temurin 60 | java-version: 8 61 | cache: sbt 62 | 63 | - name: sbt update 64 | if: matrix.java == 'temurin@8' && steps.setup-java-temurin-8.outputs.cache-hit == 'false' 65 | run: sbt +update 66 | 67 | - name: Setup Java (temurin@17) 68 | id: setup-java-temurin-17 69 | if: matrix.java == 'temurin@17' 70 | uses: actions/setup-java@v5 71 | with: 72 | distribution: temurin 73 | java-version: 17 74 | cache: sbt 75 | 76 | - name: sbt update 77 | if: matrix.java == 'temurin@17' && steps.setup-java-temurin-17.outputs.cache-hit == 'false' 78 | run: sbt +update 79 | 80 | - name: Check that workflows are up to date 81 | run: sbt githubWorkflowCheck 82 | 83 | - name: Check headers and formatting 84 | if: matrix.java == 'temurin@8' && matrix.os == 'ubuntu-22.04' 85 | run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' headerCheckAll scalafmtCheckAll 'project /' scalafmtSbtCheck 86 | 87 | - name: nativeLink 88 | if: matrix.project == 'mouseNative' 89 | run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' Test/nativeLink 90 | 91 | - name: scalaJSLink 92 | if: matrix.project == 'mouseJS' 93 | run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' Test/scalaJSLinkerResult 94 | 95 | - name: Test 96 | run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' test 97 | 98 | - name: Check binary compatibility 99 | if: matrix.java == 'temurin@8' && matrix.os == 'ubuntu-22.04' 100 | run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' mimaReportBinaryIssues 101 | 102 | - name: Generate API documentation 103 | if: matrix.java == 'temurin@8' && matrix.os == 'ubuntu-22.04' 104 | run: sbt 'project ${{ matrix.project }}' '++ ${{ matrix.scala }}' doc 105 | 106 | - name: Make target directories 107 | if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') 108 | run: mkdir -p js/target jvm/target native/target project/target 109 | 110 | - name: Compress target directories 111 | if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') 112 | run: tar cf targets.tar js/target jvm/target native/target project/target 113 | 114 | - name: Upload target directories 115 | if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') 116 | uses: actions/upload-artifact@v5 117 | with: 118 | name: target-${{ matrix.os }}-${{ matrix.java }}-${{ matrix.scala }}-${{ matrix.project }} 119 | path: targets.tar 120 | 121 | publish: 122 | name: Publish Artifacts 123 | needs: [build] 124 | if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') 125 | strategy: 126 | matrix: 127 | os: [ubuntu-22.04] 128 | java: [temurin@8] 129 | runs-on: ${{ matrix.os }} 130 | steps: 131 | - name: Checkout current branch (full) 132 | uses: actions/checkout@v5 133 | with: 134 | fetch-depth: 0 135 | 136 | - name: Setup sbt 137 | uses: sbt/setup-sbt@v1 138 | 139 | - name: Setup Java (temurin@8) 140 | id: setup-java-temurin-8 141 | if: matrix.java == 'temurin@8' 142 | uses: actions/setup-java@v5 143 | with: 144 | distribution: temurin 145 | java-version: 8 146 | cache: sbt 147 | 148 | - name: sbt update 149 | if: matrix.java == 'temurin@8' && steps.setup-java-temurin-8.outputs.cache-hit == 'false' 150 | run: sbt +update 151 | 152 | - name: Setup Java (temurin@17) 153 | id: setup-java-temurin-17 154 | if: matrix.java == 'temurin@17' 155 | uses: actions/setup-java@v5 156 | with: 157 | distribution: temurin 158 | java-version: 17 159 | cache: sbt 160 | 161 | - name: sbt update 162 | if: matrix.java == 'temurin@17' && steps.setup-java-temurin-17.outputs.cache-hit == 'false' 163 | run: sbt +update 164 | 165 | - name: Download target directories (2.12, mouseJVM) 166 | uses: actions/download-artifact@v6 167 | with: 168 | name: target-${{ matrix.os }}-${{ matrix.java }}-2.12-mouseJVM 169 | 170 | - name: Inflate target directories (2.12, mouseJVM) 171 | run: | 172 | tar xf targets.tar 173 | rm targets.tar 174 | 175 | - name: Download target directories (2.12, mouseNative) 176 | uses: actions/download-artifact@v6 177 | with: 178 | name: target-${{ matrix.os }}-${{ matrix.java }}-2.12-mouseNative 179 | 180 | - name: Inflate target directories (2.12, mouseNative) 181 | run: | 182 | tar xf targets.tar 183 | rm targets.tar 184 | 185 | - name: Download target directories (2.12, mouseJS) 186 | uses: actions/download-artifact@v6 187 | with: 188 | name: target-${{ matrix.os }}-${{ matrix.java }}-2.12-mouseJS 189 | 190 | - name: Inflate target directories (2.12, mouseJS) 191 | run: | 192 | tar xf targets.tar 193 | rm targets.tar 194 | 195 | - name: Download target directories (3, mouseJVM) 196 | uses: actions/download-artifact@v6 197 | with: 198 | name: target-${{ matrix.os }}-${{ matrix.java }}-3-mouseJVM 199 | 200 | - name: Inflate target directories (3, mouseJVM) 201 | run: | 202 | tar xf targets.tar 203 | rm targets.tar 204 | 205 | - name: Download target directories (3, mouseNative) 206 | uses: actions/download-artifact@v6 207 | with: 208 | name: target-${{ matrix.os }}-${{ matrix.java }}-3-mouseNative 209 | 210 | - name: Inflate target directories (3, mouseNative) 211 | run: | 212 | tar xf targets.tar 213 | rm targets.tar 214 | 215 | - name: Download target directories (3, mouseJS) 216 | uses: actions/download-artifact@v6 217 | with: 218 | name: target-${{ matrix.os }}-${{ matrix.java }}-3-mouseJS 219 | 220 | - name: Inflate target directories (3, mouseJS) 221 | run: | 222 | tar xf targets.tar 223 | rm targets.tar 224 | 225 | - name: Download target directories (2.13, mouseJVM) 226 | uses: actions/download-artifact@v6 227 | with: 228 | name: target-${{ matrix.os }}-${{ matrix.java }}-2.13-mouseJVM 229 | 230 | - name: Inflate target directories (2.13, mouseJVM) 231 | run: | 232 | tar xf targets.tar 233 | rm targets.tar 234 | 235 | - name: Download target directories (2.13, mouseNative) 236 | uses: actions/download-artifact@v6 237 | with: 238 | name: target-${{ matrix.os }}-${{ matrix.java }}-2.13-mouseNative 239 | 240 | - name: Inflate target directories (2.13, mouseNative) 241 | run: | 242 | tar xf targets.tar 243 | rm targets.tar 244 | 245 | - name: Download target directories (2.13, mouseJS) 246 | uses: actions/download-artifact@v6 247 | with: 248 | name: target-${{ matrix.os }}-${{ matrix.java }}-2.13-mouseJS 249 | 250 | - name: Inflate target directories (2.13, mouseJS) 251 | run: | 252 | tar xf targets.tar 253 | rm targets.tar 254 | 255 | - name: Import signing key 256 | if: env.PGP_SECRET != '' && env.PGP_PASSPHRASE == '' 257 | env: 258 | PGP_SECRET: ${{ secrets.PGP_SECRET }} 259 | PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }} 260 | run: echo $PGP_SECRET | base64 -d -i - | gpg --import 261 | 262 | - name: Import signing key and strip passphrase 263 | if: env.PGP_SECRET != '' && env.PGP_PASSPHRASE != '' 264 | env: 265 | PGP_SECRET: ${{ secrets.PGP_SECRET }} 266 | PGP_PASSPHRASE: ${{ secrets.PGP_PASSPHRASE }} 267 | run: | 268 | echo "$PGP_SECRET" | base64 -d -i - > /tmp/signing-key.gpg 269 | echo "$PGP_PASSPHRASE" | gpg --pinentry-mode loopback --passphrase-fd 0 --import /tmp/signing-key.gpg 270 | (echo "$PGP_PASSPHRASE"; echo; echo) | gpg --command-fd 0 --pinentry-mode loopback --change-passphrase $(gpg --list-secret-keys --with-colons 2> /dev/null | grep '^sec:' | cut --delimiter ':' --fields 5 | tail -n 1) 271 | 272 | - name: Publish 273 | env: 274 | SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} 275 | SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} 276 | SONATYPE_CREDENTIAL_HOST: ${{ secrets.SONATYPE_CREDENTIAL_HOST }} 277 | run: sbt tlCiRelease 278 | 279 | dependency-submission: 280 | name: Submit Dependencies 281 | if: github.event.repository.fork == false && github.event_name != 'pull_request' 282 | strategy: 283 | matrix: 284 | os: [ubuntu-22.04] 285 | java: [temurin@8] 286 | runs-on: ${{ matrix.os }} 287 | steps: 288 | - name: Checkout current branch (full) 289 | uses: actions/checkout@v5 290 | with: 291 | fetch-depth: 0 292 | 293 | - name: Setup sbt 294 | uses: sbt/setup-sbt@v1 295 | 296 | - name: Setup Java (temurin@8) 297 | id: setup-java-temurin-8 298 | if: matrix.java == 'temurin@8' 299 | uses: actions/setup-java@v5 300 | with: 301 | distribution: temurin 302 | java-version: 8 303 | cache: sbt 304 | 305 | - name: sbt update 306 | if: matrix.java == 'temurin@8' && steps.setup-java-temurin-8.outputs.cache-hit == 'false' 307 | run: sbt +update 308 | 309 | - name: Setup Java (temurin@17) 310 | id: setup-java-temurin-17 311 | if: matrix.java == 'temurin@17' 312 | uses: actions/setup-java@v5 313 | with: 314 | distribution: temurin 315 | java-version: 17 316 | cache: sbt 317 | 318 | - name: sbt update 319 | if: matrix.java == 'temurin@17' && steps.setup-java-temurin-17.outputs.cache-hit == 'false' 320 | run: sbt +update 321 | 322 | - name: Submit Dependencies 323 | uses: scalacenter/sbt-dependency-submission@v2 324 | with: 325 | modules-ignore: docs_2.12 docs_3 docs_2.13 mousejvm_2.12 mousejvm_3 mousejvm_2.13 mousenative_2.12 mousenative_3 mousenative_2.13 mousejs_2.12 mousejs_3 mousejs_2.13 326 | configs-ignore: test scala-tool scala-doc-tool test-internal 327 | 328 | site: 329 | name: Generate Site 330 | strategy: 331 | matrix: 332 | os: [ubuntu-22.04] 333 | java: [temurin@17] 334 | runs-on: ${{ matrix.os }} 335 | steps: 336 | - name: Checkout current branch (full) 337 | uses: actions/checkout@v5 338 | with: 339 | fetch-depth: 0 340 | 341 | - name: Setup sbt 342 | uses: sbt/setup-sbt@v1 343 | 344 | - name: Setup Java (temurin@8) 345 | id: setup-java-temurin-8 346 | if: matrix.java == 'temurin@8' 347 | uses: actions/setup-java@v5 348 | with: 349 | distribution: temurin 350 | java-version: 8 351 | cache: sbt 352 | 353 | - name: sbt update 354 | if: matrix.java == 'temurin@8' && steps.setup-java-temurin-8.outputs.cache-hit == 'false' 355 | run: sbt +update 356 | 357 | - name: Setup Java (temurin@17) 358 | id: setup-java-temurin-17 359 | if: matrix.java == 'temurin@17' 360 | uses: actions/setup-java@v5 361 | with: 362 | distribution: temurin 363 | java-version: 17 364 | cache: sbt 365 | 366 | - name: sbt update 367 | if: matrix.java == 'temurin@17' && steps.setup-java-temurin-17.outputs.cache-hit == 'false' 368 | run: sbt +update 369 | 370 | - name: Generate site 371 | run: sbt docs/tlSite 372 | 373 | - name: Publish site 374 | if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main' 375 | uses: peaceiris/actions-gh-pages@v4.0.0 376 | with: 377 | github_token: ${{ secrets.GITHUB_TOKEN }} 378 | publish_dir: site/target/docs/site 379 | keep_files: true 380 | -------------------------------------------------------------------------------- /shared/src/test/scala/mouse/FTupleNSyntaxSuite.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016 Typelevel 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package mouse 23 | 24 | import org.scalacheck.Prop._ 25 | 26 | class FTupleNSyntaxSuite extends MouseSuite { 27 | test("_1F, _2F, _3F works for Tuple3") { 28 | forAll { (l: List[(Int, Int, Int)]) => 29 | assertEquals(l._1F, l.map(_._1)) 30 | assertEquals(l._2F, l.map(_._2)) 31 | assertEquals(l._3F, l.map(_._3)) 32 | } 33 | } 34 | 35 | test("_1F, _2F, _3F, _4F works for Tuple4") { 36 | forAll { (l: List[(Int, Int, Int, Int)]) => 37 | assertEquals(l._1F, l.map(_._1)) 38 | assertEquals(l._2F, l.map(_._2)) 39 | assertEquals(l._3F, l.map(_._3)) 40 | assertEquals(l._4F, l.map(_._4)) 41 | } 42 | } 43 | 44 | test("_1F, _2F, _3F, _4F, _5F works for Tuple5") { 45 | forAll { (l: List[(Int, Int, Int, Int, Int)]) => 46 | assertEquals(l._1F, l.map(_._1)) 47 | assertEquals(l._2F, l.map(_._2)) 48 | assertEquals(l._3F, l.map(_._3)) 49 | assertEquals(l._4F, l.map(_._4)) 50 | assertEquals(l._5F, l.map(_._5)) 51 | } 52 | } 53 | 54 | test("_1F, _2F, _3F, _4F, _5F, _6F works for Tuple6") { 55 | forAll { (l: List[(Int, Int, Int, Int, Int, Int)]) => 56 | assertEquals(l._1F, l.map(_._1)) 57 | assertEquals(l._2F, l.map(_._2)) 58 | assertEquals(l._3F, l.map(_._3)) 59 | assertEquals(l._4F, l.map(_._4)) 60 | assertEquals(l._5F, l.map(_._5)) 61 | assertEquals(l._6F, l.map(_._6)) 62 | } 63 | } 64 | 65 | test("_1F, _2F, _3F, _4F, _5F, _6F works for Tuple6") { 66 | forAll { (l: List[(Int, Int, Int, Int, Int, Int)]) => 67 | assertEquals(l._1F, l.map(_._1)) 68 | assertEquals(l._2F, l.map(_._2)) 69 | assertEquals(l._3F, l.map(_._3)) 70 | assertEquals(l._4F, l.map(_._4)) 71 | assertEquals(l._5F, l.map(_._5)) 72 | assertEquals(l._6F, l.map(_._6)) 73 | } 74 | } 75 | 76 | test("_1F, _2F, _3F, _4F, _5F, _6F, _7F works for Tuple7") { 77 | forAll { (l: List[(Int, Int, Int, Int, Int, Int, Int)]) => 78 | assertEquals(l._1F, l.map(_._1)) 79 | assertEquals(l._2F, l.map(_._2)) 80 | assertEquals(l._3F, l.map(_._3)) 81 | assertEquals(l._4F, l.map(_._4)) 82 | assertEquals(l._5F, l.map(_._5)) 83 | assertEquals(l._6F, l.map(_._6)) 84 | assertEquals(l._7F, l.map(_._7)) 85 | } 86 | } 87 | 88 | test("_1F, _2F, _3F, _4F, _5F, _6F, _7F, 8F works for Tuple8") { 89 | forAll { (l: List[(Int, Int, Int, Int, Int, Int, Int, Int)]) => 90 | assertEquals(l._1F, l.map(_._1)) 91 | assertEquals(l._2F, l.map(_._2)) 92 | assertEquals(l._3F, l.map(_._3)) 93 | assertEquals(l._4F, l.map(_._4)) 94 | assertEquals(l._5F, l.map(_._5)) 95 | assertEquals(l._6F, l.map(_._6)) 96 | assertEquals(l._7F, l.map(_._7)) 97 | assertEquals(l._8F, l.map(_._8)) 98 | } 99 | } 100 | 101 | test("_1F, _2F, _3F, _4F, _5F, _6F, _7F, _8F, _9F works for Tuple9") { 102 | forAll { (l: List[(Int, Int, Int, Int, Int, Int, Int, Int, Int)]) => 103 | assertEquals(l._1F, l.map(_._1)) 104 | assertEquals(l._2F, l.map(_._2)) 105 | assertEquals(l._3F, l.map(_._3)) 106 | assertEquals(l._4F, l.map(_._4)) 107 | assertEquals(l._5F, l.map(_._5)) 108 | assertEquals(l._6F, l.map(_._6)) 109 | assertEquals(l._7F, l.map(_._7)) 110 | assertEquals(l._8F, l.map(_._8)) 111 | assertEquals(l._9F, l.map(_._9)) 112 | } 113 | } 114 | 115 | test("_1F, _2F, _3F, _4F, _5F, _6F, _7F, _8F, _9F, _10F works for Tuple10") { 116 | forAll { (l: List[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)]) => 117 | assertEquals(l._1F, l.map(_._1)) 118 | assertEquals(l._2F, l.map(_._2)) 119 | assertEquals(l._3F, l.map(_._3)) 120 | assertEquals(l._4F, l.map(_._4)) 121 | assertEquals(l._5F, l.map(_._5)) 122 | assertEquals(l._6F, l.map(_._6)) 123 | assertEquals(l._7F, l.map(_._7)) 124 | assertEquals(l._8F, l.map(_._8)) 125 | assertEquals(l._9F, l.map(_._9)) 126 | assertEquals(l._10F, l.map(_._10)) 127 | } 128 | } 129 | 130 | test("_1F, _2F, _3F, _4F, _5F, _6F, _7F, _8F, _9F, _10F, _11F works for Tuple11") { 131 | forAll { (l: List[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)]) => 132 | assertEquals(l._1F, l.map(_._1)) 133 | assertEquals(l._2F, l.map(_._2)) 134 | assertEquals(l._3F, l.map(_._3)) 135 | assertEquals(l._4F, l.map(_._4)) 136 | assertEquals(l._5F, l.map(_._5)) 137 | assertEquals(l._6F, l.map(_._6)) 138 | assertEquals(l._7F, l.map(_._7)) 139 | assertEquals(l._8F, l.map(_._8)) 140 | assertEquals(l._9F, l.map(_._9)) 141 | assertEquals(l._10F, l.map(_._10)) 142 | assertEquals(l._11F, l.map(_._11)) 143 | } 144 | } 145 | 146 | test("_1F, _2F, _3F, _4F, _5F, _6F, _7F, _8F, _9F, _10F, _11F, _12F works for Tuple12") { 147 | forAll { (l: List[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)]) => 148 | assertEquals(l._1F, l.map(_._1)) 149 | assertEquals(l._2F, l.map(_._2)) 150 | assertEquals(l._3F, l.map(_._3)) 151 | assertEquals(l._4F, l.map(_._4)) 152 | assertEquals(l._5F, l.map(_._5)) 153 | assertEquals(l._6F, l.map(_._6)) 154 | assertEquals(l._7F, l.map(_._7)) 155 | assertEquals(l._8F, l.map(_._8)) 156 | assertEquals(l._9F, l.map(_._9)) 157 | assertEquals(l._10F, l.map(_._10)) 158 | assertEquals(l._11F, l.map(_._11)) 159 | assertEquals(l._12F, l.map(_._12)) 160 | } 161 | } 162 | 163 | test("_1F, _2F, _3F, _4F, _5F, _6F, _7F, _8F, _9F, _10F, _11F, _12F, _13F works for Tuple13") { 164 | forAll { (l: List[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)]) => 165 | assertEquals(l._1F, l.map(_._1)) 166 | assertEquals(l._2F, l.map(_._2)) 167 | assertEquals(l._3F, l.map(_._3)) 168 | assertEquals(l._4F, l.map(_._4)) 169 | assertEquals(l._5F, l.map(_._5)) 170 | assertEquals(l._6F, l.map(_._6)) 171 | assertEquals(l._7F, l.map(_._7)) 172 | assertEquals(l._8F, l.map(_._8)) 173 | assertEquals(l._9F, l.map(_._9)) 174 | assertEquals(l._10F, l.map(_._10)) 175 | assertEquals(l._11F, l.map(_._11)) 176 | assertEquals(l._12F, l.map(_._12)) 177 | assertEquals(l._13F, l.map(_._13)) 178 | } 179 | } 180 | 181 | test("_1F, _2F, _3F, _4F, _5F, _6F, _7F, _8F, _9F, _10F, _11F, _12F, _13F, _14F works for Tuple14") { 182 | forAll { (l: List[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)]) => 183 | assertEquals(l._1F, l.map(_._1)) 184 | assertEquals(l._2F, l.map(_._2)) 185 | assertEquals(l._3F, l.map(_._3)) 186 | assertEquals(l._4F, l.map(_._4)) 187 | assertEquals(l._5F, l.map(_._5)) 188 | assertEquals(l._6F, l.map(_._6)) 189 | assertEquals(l._7F, l.map(_._7)) 190 | assertEquals(l._8F, l.map(_._8)) 191 | assertEquals(l._9F, l.map(_._9)) 192 | assertEquals(l._10F, l.map(_._10)) 193 | assertEquals(l._11F, l.map(_._11)) 194 | assertEquals(l._12F, l.map(_._12)) 195 | assertEquals(l._13F, l.map(_._13)) 196 | assertEquals(l._14F, l.map(_._14)) 197 | } 198 | } 199 | 200 | test("_1F, _2F, _3F, _4F, _5F, _6F, _7F, _8F, _9F, _10F, _11F, _12F, _13F, _14F, _15F works for Tuple15") { 201 | forAll { (l: List[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)]) => 202 | assertEquals(l._1F, l.map(_._1)) 203 | assertEquals(l._2F, l.map(_._2)) 204 | assertEquals(l._3F, l.map(_._3)) 205 | assertEquals(l._4F, l.map(_._4)) 206 | assertEquals(l._5F, l.map(_._5)) 207 | assertEquals(l._6F, l.map(_._6)) 208 | assertEquals(l._7F, l.map(_._7)) 209 | assertEquals(l._8F, l.map(_._8)) 210 | assertEquals(l._9F, l.map(_._9)) 211 | assertEquals(l._10F, l.map(_._10)) 212 | assertEquals(l._11F, l.map(_._11)) 213 | assertEquals(l._12F, l.map(_._12)) 214 | assertEquals(l._13F, l.map(_._13)) 215 | assertEquals(l._14F, l.map(_._14)) 216 | assertEquals(l._15F, l.map(_._15)) 217 | } 218 | } 219 | 220 | test("_1F, _2F, _3F, _4F, _5F, _6F, _7F, _8F, _9F, _10F, _11F, _12F, _13F, _14F, _15F, _16F works for Tuple16") { 221 | forAll { (l: List[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)]) => 222 | assertEquals(l._1F, l.map(_._1)) 223 | assertEquals(l._2F, l.map(_._2)) 224 | assertEquals(l._3F, l.map(_._3)) 225 | assertEquals(l._4F, l.map(_._4)) 226 | assertEquals(l._5F, l.map(_._5)) 227 | assertEquals(l._6F, l.map(_._6)) 228 | assertEquals(l._7F, l.map(_._7)) 229 | assertEquals(l._8F, l.map(_._8)) 230 | assertEquals(l._9F, l.map(_._9)) 231 | assertEquals(l._10F, l.map(_._10)) 232 | assertEquals(l._11F, l.map(_._11)) 233 | assertEquals(l._12F, l.map(_._12)) 234 | assertEquals(l._13F, l.map(_._13)) 235 | assertEquals(l._14F, l.map(_._14)) 236 | assertEquals(l._15F, l.map(_._15)) 237 | assertEquals(l._16F, l.map(_._16)) 238 | } 239 | } 240 | 241 | test( 242 | "_1F, _2F, _3F, _4F, _5F, _6F, _7F, _8F, _9F, _10F, _11F, _12F, _13F, _14F, _15F, _16F, _17F works for Tuple17" 243 | ) { 244 | forAll { (l: List[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)]) => 245 | assertEquals(l._1F, l.map(_._1)) 246 | assertEquals(l._2F, l.map(_._2)) 247 | assertEquals(l._3F, l.map(_._3)) 248 | assertEquals(l._4F, l.map(_._4)) 249 | assertEquals(l._5F, l.map(_._5)) 250 | assertEquals(l._6F, l.map(_._6)) 251 | assertEquals(l._7F, l.map(_._7)) 252 | assertEquals(l._8F, l.map(_._8)) 253 | assertEquals(l._9F, l.map(_._9)) 254 | assertEquals(l._10F, l.map(_._10)) 255 | assertEquals(l._11F, l.map(_._11)) 256 | assertEquals(l._12F, l.map(_._12)) 257 | assertEquals(l._13F, l.map(_._13)) 258 | assertEquals(l._14F, l.map(_._14)) 259 | assertEquals(l._15F, l.map(_._15)) 260 | assertEquals(l._16F, l.map(_._16)) 261 | assertEquals(l._17F, l.map(_._17)) 262 | } 263 | } 264 | 265 | test( 266 | "_1F, _2F, _3F, _4F, _5F, _6F, _7F, _8F, _9F, _10F, _11F, _12F, _13F, _14F, _15F, _16F, _17F, _18F works for Tuple18" 267 | ) { 268 | forAll { (l: List[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)]) => 269 | assertEquals(l._1F, l.map(_._1)) 270 | assertEquals(l._2F, l.map(_._2)) 271 | assertEquals(l._3F, l.map(_._3)) 272 | assertEquals(l._4F, l.map(_._4)) 273 | assertEquals(l._5F, l.map(_._5)) 274 | assertEquals(l._6F, l.map(_._6)) 275 | assertEquals(l._7F, l.map(_._7)) 276 | assertEquals(l._8F, l.map(_._8)) 277 | assertEquals(l._9F, l.map(_._9)) 278 | assertEquals(l._10F, l.map(_._10)) 279 | assertEquals(l._11F, l.map(_._11)) 280 | assertEquals(l._12F, l.map(_._12)) 281 | assertEquals(l._13F, l.map(_._13)) 282 | assertEquals(l._14F, l.map(_._14)) 283 | assertEquals(l._15F, l.map(_._15)) 284 | assertEquals(l._16F, l.map(_._16)) 285 | assertEquals(l._17F, l.map(_._17)) 286 | assertEquals(l._18F, l.map(_._18)) 287 | } 288 | } 289 | 290 | test( 291 | "_1F, _2F, _3F, _4F, _5F, _6F, _7F, _8F, _9F, _10F, _11F, _12F, _13F, _14F, _15F, _16F, _17F, _18F, _19F works for Tuple19" 292 | ) { 293 | forAll { 294 | (l: List[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)]) => 295 | assertEquals(l._1F, l.map(_._1)) 296 | assertEquals(l._2F, l.map(_._2)) 297 | assertEquals(l._3F, l.map(_._3)) 298 | assertEquals(l._4F, l.map(_._4)) 299 | assertEquals(l._5F, l.map(_._5)) 300 | assertEquals(l._6F, l.map(_._6)) 301 | assertEquals(l._7F, l.map(_._7)) 302 | assertEquals(l._8F, l.map(_._8)) 303 | assertEquals(l._9F, l.map(_._9)) 304 | assertEquals(l._10F, l.map(_._10)) 305 | assertEquals(l._11F, l.map(_._11)) 306 | assertEquals(l._12F, l.map(_._12)) 307 | assertEquals(l._13F, l.map(_._13)) 308 | assertEquals(l._14F, l.map(_._14)) 309 | assertEquals(l._15F, l.map(_._15)) 310 | assertEquals(l._16F, l.map(_._16)) 311 | assertEquals(l._17F, l.map(_._17)) 312 | assertEquals(l._18F, l.map(_._18)) 313 | assertEquals(l._19F, l.map(_._19)) 314 | } 315 | } 316 | 317 | test( 318 | "_1F, _2F, _3F, _4F, _5F, _6F, _7F, _8F, _9F, _10F, _11F, _12F, _13F, _14F, _15F, _16F, _17F, _18F, _19F, _20F works for Tuple20" 319 | ) { 320 | forAll { 321 | (l: List[(Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int)]) => 322 | assertEquals(l._1F, l.map(_._1)) 323 | assertEquals(l._2F, l.map(_._2)) 324 | assertEquals(l._3F, l.map(_._3)) 325 | assertEquals(l._4F, l.map(_._4)) 326 | assertEquals(l._5F, l.map(_._5)) 327 | assertEquals(l._6F, l.map(_._6)) 328 | assertEquals(l._7F, l.map(_._7)) 329 | assertEquals(l._8F, l.map(_._8)) 330 | assertEquals(l._9F, l.map(_._9)) 331 | assertEquals(l._10F, l.map(_._10)) 332 | assertEquals(l._11F, l.map(_._11)) 333 | assertEquals(l._12F, l.map(_._12)) 334 | assertEquals(l._13F, l.map(_._13)) 335 | assertEquals(l._14F, l.map(_._14)) 336 | assertEquals(l._15F, l.map(_._15)) 337 | assertEquals(l._16F, l.map(_._16)) 338 | assertEquals(l._17F, l.map(_._17)) 339 | assertEquals(l._18F, l.map(_._18)) 340 | assertEquals(l._19F, l.map(_._19)) 341 | assertEquals(l._20F, l.map(_._20)) 342 | } 343 | } 344 | 345 | test( 346 | "_1F, _2F, _3F, _4F, _5F, _6F, _7F, _8F, _9F, _10F, _11F, _12F, _13F, _14F, _15F, _16F, _17F, _18F, _19F, _20F, _21F works for Tuple21" 347 | ) { 348 | forAll { 349 | (l: List[ 350 | (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) 351 | ]) => 352 | assertEquals(l._1F, l.map(_._1)) 353 | assertEquals(l._2F, l.map(_._2)) 354 | assertEquals(l._3F, l.map(_._3)) 355 | assertEquals(l._4F, l.map(_._4)) 356 | assertEquals(l._5F, l.map(_._5)) 357 | assertEquals(l._6F, l.map(_._6)) 358 | assertEquals(l._7F, l.map(_._7)) 359 | assertEquals(l._8F, l.map(_._8)) 360 | assertEquals(l._9F, l.map(_._9)) 361 | assertEquals(l._10F, l.map(_._10)) 362 | assertEquals(l._11F, l.map(_._11)) 363 | assertEquals(l._12F, l.map(_._12)) 364 | assertEquals(l._13F, l.map(_._13)) 365 | assertEquals(l._14F, l.map(_._14)) 366 | assertEquals(l._15F, l.map(_._15)) 367 | assertEquals(l._16F, l.map(_._16)) 368 | assertEquals(l._17F, l.map(_._17)) 369 | assertEquals(l._18F, l.map(_._18)) 370 | assertEquals(l._19F, l.map(_._19)) 371 | assertEquals(l._20F, l.map(_._20)) 372 | assertEquals(l._21F, l.map(_._21)) 373 | } 374 | } 375 | 376 | test( 377 | "_1F, _2F, _3F, _4F, _5F, _6F, _7F, _8F, _9F, _10F, _11F, _12F, _13F, _14F, _15F, _16F, _17F, _18F, _19F, _20F, _21F, _22F works for Tuple22" 378 | ) { 379 | forAll { 380 | (l: List[ 381 | (Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int, Int) 382 | ]) => 383 | assertEquals(l._1F, l.map(_._1)) 384 | assertEquals(l._2F, l.map(_._2)) 385 | assertEquals(l._3F, l.map(_._3)) 386 | assertEquals(l._4F, l.map(_._4)) 387 | assertEquals(l._5F, l.map(_._5)) 388 | assertEquals(l._6F, l.map(_._6)) 389 | assertEquals(l._7F, l.map(_._7)) 390 | assertEquals(l._8F, l.map(_._8)) 391 | assertEquals(l._9F, l.map(_._9)) 392 | assertEquals(l._10F, l.map(_._10)) 393 | assertEquals(l._11F, l.map(_._11)) 394 | assertEquals(l._12F, l.map(_._12)) 395 | assertEquals(l._13F, l.map(_._13)) 396 | assertEquals(l._14F, l.map(_._14)) 397 | assertEquals(l._15F, l.map(_._15)) 398 | assertEquals(l._16F, l.map(_._16)) 399 | assertEquals(l._17F, l.map(_._17)) 400 | assertEquals(l._18F, l.map(_._18)) 401 | assertEquals(l._19F, l.map(_._19)) 402 | assertEquals(l._20F, l.map(_._20)) 403 | assertEquals(l._21F, l.map(_._21)) 404 | assertEquals(l._22F, l.map(_._22)) 405 | } 406 | } 407 | } 408 | --------------------------------------------------------------------------------