Repository: propensive/rapture Branch: dev Commit: bbd9234337dc Files: 219 Total size: 974.3 KB Directory structure: gitextract_x7qdv63e/ ├── .gitignore ├── .jvmopts ├── .scalafmt ├── .travis.yml ├── README.md ├── base/ │ └── shared/ │ └── src/ │ └── main/ │ ├── scala/ │ │ └── Dummy.scala │ ├── scala_2.10/ │ │ └── compat.scala │ ├── scala_2.11/ │ │ └── compat.scala │ └── scala_2.12/ │ └── compat.scala ├── build.sbt ├── cli/ │ └── shared/ │ └── src/ │ ├── main/ │ │ └── scala/ │ │ └── rapture/ │ │ └── cli/ │ │ ├── cli.scala │ │ ├── glob.scala │ │ ├── macros.scala │ │ ├── params.scala │ │ ├── params2.scala │ │ ├── process.scala │ │ ├── tabulate.scala │ │ └── zsh.scala │ └── test/ │ └── scala/ │ └── rapture/ │ └── cli/ │ └── tests.scala ├── codec/ │ └── shared/ │ └── src/ │ └── main/ │ └── scala/ │ └── rapture/ │ └── codec/ │ ├── base64.scala │ ├── bytes.scala │ └── encodings.scala ├── core/ │ └── shared/ │ └── src/ │ └── main/ │ └── scala/ │ └── rapture/ │ └── core/ │ ├── actor.scala │ ├── alloc.scala │ ├── app.scala │ ├── core.scala │ ├── default.scala │ ├── functor.scala │ ├── macros.scala │ ├── med.scala │ ├── modes.scala │ ├── package.scala │ ├── parser.scala │ ├── pool.scala │ ├── result.scala │ ├── serializer.scala │ ├── threads.scala │ └── time.scala ├── core-scalaz/ │ └── shared/ │ └── src/ │ └── main/ │ └── scala/ │ └── rapture/ │ └── core-scalaz/ │ ├── modes.scala │ └── transformers.scala ├── core-test/ │ └── shared/ │ └── src/ │ └── test/ │ └── scala/ │ └── rapture/ │ └── core/ │ └── tests.scala ├── crypto/ │ └── shared/ │ └── src/ │ └── main/ │ └── scala/ │ └── rapture/ │ └── crypto/ │ ├── aes.scala │ └── digest.scala ├── css/ │ └── shared/ │ └── src/ │ └── main/ │ └── scala/ │ └── rapture/ │ └── css/ │ ├── context.scala │ ├── css.scala │ ├── model.scala │ ├── package.scala │ ├── properties.scala │ └── validator.scala ├── css-test/ │ └── shared/ │ └── src/ │ └── test/ │ └── scala/ │ └── rapture/ │ └── css-test/ │ └── tests.scala ├── csv/ │ └── shared/ │ └── src/ │ └── main/ │ └── scala/ │ └── rapture/ │ └── csv/ │ ├── csv.scala │ └── macros.scala ├── currency/ │ └── shared/ │ └── src/ │ └── main/ │ └── scala/ │ └── rapture/ │ └── currency/ │ ├── currency.scala │ └── iso.scala ├── data/ │ └── shared/ │ └── src/ │ └── main/ │ └── scala/ │ └── rapture/ │ └── data/ │ ├── ast.scala │ ├── context.scala │ ├── data.scala │ ├── exceptions.scala │ ├── extractors.scala │ ├── macros.scala │ └── serializers.scala ├── doc/ │ ├── core-scalaz.md │ ├── core.md │ ├── html.md │ ├── i18n.md │ ├── json.md │ └── m4-release.md ├── dom/ │ └── shared/ │ └── src/ │ └── main/ │ └── scala/ │ └── rapture/ │ └── dom/ │ ├── dom.scala │ ├── format.scala │ └── macro.scala ├── etc/ │ ├── header │ └── updateheader.sh ├── fs/ │ └── shared/ │ └── src/ │ └── main/ │ └── scala/ │ └── rapture/ │ └── fs/ │ └── files.scala ├── google-translate/ │ └── shared/ │ └── src/ │ └── main/ │ └── scala/ │ └── rapture/ │ └── google-translate/ │ └── package.scala ├── html/ │ └── shared/ │ └── src/ │ ├── main/ │ │ └── scala/ │ │ └── rapture/ │ │ └── html/ │ │ ├── doc.scala │ │ ├── phantom.scala │ │ └── syntax.scala │ └── test/ │ └── scala/ │ └── rapture/ │ └── html/ │ └── tests.scala ├── http/ │ └── shared/ │ └── src/ │ └── main/ │ └── scala/ │ └── rapture/ │ └── http/ │ ├── extractors.scala │ ├── forms.scala │ ├── handlers.scala │ ├── http.scala │ ├── page.scala │ ├── request.scala │ ├── response.scala │ └── widgets.scala ├── http-jetty/ │ └── shared/ │ └── src/ │ └── main/ │ └── scala/ │ └── rapture/ │ └── http-jetty/ │ ├── http.scala │ └── servlet.scala ├── http-json/ │ └── shared/ │ └── src/ │ └── main/ │ └── scala/ │ └── rapture/ │ └── http-json/ │ └── handler.scala ├── i18n/ │ └── shared/ │ └── src/ │ ├── main/ │ │ └── scala/ │ │ └── rapture/ │ │ └── i18n/ │ │ ├── i18n.scala │ │ ├── languages.scala │ │ └── package.scala │ └── test/ │ └── scala/ │ └── rapture/ │ └── i18n/ │ └── tests.scala ├── io/ │ └── shared/ │ └── src/ │ └── main/ │ └── scala/ │ └── rapture/ │ └── io/ │ ├── contenttype.scala │ ├── copy.scala │ ├── delete.scala │ ├── guid.scala │ ├── io.scala │ ├── move.scala │ ├── multipart.scala │ ├── name.scala │ ├── package.scala │ ├── size.scala │ ├── slurp.scala │ ├── streams.scala │ ├── strings.scala │ └── wrappers.scala ├── java8-support/ │ └── shared/ │ └── src/ │ └── main/ │ └── scala/ │ └── rapture/ │ └── core/ │ └── java8/ │ └── time.scala ├── js/ │ └── shared/ │ └── src/ │ └── main/ │ └── scala/ │ └── rapture/ │ └── js/ │ ├── context.scala │ ├── js.scala │ ├── package.scala │ └── validator.scala ├── json/ │ └── shared/ │ └── src/ │ └── main/ │ └── scala/ │ └── rapture/ │ └── json/ │ ├── ast.scala │ ├── context.scala │ ├── extractors.scala │ ├── formatters.scala │ ├── json.scala │ ├── macros.scala │ ├── package.scala │ ├── serializers.scala │ ├── validator.scala │ └── verifier.scala ├── json-argonaut/ │ └── shared/ │ └── src/ │ └── main/ │ └── scala/ │ └── rapture/ │ └── json-argonaut/ │ ├── ast.scala │ ├── extractors.scala │ ├── package.scala │ ├── parse.scala │ └── serializers.scala ├── json-circe/ │ └── shared/ │ └── src/ │ └── main/ │ └── scala/ │ └── rapture/ │ └── json-circe/ │ ├── ast.scala │ ├── extractors.scala │ ├── package.scala │ ├── parse.scala │ └── serializers.scala ├── json-jackson/ │ └── shared/ │ └── src/ │ └── main/ │ └── scala/ │ └── rapture/ │ └── json-jackson/ │ ├── ast.scala │ ├── extractors.scala │ ├── package.scala │ ├── parse.scala │ └── serializers.scala ├── json-jawn/ │ └── shared/ │ └── src/ │ └── main/ │ └── scala/ │ └── rapture/ │ └── json-jawn/ │ ├── ast.scala │ ├── extractors.scala │ ├── package.scala │ ├── parse.scala │ └── serializers.scala ├── json-json4s/ │ └── shared/ │ └── src/ │ └── main/ │ └── scala/ │ └── rapture/ │ └── json-json4s/ │ ├── ast.scala │ ├── extractors.scala │ ├── package.scala │ ├── parse.scala │ └── serializers.scala ├── json-lift/ │ └── shared/ │ └── src/ │ └── main/ │ └── scala/ │ └── rapture/ │ └── json-lift/ │ ├── ast.scala │ ├── extractors.scala │ ├── package.scala │ ├── parse.scala │ └── serializers.scala ├── json-play/ │ └── shared/ │ └── src/ │ └── main/ │ └── scala/ │ └── rapture/ │ └── json-play/ │ ├── ast.scala │ ├── extraction.scala │ ├── package.scala │ ├── parse.scala │ └── serializers.scala ├── json-spray/ │ └── shared/ │ └── src/ │ └── main/ │ └── scala/ │ └── rapture/ │ └── json-spray/ │ ├── ast.scala │ ├── extraction.scala │ ├── package.scala │ ├── parse.scala │ └── serializers.scala ├── json-test/ │ └── shared/ │ └── src/ │ └── test/ │ └── scala/ │ └── rapture/ │ └── json-test/ │ ├── java8TimeTests.scala │ └── tests.scala ├── latex/ │ └── shared/ │ └── src/ │ └── main/ │ └── scala/ │ └── latex/ │ └── latex.scala ├── log/ │ └── shared/ │ └── src/ │ └── main/ │ └── scala/ │ └── rapture/ │ └── log/ │ ├── levels.scala │ ├── log.scala │ └── parts.scala ├── mail/ │ ├── lib/ │ │ └── javamail.jar │ └── shared/ │ └── src/ │ └── main/ │ └── scala/ │ └── rapture/ │ └── mail/ │ ├── javamail.scala │ ├── mail.scala │ └── plaintext.scala ├── mime/ │ └── shared/ │ └── src/ │ └── main/ │ └── scala/ │ └── rapture/ │ └── mime/ │ └── mime.scala ├── net/ │ └── shared/ │ └── src/ │ └── main/ │ └── scala/ │ └── rapture/ │ └── net/ │ ├── browser.scala │ ├── exceptions.scala │ ├── http.scala │ ├── ip.scala │ ├── net.scala │ ├── package.scala │ ├── services.scala │ └── sockets.scala ├── net-test/ │ └── shared/ │ └── src/ │ └── test/ │ └── scala/ │ └── rapture/ │ └── net/ │ └── test/ │ ├── DockerHttpBinServerSpec.scala │ ├── HttpClientSpec.scala │ └── helper/ │ └── DockerContainer.scala ├── project/ │ ├── build.properties │ └── plugins.sbt ├── test/ │ └── shared/ │ └── src/ │ └── main/ │ └── scala/ │ └── rapture/ │ └── test/ │ ├── macros.scala │ ├── report.scala │ ├── scalatest.scala │ └── test.scala ├── text/ │ └── shared/ │ └── src/ │ └── main/ │ └── scala/ │ └── rapture/ │ └── text/ │ ├── ansi.scala │ └── text.scala ├── time/ │ └── shared/ │ └── src/ │ └── main/ │ └── scala/ │ └── rapture/ │ └── time/ │ └── time.scala ├── unixsocket/ │ └── jvm/ │ └── src/ │ └── main/ │ └── scala/ │ └── rapture/ │ └── unixsocket/ │ ├── UnixSocketHttpClient.scala │ ├── UnixSocketHttpUrl.scala │ └── package.scala ├── uri/ │ └── shared/ │ └── src/ │ └── main/ │ └── scala/ │ └── rapture/ │ └── uri/ │ ├── classpath.scala │ ├── macros.scala │ ├── nav.scala │ ├── package.scala │ ├── paths.scala │ └── uri.scala ├── version.sbt ├── xml/ │ └── shared/ │ └── src/ │ └── main/ │ └── scala/ │ └── rapture/ │ └── xml/ │ ├── ast.scala │ ├── context.scala │ ├── extractors.scala │ ├── formatters.scala │ ├── macros.scala │ ├── package.scala │ ├── serializers.scala │ ├── validator.scala │ └── xml.scala ├── xml-stdlib/ │ └── shared/ │ └── src/ │ └── main/ │ └── scala/ │ └── rapture/ │ └── xml-stdlib/ │ ├── ast.scala │ ├── extractors.scala │ ├── package.scala │ ├── parse.scala │ └── serializers.scala └── xml-test/ └── shared/ └── src/ └── test/ └── scala/ └── rapture/ └── xml-test/ ├── java8TimeTests.scala └── tests.scala ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ project/boot target .ensime .ensime_lucene TAGS \#*# *~ .#* .lib .history .*.swp .idea .idea/* .idea_modules .DS_Store .cache .settings .classpath .project ================================================ FILE: .jvmopts ================================================ # see https://weblogs.java.net/blog/kcpeppe/archive/2013/12/11/case-study-jvm-hotspot-flags -Dfile.encoding=UTF8 -Xms2G -Xmx4G -Xss6M -XX:MaxPermSize=512M -XX:ReservedCodeCacheSize=250M -XX:+TieredCompilation -XX:-UseGCOverheadLimit # effectively adds GC to Perm space -XX:+CMSClassUnloadingEnabled # must be enabled for CMSClassUnloadingEnabled to work -XX:+UseConcMarkSweepGC ================================================ FILE: .scalafmt ================================================ --maxColumn 120 ================================================ FILE: .travis.yml ================================================ language: scala services: - docker sudo: required git: depth: 50 scala: - 2.10.6 - 2.11.8 - 2.12.1 jdk: - oraclejdk8 script: - if [[ "$TRAVIS_PULL_REQUEST" == "false" && "$TRAVIS_BRANCH" == "dev" && $(cat version.sbt) =~ "-SNAPSHOT" ]] ; then sbt ++$TRAVIS_SCALA_VERSION test publish gitSnapshots publish ; else sbt ++$TRAVIS_SCALA_VERSION test ; fi notifications: webhooks: urls: - https://webhooks.gitter.im/e/NX9T51sqZ7a2U3zE8Thm - https://webhooks.gitter.im/e/42e709a53fb8e39472d3 on_success: always on_failure: always on_start: true env: global: - secure: DyrwS40Re2KfnuEMXNDkrWHDLeXzBsM1pCUBWy8ApexKEwI2SuO4at2th6yC8QvvLMmOkvDkheRVg1yENYTtUjerx4HATEoQcQ6RFjAfw4RmQLuUYWZFomGJ/q0KQ2EZmCCljbXM1q9vnhETuCgOcAMS5IRYOJf0EPYPFNArt8A= - secure: c5OZn0AALmuPiZz8VYDqUNfJzxoUJ6dO/i1J3QQ/b9DQd2gWkdpAvpgqLX7SdyL26IssWPMJ4Zc6utMd8ONLrCdoPaFJr1arflwpSuP+tadxJEl2H0EBjSL2WFsce8j7HbhwGtoVwee2bKJ5gAMilInQXSoMqm9b5EBIN0JA2Ks= cache: directories: - $HOME/.sbt/0.13/dependency - $HOME/.sbt/boot/ - $HOME/.sbt/launchers - $HOME/.ivy2/cache before_cache: - du -h -d 1 $HOME/.ivy2/cache - du -h -d 2 $HOME/.sbt/ - find $HOME/.sbt -name "*.lock" -type f -delete - find $HOME/.ivy2/cache -name "ivydata-*.properties" -type f -delete ================================================ FILE: README.md ================================================ # Rapture [![Build Status](https://travis-ci.org/propensive/rapture.png?branch=dev)](https://travis-ci.org/propensive/rapture) [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.propensive/rapture-core_2.11/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.propensive/rapture-core_2.11) [![License](http://img.shields.io/:license-Apache%202-brightgreen.svg)](http://www.apache.org/licenses/LICENSE-2.0.txt) [![Gitter](https://img.shields.io/badge/gitter-join%20chat-green.svg)](https://gitter.im/propensive/rapture) [![Javadocs](https://javadoc.io/badge/com.propensive/rapture_2.12.svg)](https://javadoc.io/doc/com.propensive/rapture-core_2.12) Rapture is an evolving collection of *useful* libraries for solving common, everyday programming tasks, using advanced features of Scala to offer better type-safety through powerful APIs that all Scala developers, beginners and advanced users, should find intuitive. Rapture consists of a number of modules, the most notable of which are: - Core (`core`) — a library of common utilities for other projects, notably *modes* and the `Result` type - [JSON](doc/json.md) (`json`) — comprehensive support for working with JSON data - XML (`xml`) — comprehensive, but experimental, support for working with XML data - I/O (`io`) — I/O (network, filesystem) functionality and infrastructure - I18n (`i18n`) — simple, typesafe representation of internationalized strings - CLI (`cli`) — support for working with command-line applications and shell interaction # Themes in Rapture The Rapture modules share a common philosophy that has evolved over time and experience. Here are a few of the philosophical themes crosscutting all of the Rapture modules. - A primary goal of intuitive, readable APIs and minimal code repetition - Extreme type-safety, with a goal to reduce the surface area of code exposed to runtime exceptions - Thoroughly typeclass-driven design, for extensibility - Fearless exploitation of all Scala features, where (but only where) it is appropriate - Agnostic support for multiple, alternative implementations of many operations with pluggable backends - Extensive, but principled, usage of implicits to configure and constrain operations - Support for modes in most APIs; the ability to change how failures are handled through return types ## Availability Snapshots of Rapture are available for Scala 2.10 and 2.11 under the *Apache 2.0 License* in the [Sonatype Snapshots repository](https://oss.sonatype.org/content/repositories/snapshots/com/propensive/), with group ID `com.propensive` and artifact ID `rapture-[module]`, where module is the name of the module, as taken from the list above. Development work to get most Rapture modules working on [Scala.JS](htp://www.scala-js.org/) is ongoing. You can build and run Rapture locally by cloning this repository and running `sbt publishLocal`. ## Contributing Rapture openly welcomes contributions! We would love to receive pull requests of bugfixes and enhancements from other developers. To avoid potential wasted effort, bugs should first be reported on the Github issue tracker, and it's normally a good idea to talk about enhancements on the [Gitter channel](https://gitter.im/propensive/rapture) before embarking on any development. Alternatively, just send Jon Pretty ([@propensive](https://twitter.com/propensive/)) a tweet to start a conversation. Current contributors include: - Jon Pretty - Raúl Raja Martínez - Alistair Johnson ## Documentation Rapture's documentation is currently sparse, though we are working to improve this. ================================================ FILE: base/shared/src/main/scala/Dummy.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ ================================================ FILE: base/shared/src/main/scala_2.10/compat.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.base import scala.reflect._ import macros._ object `package` { type BlackboxContext = Context type WhiteboxContext = Context lazy val compatibility = new Compat210() } class Compat210() { def termName(c: Context, s: String) = c.universe.newTermName(s) def typeName(c: Context, s: String) = c.universe.newTypeName(s) def constructor(c: Context) = c.universe.nme.CONSTRUCTOR def wildcard(c: Context) = c.universe.nme.WILDCARD def typeIntersection(c: Context)(xs: List[c.universe.Type]) = c.universe.intersectionType(xs) def paramLists(c: Context)(t: c.universe.MethodSymbol) = t.paramss def normalize(c: Context)(t: c.universe.Type) = t.normalize def declarations(c: Context)(t: c.universe.Type) = t.declarations def declaration(c: Context)(t: c.universe.Type, n: c.universe.Name) = t.declaration(n) def readLine(): String = Console.readLine() def typecheck(c: Context)(s: c.Tree) = c.typeCheck(s) def freshName(c: Context)(s: String) = c.fresh(s) def companion(c: Context)(x: c.universe.Symbol) = x.companionSymbol def samePosition(c: Context)(p1: c.universe.Position, p2: c.universe.Position) = p1.isDefined && p1.point == p2.point def enclosingDef(c: Context)(pos: c.universe.Position): Option[c.universe.Name] = { import c.universe._ c.enclosingClass.collect { case DefDef(_, name, _, _, _, rhs) if samePosition(c)(rhs.pos, pos) => name }.headOption } def enclosingVals(c: Context)(pos: c.universe.Position, count: Int): Option[c.universe.Name] = { import c.universe._ c.enclosingClass.collect { case ValDef(_, name, _, rhs) if samePosition(c)(rhs.pos, pos) => name }.drop(count).headOption } } ================================================ FILE: base/shared/src/main/scala_2.11/compat.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.base import scala.reflect._ import macros._ object `package` { type BlackboxContext = blackbox.Context type WhiteboxContext = whitebox.Context lazy val compatibility = new Compat211() } class Compat211() { def termName[C <: blackbox.Context](c: C, s: String) = c.universe.TermName(s) def typeName[C <: blackbox.Context](c: C, s: String) = c.universe.TypeName(s) def constructor[C <: blackbox.Context](c: C) = c.universe.termNames.CONSTRUCTOR def wildcard[C <: blackbox.Context](c: C) = c.universe.termNames.WILDCARD def typeIntersection[C <: blackbox.Context](c: C)(xs: List[c.universe.Type]) = c.universe.internal.intersectionType(xs) def paramLists[C <: blackbox.Context](c: C)(t: c.universe.MethodSymbol) = t.paramLists def normalize[C <: blackbox.Context](c: C)(t: c.universe.Type) = t.dealias def declarations[C <: blackbox.Context](c: C)(t: c.universe.Type) = t.decls def declaration[C <: blackbox.Context](c: C)(t: c.universe.Type, d: c.universe.Name) = t.decl(d) def readLine(): String = scala.io.StdIn.readLine def typecheck[C <: blackbox.Context](c: C)(t: c.Tree) = c.typecheck(t) def freshName[C <: blackbox.Context](c: C)(s: String) = c.freshName(s) def companion[C <: blackbox.Context](c: C)(s: c.universe.Symbol) = s.companion def samePosition[C <: blackbox.Context](c: C)(p1: c.universe.Position, p2: c.universe.Position) = { import c.universe._ p1 != NoPosition && p2 != NoPosition && p1.start == p2.start } def enclosingDef[C <: blackbox.Context](c: C)(pos: c.universe.Position): Option[c.universe.Name] = Some(c.internal.enclosingOwner.asTerm.name) def enclosingVals[C <: blackbox.Context](c: C)(pos: c.universe.Position, count: Int): Option[c.universe.Name] = Some(c.internal.enclosingOwner.asTerm.name) } ================================================ FILE: base/shared/src/main/scala_2.12/compat.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.base import scala.reflect._ import macros._ object `package` { type BlackboxContext = blackbox.Context type WhiteboxContext = whitebox.Context lazy val compatibility = new Compat212() } class Compat212() { def termName[C <: blackbox.Context](c: C, s: String) = c.universe.TermName(s) def typeName[C <: blackbox.Context](c: C, s: String) = c.universe.TypeName(s) def constructor[C <: blackbox.Context](c: C) = c.universe.termNames.CONSTRUCTOR def wildcard[C <: blackbox.Context](c: C) = c.universe.termNames.WILDCARD def typeIntersection[C <: blackbox.Context](c: C)(xs: List[c.universe.Type]) = c.universe.internal.intersectionType(xs) def paramLists[C <: blackbox.Context](c: C)(t: c.universe.MethodSymbol) = t.paramLists def normalize[C <: blackbox.Context](c: C)(t: c.universe.Type) = t.dealias def declarations[C <: blackbox.Context](c: C)(t: c.universe.Type) = t.decls def declaration[C <: blackbox.Context](c: C)(t: c.universe.Type, d: c.universe.Name) = t.decl(d) def readLine(): String = scala.io.StdIn.readLine def typecheck[C <: blackbox.Context](c: C)(t: c.Tree) = c.typecheck(t) def freshName[C <: blackbox.Context](c: C)(s: String) = c.freshName(s) def companion[C <: blackbox.Context](c: C)(s: c.universe.Symbol) = s.companion def samePosition[C <: blackbox.Context](c: C)(p1: c.universe.Position, p2: c.universe.Position) = { import c.universe._ p1 != NoPosition && p2 != NoPosition && p1.start == p2.start } def enclosingDef[C <: blackbox.Context](c: C)(pos: c.universe.Position): Option[c.universe.Name] = Some(c.internal.enclosingOwner.asTerm.name) def enclosingVals[C <: blackbox.Context](c: C)(pos: c.universe.Position, count: Int): Option[c.universe.Name] = Some(c.internal.enclosingOwner.asTerm.name) } ================================================ FILE: build.sbt ================================================ import com.typesafe.sbt.pgp.PgpKeys.publishSigned import ReleaseTransformations._ enablePlugins(GitBranchPrompt) lazy val buildSettings = Seq( organization := "com.propensive", scalaVersion := "2.12.1", crossScalaVersions := Seq("2.12.2", "2.11.8", "2.10.6") ) lazy val commonSettings = Seq( // scalafmtConfig in ThisBuild := Some(file(".scalafmt")), scalacOptions ++= Seq( "-deprecation", "-encoding", "UTF-8", "-feature", "-unchecked", "-Xfatal-warnings", "-Xlint", "-language:existentials" /* "-language:higherKinds", "-language:implicitConversions", "-language:experimental.macros", "-Yinline-warnings", "-Yno-adapted-args", "-Ywarn-numeric-widen", "-Ywarn-value-discard", "-Xfuture" */ ) ++ (CrossVersion.partialVersion(scalaVersion.value) match { case Some((2, majorVersion)) if majorVersion >= 11 => Seq("-Ywarn-unused-import") case _ => Seq.empty }), scalacOptions in (Compile, console) ~= (_ filterNot (_ == "-Ywarn-unused-import")), scalacOptions in (Test, console) := (scalacOptions in (Compile, console)).value, scalaJSStage in Test := FastOptStage, concurrentRestrictions in Global ++= Seq(Tags.limitSum(2, Tags.CPU, Tags.Untagged), Tags.limit(Tags.Test, 1)), scmInfo := Some(ScmInfo(url("https://github.com/propensive/rapture"), "scm:git:git@github.com:propensive/rapture.git")) ) ++ scalaMacroDependencies lazy val raptureSettings = buildSettings ++ commonSettings ++ publishSettings lazy val rapture = project.in(file(".")) .settings(moduleName := "root") .settings(raptureSettings) .settings(noPublishSettings) .settings(noSourceSettings) .aggregate(raptureJVM, raptureJS, raptureExtrasJVM, raptureExtrasJS) .dependsOn(raptureJVM, raptureJS, raptureExtrasJVM, raptureExtrasJS) lazy val raptureJVM = project.in(file(".raptureJVM")) .settings(moduleName := "rapture") .settings(raptureSettings) .aggregate(baseJVM, coreJVM, timeJVM, uriJVM, codecJVM, cryptoJVM, csvJVM, ioJVM, fsJVM, netJVM, httpJVM, mimeJVM, cliJVM, mailJVM, logJVM, i18nJVM, googleTranslateJVM, textJVM, latexJVM, testJVM, dataJVM, xmlJVM, jsJVM, cssJVM, currencyJVM, jsonJVM, htmlJVM, domJVM, coreScalazJVM, httpJsonJVM, java8SupportJVM, unixsocketJVM) .dependsOn(baseJVM, coreJVM, timeJVM, uriJVM, codecJVM, cryptoJVM, csvJVM, ioJVM, fsJVM, netJVM, httpJVM, mimeJVM, cliJVM, mailJVM, logJVM, i18nJVM, googleTranslateJVM, textJVM, latexJVM, testJVM, dataJVM, xmlJVM, jsJVM, cssJVM, currencyJVM, jsonJVM, htmlJVM, domJVM, coreScalazJVM, httpJsonJVM, java8SupportJVMi, unixsocketJVM) lazy val raptureJS = project.in(file(".raptureJS")) .settings(moduleName := "rapture") .settings(raptureSettings) .aggregate(baseJS, coreJS, timeJS, uriJS, codecJS, cryptoJS, csvJS, ioJS, fsJS, netJS, httpJS, mimeJS, cliJS, mailJS, logJS, i18nJS, googleTranslateJS, textJS, latexJS, testJS, dataJS, jsonJS, htmlJS, domJS, coreScalazJS, httpJsonJS, xmlJS, jsJS, cssJS, currencyJS, java8SupportJS) .dependsOn(baseJS, coreJS, timeJS, uriJS, codecJS, cryptoJS, csvJS, ioJS, fsJS, netJS, httpJS, mimeJS, cliJS, mailJS, logJS, i18nJS, googleTranslateJS, textJS, latexJS, testJS, dataJS, jsonJS, htmlJS, domJS, coreScalazJS, httpJsonJS, xmlJS, jsJS, cssJS, currencyJS, java8SupportJS) .enablePlugins(ScalaJSPlugin) lazy val raptureExtras = crossProject .aggregate(`core-test`, `http-jetty`, `json-circe`, `xml-stdlib`, `json-jawn`, `json-play`, `json-json4s`, `json-spray`, `json-argonaut`, `json-jackson`, `json-test`, `xml-test`, `json-lift`, `net-test`, `java8-support`) .dependsOn(`core-test`, `http-jetty`, `json-circe`, `xml-stdlib`, `json-jawn`, `json-play`, `json-json4s`, `json-spray`, `json-argonaut`, `json-jackson`, `json-test`, `xml-test`, `json-lift`, `net-test`, `java8-support`) .settings(moduleName := "rapture-extras") .settings(raptureSettings:_*) .settings(crossVersionSharedSources():_*) lazy val raptureExtrasJVM = raptureExtras.jvm lazy val raptureExtrasJS = raptureExtras.js // rapture-base lazy val base = crossProject .settings(moduleName := "rapture-base") .settings(raptureSettings:_*) .settings(crossVersionSharedSources():_*) lazy val baseJVM = base.jvm lazy val baseJS = base.js // rapture-core lazy val core = crossProject.dependsOn(base) .settings(moduleName := "rapture-core") .settings(raptureSettings:_*) lazy val coreJVM = core.jvm lazy val coreJS = core.js // rapture-core-test lazy val `core-test` = crossProject.dependsOn(test, `core-scalaz`) .settings(moduleName := "rapture-core-test") .settings(raptureSettings:_*) lazy val coreTestJVM = `core-test`.jvm lazy val coreTestJS = `core-test`.js // rapture-uri lazy val uri = crossProject.dependsOn(core) .settings(moduleName := "rapture-uri") .settings(raptureSettings:_*) lazy val uriJVM = uri.jvm lazy val uriJS = uri.js // rapture-codec lazy val codec = crossProject.dependsOn(core) .settings(moduleName := "rapture-codec") .settings(raptureSettings:_*) lazy val codecJVM = codec.jvm lazy val codecJS = codec.js // rapture-crypto lazy val crypto = crossProject.dependsOn(core, codec) .settings(moduleName := "rapture-crypto") .settings(raptureSettings:_*) lazy val cryptoJVM = crypto.jvm lazy val cryptoJS = crypto.js // rapture-io lazy val io = crossProject.dependsOn(codec, mime, uri) .settings(moduleName := "rapture-io") .settings(raptureSettings:_*) lazy val ioJVM = io.jvm lazy val ioJS = io.js // rapture-mime lazy val mime = crossProject.dependsOn() .settings(moduleName := "rapture-mime") .settings(raptureSettings:_*) lazy val mimeJVM = mime.jvm lazy val mimeJS = mime.js // rapture-net lazy val net = crossProject.dependsOn(io) .settings(moduleName := "rapture-net") .settings(raptureSettings:_*) .settings(libraryDependencies += "commons-net" % "commons-net" % "2.0") lazy val netJVM = net.jvm lazy val netJS = net.js // rapture-net-test lazy val `net-test` = crossProject.dependsOn(net, test, json, `json-circe`) .settings(moduleName := "rapture-net-test") .settings(raptureSettings: _*) .settings( libraryDependencies ++= Seq( "com.spotify" % "docker-client" % "5.0.2", "org.scalatest" %% "scalatest" % "2.2.6" ) ) lazy val netTestJVM = `net-test`.jvm lazy val netTestJS = `net-test`.js // rapture-unixsocket // this is not a scala-js project - it may only be used on the jvm and on linux lazy val unixsocketJVM = (project in file("unixsocket/jvm")).dependsOn(io.jvm, core.jvm, net.jvm, uri.jvm) .settings(moduleName := "rapture-unixsocket") .settings(raptureSettings: _*) .settings(libraryDependencies ++= Seq( "com.kohlschutter.junixsocket" % "junixsocket-native" % "2.0.4", "com.kohlschutter.junixsocket" % "junixsocket-native-common" % "2.0.4", "com.kohlschutter.junixsocket" % "junixsocket-common" % "2.0.4", "org.apache.httpcomponents" % "httpclient" % "4.5.2" )) // rapture-time lazy val time = crossProject.dependsOn(core) .settings(moduleName := "rapture-time") .settings(raptureSettings:_*) lazy val timeJVM = time.jvm lazy val timeJS = time.js // rapture-http lazy val http = crossProject.dependsOn(net, uri, json, html, fs, log, time) .settings(moduleName := "rapture-http") .settings(raptureSettings:_*) .settings(libraryDependencies += "javax.servlet" % "servlet-api" % "2.5") .settings(libraryDependencies += "org.w3c.css" % "sac" % "1.3") .settings(libraryDependencies += "net.sourceforge.cssparser" % "cssparser" % "0.9.20") lazy val httpJVM = http.jvm lazy val httpJS = http.js // rapture-http-json lazy val `http-json` = crossProject.dependsOn(http, json) .settings(moduleName := "rapture-http-json") .settings(raptureSettings:_*) lazy val httpJsonJVM = `http-json`.jvm lazy val httpJsonJS = `http-json`.js // rapture-http-jetty lazy val `http-jetty` = crossProject.dependsOn(http) .settings(moduleName := "rapture-http-jetty") .settings(raptureSettings:_*) .settings(libraryDependencies += "org.eclipse.jetty" % "jetty-servlet" % "7.6.10.v20130312") lazy val httpJettyJVM = `http-jetty`.jvm lazy val httpJettyJS = `http-jetty`.js // rapture-fs lazy val fs = crossProject.dependsOn(io) .settings(moduleName := "rapture-fs") .settings(raptureSettings:_*) lazy val fsJVM = fs.jvm lazy val fsJS = fs.js // rapture-csv lazy val csv = crossProject.dependsOn(fs) .settings(moduleName := "rapture-csv") .settings(raptureSettings:_*) lazy val csvJVM = csv.jvm lazy val csvJS = csv.js // rapture-cli lazy val cli = crossProject.dependsOn(log, fs) .settings(moduleName := "rapture-cli") .settings(raptureSettings:_*) lazy val cliJVM = cli.jvm lazy val cliJS = cli.js // rapture-mail lazy val mail = crossProject.dependsOn(io, html, net) .settings(moduleName := "rapture-mail") .settings(raptureSettings:_*) .settings(libraryDependencies += "javax.mail" % "mail" % "1.4") lazy val mailJVM = mail.jvm lazy val mailJS = mail.js // rapture-log lazy val log = crossProject.dependsOn(io) .settings(moduleName := "rapture-log") .settings(raptureSettings:_*) lazy val logJVM = log.jvm lazy val logJS = log.js // rapture-i18n lazy val i18n = crossProject.dependsOn(core, test) .settings(moduleName := "rapture-i18n") .settings(raptureSettings:_*) lazy val i18nJVM = i18n.jvm lazy val i18nJS = i18n.js // rapture-google-translate lazy val `google-translate` = crossProject.dependsOn(core, net, `json-jawn`, i18n) .settings(moduleName := "rapture-google-translate") .settings(raptureSettings:_*) lazy val googleTranslateJVM = `google-translate`.jvm lazy val googleTranslateJS = `google-translate`.js // rapture-text lazy val text = crossProject.dependsOn(core) .settings(moduleName := "rapture-text") .settings(raptureSettings:_*) lazy val textJVM = text.jvm lazy val textJS = text.js // rapture-latex lazy val latex = crossProject.dependsOn(text, cli) .settings(moduleName := "rapture-latex") .settings(raptureSettings:_*) lazy val latexJVM = latex.jvm lazy val latexJS = latex.js // rapture-test lazy val test = crossProject.dependsOn(cli, fs, text) .settings(moduleName := "rapture-test") .settings(raptureSettings:_*) .settings(libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.1") lazy val testJVM = test.jvm lazy val testJS = test.js // rapture-dom lazy val dom = crossProject.dependsOn(core) .settings(moduleName := "rapture-dom") .settings(raptureSettings:_*) lazy val domJVM = dom.jvm lazy val domJS = dom.js // rapture-html lazy val html = crossProject.dependsOn(net, mime, dom, test, js, css) .settings(moduleName := "rapture-html") .settings(raptureSettings:_*) lazy val htmlJVM = html.jvm lazy val htmlJS = html.js // rapture-data lazy val data = crossProject.dependsOn(core) .settings(moduleName := "rapture-data") .settings(raptureSettings:_*) lazy val dataJVM = data.jvm lazy val dataJS = data.js // rapture-xml lazy val xml = crossProject.dependsOn(data) .settings(moduleName := "rapture-xml") .settings(raptureSettings:_*) lazy val xmlJVM = xml.jvm lazy val xmlJS = xml.js // rapture-js lazy val js = crossProject.dependsOn(data) .settings(moduleName := "rapture-js") .settings(raptureSettings:_*) lazy val jsJVM = js.jvm lazy val jsJS = js.js // rapture-css lazy val css = crossProject.dependsOn(data, dom) .settings(moduleName := "rapture-css") .settings(raptureSettings:_*) .settings(libraryDependencies += "net.sourceforge.cssparser" % "cssparser" % "0.9.20") lazy val cssJVM = css.jvm lazy val cssJS = css.js // rapture-currency lazy val currency = crossProject.dependsOn(data) .settings(moduleName := "rapture-currency") .settings(raptureSettings:_*) lazy val currencyJVM = currency.jvm lazy val currencyJS = currency.js // rapture-json lazy val json = crossProject.dependsOn(data) .settings(moduleName := "rapture-json") .settings(raptureSettings:_*) lazy val jsonJVM = json.jvm lazy val jsonJS = json.js // rapture-java8-support lazy val `java8-support` = crossProject.dependsOn(core) .settings(moduleName := "rapture-java8-support") .settings(raptureSettings:_*) lazy val java8SupportJVM = `java8-support`.jvm lazy val java8SupportJS = `java8-support`.js // rapture-json-circe lazy val `json-circe` = crossProject.dependsOn(json) .settings(moduleName := "rapture-json-circe") .settings(raptureSettings:_*) .settings(libraryDependencies += "io.circe" %% "circe-core" % "0.7.0") .settings(libraryDependencies += "io.circe" %% "circe-jawn" % "0.7.0") lazy val jsonCirceJVM = `json-circe`.jvm lazy val jsonCirceJS = `json-circe`.js // rapture-xml-stdlib lazy val `xml-stdlib` = crossProject.dependsOn(xml) .settings(moduleName := "rapture-xml-stdlib") .settings(raptureSettings:_*) lazy val xmlStdlibJVM = `xml-stdlib`.jvm lazy val xmlStdlibJS = `xml-stdlib`.js // rapture-json-jawn lazy val `json-jawn` = crossProject.dependsOn(json) .settings(moduleName := "rapture-json-jawn") .settings(raptureSettings:_*) .settings(libraryDependencies += "org.spire-math" %% "jawn-parser" % "0.10.4") .settings(libraryDependencies += "org.spire-math" %% "jawn-ast" % "0.10.4") lazy val jsonJawnJVM = `json-jawn`.jvm lazy val jsonJawnJS = `json-jawn`.js lazy val playJsonDependencies: Seq[Setting[_]] = Seq( libraryDependencies += (CrossVersion.partialVersion(scalaVersion.value) match { case Some((2, 10)) => "com.typesafe.play" %% "play-json" % "2.4.6" case Some((2, 11)) => "com.typesafe.play" %% "play-json" % "2.5.3" case Some((2, 12)) => "com.typesafe.play" %% "play-json" % "2.6.0-M1" }) ) // rapture-json-play lazy val `json-play` = crossProject.dependsOn(json) .settings(moduleName := "rapture-json-play") .settings(raptureSettings: _*) .settings(playJsonDependencies: _*) lazy val jsonPlayJVM = `json-play`.jvm lazy val jsonPlayJS = `json-play`.js // rapture-json-json4s lazy val `json-json4s` = crossProject.dependsOn(json) .settings(moduleName := "rapture-json-json4s") .settings(raptureSettings:_*) .settings(libraryDependencies += "org.json4s" %% "json4s-native" % "3.5.0") lazy val jsonJson4sJVM = `json-json4s`.jvm lazy val jsonJson4sJS = `json-json4s`.js // rapture-json-spray lazy val `json-spray` = crossProject.dependsOn(json) .settings(moduleName := "rapture-json-spray") .settings(raptureSettings:_*) .settings(libraryDependencies += "io.spray" %% "spray-json" % "1.3.3") lazy val jsonSprayJVM = `json-spray`.jvm lazy val jsonSprayJS = `json-spray`.js // rapture-json-argonaut lazy val `json-argonaut` = crossProject.dependsOn(json) .settings(moduleName := "rapture-json-argonaut") .settings(raptureSettings:_*) .settings(libraryDependencies += "io.argonaut" %% "argonaut" % "6.2-RC1") lazy val jsonArgonautJVM = `json-argonaut`.jvm lazy val jsonArgonautJS = `json-argonaut`.js // rapture-json-jackson lazy val `json-jackson` = crossProject.dependsOn(json) .settings(moduleName := "rapture-json-jackson") .settings(raptureSettings:_*) .settings(libraryDependencies += "com.fasterxml.jackson.core" % "jackson-databind" % "2.7.2") lazy val jsonJacksonJVM = `json-jackson`.jvm lazy val jsonJacksonJS = `json-jackson`.js // rapture-core-scalaz lazy val `core-scalaz` = crossProject.dependsOn(core) .settings(moduleName := "rapture-core-scalaz") .settings(raptureSettings:_*) .settings(libraryDependencies += "org.scalaz" %% "scalaz-core" % "7.2.8") .settings(libraryDependencies += "org.scalaz" %% "scalaz-concurrent" % "7.2.8") lazy val coreScalazJVM = `core-scalaz`.jvm lazy val coreScalazJS = `core-scalaz`.js // rapture-json-test lazy val `json-test` = crossProject .dependsOn(`json-jawn`, `json-lift`, `json-spray`, `json-argonaut`, `json-jackson`, `json-play`, `json-json4s`, `json-circe`, `java8-support`, test) .settings(moduleName := "rapture-json-test") .settings(raptureSettings:_*) lazy val jsonTestJVM = `json-test`.jvm lazy val jsonTestJS = `json-test`.js // rapture-css-test lazy val `css-test` = crossProject.dependsOn(css, html, test) .settings(moduleName := "rapture-css-test") .settings(raptureSettings:_*) lazy val cssTestJVM = `css-test`.jvm lazy val cssTestJS = `css-test`.js // rapture-xml-test lazy val `xml-test` = crossProject.dependsOn(`xml-stdlib`, `java8-support`, test) .settings(moduleName := "rapture-xml-test") .settings(raptureSettings:_*) lazy val xmlTestJVM = `xml-test`.jvm lazy val xmlTestJS = `xml-test`.js // rapture-json-lift lazy val `json-lift` = crossProject.dependsOn(json) .settings(moduleName := "rapture-json-lift") .settings(raptureSettings:_*) .settings(libraryDependencies += (CrossVersion.partialVersion(scalaVersion.value) match { case Some((2, 10)) => "net.liftweb" %% "lift-json" % "2.6.3" case Some((2, scalaMajor)) if scalaMajor >= 11 => "net.liftweb" %% "lift-json" % "3.0.1" })) lazy val jsonLiftJVM = `json-lift`.jvm lazy val jsonLiftJS = `json-lift`.js lazy val publishSettings = Seq( homepage := Some(url("http://rapture.io/")), licenses := Seq("Apache-2.0" -> url("http://www.apache.org/licenses/LICENSE-2.0.txt")), autoAPIMappings := true, publishMavenStyle := true, publishArtifact in Test := false, pomIncludeRepository := { _ => false }, publishTo := { val nexus = "https://oss.sonatype.org/" if (isSnapshot.value) Some("snapshots" at nexus + "content/repositories/snapshots") else Some("releases" at nexus + "service/local/staging/deploy/maven2") }, pomExtra := ( propensive Jon Petty http://github.com/propensive/rapture ), releaseProcess := Seq[ReleaseStep]( checkSnapshotDependencies, inquireVersions, runTest, setReleaseVersion, commitReleaseVersion, tagRelease, publishArtifacts, setNextVersion, commitNextVersion, ReleaseStep(action = Command.process("sonatypeReleaseAll", _)), pushChanges ), releaseCrossBuild := true, releasePublishArtifactsAction := PgpKeys.publishSigned.value ) lazy val noPublishSettings = Seq( publish := (), publishLocal := (), publishArtifact := false ) lazy val noSourceSettings = Seq( sources in Compile := Seq(), sources in Test := Seq() ) import java.io.File def crossVersionSharedSources() = Seq( (unmanagedSourceDirectories in Compile) ++= { (unmanagedSourceDirectories in Compile ).value.map { dir:File => new File(dir.getPath + "_" + scalaBinaryVersion.value)}} ) lazy val scalaMacroDependencies: Seq[Setting[_]] = Seq( libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value, libraryDependencies += "org.scala-lang" % "scala-compiler" % scalaVersion.value, libraryDependencies ++= { CrossVersion.partialVersion(scalaVersion.value) match { // if scala 2.11+ is used, quasiquotes are merged into scala-reflect case Some((2, scalaMajor)) if scalaMajor >= 11 => Seq() // in Scala 2.10, quasiquotes are provided by macro paradise case Some((2, 10)) => Seq( compilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full), "org.scalamacros" %% "quasiquotes" % "2.1.0" cross CrossVersion.binary ) } } ) addCommandAlias("gitSnapshots", ";set version in ThisBuild := git.gitDescribedVersion.value.get + \"-SNAPSHOT\"") // For Travis CI - see http://www.cakesolutions.net/teamblogs/publishing-artefacts-to-oss-sonatype-nexus-using-sbt-and-travis-ci credentials ++= (for { username <- Option(System.getenv().get("SONATYPE_USERNAME")) password <- Option(System.getenv().get("SONATYPE_PASSWORD")) } yield Credentials("Sonatype Nexus Repository Manager", "oss.sonatype.org", username, password)).toSeq ================================================ FILE: cli/shared/src/main/scala/rapture/cli/cli.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.cli import rapture.io._ import rapture.codec._ import rapture.fs._ import rapture.core._ import rapture.uri._ import rapture.log._ import encodings.system._ import logLevels.trace._ import language.{higherKinds, implicitConversions} import language.experimental.macros import scala.concurrent._ import scala.concurrent.ExecutionContext.Implicits.global object DebugModeConfig { implicit val defaultDebugMode: DebugModeConfig = DebugModeConfig(false) } object debugMode { def apply(): DebugModeConfig = implicitDebugMode implicit val implicitDebugMode: DebugModeConfig = DebugModeConfig(true) } case class DebugModeConfig(on: Boolean) object ShParam { implicit def stringableToShParam[T: StringSerializer](t: T): ShParam = ShParam(Vector(?[StringSerializer[T]].serialize(t))) implicit def genSeqSerializer[T: StringSerializer, Coll[E] <: TraversableOnce[E]](ts: Coll[T]): ShParam = ShParam(ts.map(?[StringSerializer[T]].serialize(_)).to[Vector]) implicit def processToShParam(process: Process): ShParam = ShParam(process.params) implicit def fsUrlToShParam(fsUrl: FsUrl): ShParam = ShParam(Vector(fsUrl.elements.mkString("/", "/", ""))) } case class ShParam(elems: Vector[String]) { def asString = elems.mkString(" ") } object `package` { implicit class ProcessStringContext(sc: StringContext) { def sh(content: ShParam*): Process = macro CliMacros.shImplementation } object cliLogging { import rapture.log.parts._ implicit val logger = Logger(uri"file:///tmp/rapture-cli/access.log") implicit def implicitSpec(implicit severity: Severity, date: Date, time: Time, thread: Thread): Spec = log"""$date $time $severity ${sourceFile(width = 12, Right)}:${lineNo(4)} ${thread(14)}""" } } sealed class CliException(msg: String) extends Exception(msg) case class ParamGetException(name: String) extends CliException(s"Missing parameter $name") abstract class BackgroundCliApp(implicit debugMode: DebugModeConfig) extends CliApp with Completions[Zsh with Bash] { val shellCompleter = new Bash with Zsh {} private var lastExitStatus = 0 override def doExit(code: Int) = lastExitStatus = code def shutdown(): Unit = () override def main(args: Array[String]) = { import cliLogging._ val appName = args(0) val fifo = File.parse(s"file:///tmp/rapture-cli/${appName}.sock") var continue = true var invocation = 0 while (continue) { val msg = fifo.slurp[Char].trim msg.split(",").to[List].map(_.urlDecode) match { case "shutdown" :: Nil => log.info("Received shutdown command") shutdown() fifo.delete() sys.exit(0) case "sigint" :: file :: Nil => log.info("Received SIGINT for file " + file) case "winch" :: file :: lines :: cols :: Nil => log.info(s"Received SIGWINCH for file $file $lines x $cols") case "exec" :: file :: pwd :: rest => log.info(s"Using pwd = $pwd") val ps = new java.io.PrintStream(new java.io.FileOutputStream(new java.io.File(file))) invocation += 1 Future { try { System.setOut(ps) try super.run(File.parse(s"file://$pwd"), rest.to[Array]) catch { case e: Throwable => if (debugMode.on) e.printStackTrace() } } catch { case e: Throwable => if (debugMode.on) e.printStackTrace() } finally ps.close() val ps2 = new java.io.PrintStream(new java.io.FileOutputStream(new java.io.File(s"$file.exit"))) try { ps2.println(lastExitStatus.toString) ps2.flush() ps2.close() System.setOut(sysOut) } finally ps2.close() } case _ => } } } } case class ReturnEarly() extends Exception() abstract class CliApp(implicit debugMode: DebugModeConfig) { private val NoOutput = new java.io.PrintStream(new java.io.OutputStream() { def write(x: Int) = () }) def exit(code: Int): Unit = throw Exit(code) def exec(block: => Unit): Exec = Exec((out: java.io.PrintStream) => block) def exec(block: java.io.PrintStream => Unit): Exec = Exec(block) val sysOut = System.out def doExit(code: Int): Unit = sys.exit(code) def main(args: Array[String]): Unit = run(File.parse(s"file://${System.getenv("PWD")}"), args) def run(pwd: FsUrl, args: Array[String]): Unit = { val exitStatus: Exit = try { Console.withOut(NoOutput) { try { val cmdLine: CmdLine = makeCmdLine(pwd, args.to[Vector]) val execution = handle(cmdLine) if (cmdLine.completer.isEmpty) { execution.exec(System.out) Exit(0) } else Exit(0) } catch { case ReturnEarly() => Exit(0) case err: Throwable => Console.withOut(sysOut) { println("Unexpected error") if (debugMode.on) err.printStackTrace() } throw Exit(1) } } } catch { case err @ Exit(_) => err } doExit(exitStatus.code) } def makeCmdLine(pwd: FsUrl, args: Vector[String]) = CmdLine(pwd, args map { s => Arg(s, None, false) }, None) def handle(cmdLine: CmdLine): Exec } trait Shell { def makeCmdLine(pwd: FsUrl, cmdLine: Vector[String]): CmdLine = CmdLine(pwd, cmdLine.map(Arg(_, None, false)), None) } trait Zsh extends Shell { override def makeCmdLine(pwd: FsUrl, cmdLine: Vector[String]): CmdLine = cmdLine match { case "---rapture-zsh" +: prefix +: cursor +: cols +: "--" +: rest => val colWidth = cols.substring(10).toInt val cur = cursor.substring(9).toInt val words = if (cur >= rest.length) rest.tail :+ "" else rest.tail val completer = Completer(prefix.substring(9).urlDecode, zshCompleter(_, colWidth)) CmdLine(pwd, words.zipWithIndex map { case (s, idx) => Arg(s, Some(completer), cur - 2 == idx) }, Some(completer)) case _ => super.makeCmdLine(pwd, cmdLine) } def zshCompleter(suggestions: Suggestions, colWidth: Int): Nothing = { suggestions.groups.foreach { g => val cmds = Compadd(g.title, g.suggestions.keys.to[Vector], true, v => g.suggestions(v), colWidth, g.hidden) cmds foreach System.out.println } throw ReturnEarly() } } trait Bash extends Shell { override def makeCmdLine(pwd: FsUrl, cmdLine: Vector[String]): CmdLine = cmdLine match { case "---rapture-bash" +: prefix +: cursor +: cols +: "--" +: rest => val colWidth = cols.substring(10).toInt val words = if (cursor.toInt - 1 >= rest.length) rest.tail :+ "" else rest.tail val completer = new Completer(prefix.urlDecode, bashCompleter) CmdLine(pwd, words.zipWithIndex map { case (s, idx) => Arg(s, Some(completer), cursor.toInt - 2 == idx) }, Some(completer)) case _ => super.makeCmdLine(pwd, cmdLine) } def bashCompleter(suggestions: Suggestions): Nothing = { System.out.println("Using bash") throw ReturnEarly() } } case class Exit(code: Int) extends Exception case class Exec(exec: java.io.PrintStream => Unit) ================================================ FILE: cli/shared/src/main/scala/rapture/cli/glob.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.cli import java.util.regex._ object globInterpreters { /* This in an incomplete implementation as it does not support character classes enclosed * by `[` and `]`. */ object unix { def apply(): GlobInterpreter = implicitGlobInterpreter implicit val implicitGlobInterpreter: GlobInterpreter = new GlobInterpreter { def interpret(glob: String): Pattern = { val sb = new StringBuilder var start = true glob foreach { c => start = false sb.append(c match { case '*' => if (start) "[^./][^/]*" else "[^/]*" case '?' => if (start) "[^./][^/]*" else "[^/]*" case '/' => start = true; "/" case esc @ ('.' | '[' | '{' | '(' | '+' | '^' | '$' | '|') => "\\" + esc case other => other.toString }) } Pattern.compile(sb.toString) } } } } trait GlobInterpreter { def interpret(glob: String): Pattern } case class Glob(globString: String)(implicit globInterpreter: GlobInterpreter) { lazy val pattern: Pattern = globInterpreter.interpret(globString) def matches(s: String) = pattern.matcher(s).matches } ================================================ FILE: cli/shared/src/main/scala/rapture/cli/macros.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.cli import rapture.base._ import rapture.core._ private[cli] object CliMacros { def shImplementation(c: BlackboxContext)(content: c.Expr[ShParam]*): c.Expr[Process] = { import c.universe._ val params = c.prefix.tree match { case Apply(_, List(Apply(_, rawParts))) => val parts = rawParts.to[Vector].zip(content.map(_.tree).to[Vector]).flatMap { case (x, y) => Vector(x, y) } :+ rawParts.last var params: Vector[c.Tree] = Vector() var param: Vector[Either[String, c.Tree]] = Vector() var inline: Boolean = false var singleQuoted: Boolean = false var doubleQuoted: Boolean = false var escaped: Boolean = false def add(chr: Char) = { param = if (param.isEmpty) Vector(Left(chr.toString)) else param.last match { case Right(_) => param :+ Left(chr.toString) case Left(str) => param.init :+ Left(str + chr) } escaped = false } def nextParam() = if (!param.isEmpty) { val next: c.Tree = if (inline) { val strings = param.map { case Left(str) => q"$str" case Right(tr) => q"""$tr.elems.mkString(" ")""" } q"_root_.scala.Vector(_root_.scala.Vector(_root_.scala.Vector(..$strings).mkString))" } else { val values = param.map { case Left(str) => q"_root_.scala.Vector($str)" case Right(tr) => q"$tr.elems" } q"_root_.scala.Vector(..$values)" } params = params :+ next param = Vector() inline = false } parts.foreach { case Literal(Constant(str: String)) => str.foreach { case chr if escaped => add(chr) case ' ' => if (singleQuoted || doubleQuoted) add(' ') else nextParam() case '\\' => escaped = true case '\'' if !doubleQuoted => singleQuoted = !singleQuoted case '"' if !singleQuoted => doubleQuoted = !doubleQuoted case chr => add(chr) } case tr => inline = inline || singleQuoted || doubleQuoted param = param :+ Right(tr.asInstanceOf[c.Tree]) } nextParam() if (singleQuoted || doubleQuoted) c.abort(c.enclosingPosition, "unclosed quoted parameter") if (params.isEmpty) c.abort(c.enclosingPosition, "no command specified") q"_root_.scala.Vector(..$params).flatten.flatten" } c.Expr(q"""new _root_.rapture.cli.Process($params)""") } } ================================================ FILE: cli/shared/src/main/scala/rapture/cli/params.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.cli import rapture.fs._ import rapture.core._ import annotation.tailrec import scala.collection.immutable.ListMap import language.higherKinds object Optable { implicit val stringOptable = new Optable[String] { def name(t: String) = t def description(t: String) = Vector() def hidden(t: String): Boolean = false } implicit val OptOptable = new Optable[Opt] { def name(t: Opt) = t.name def description(t: Opt) = Vector(s"-- ${t.description}") def hidden(t: Opt): Boolean = t.hidden } implicit val stringPairOptable = new Optable[(String, String)] { def name(t: (String, String)) = t._1 def description(t: (String, String)) = Vector(t._2) def hidden(t: (String, String)): Boolean = t._2 == "" } } trait Optable[-T] { def name(t: T): String def description(t: T): Vector[String] def hidden(t: T): Boolean } case class Opt(name: String, description: String, hidden: Boolean = false)(opts: => Opts[Opt]) { def unapply(arg: Arg): Boolean = opts.unapply(arg) == Some(this) } case class Opts[T: Optable](options: T*) { private val optable = implicitly[Optable[T]] def unapply(arg: Arg): Option[T] = { val p = arg(suggester) options.to[List].find(optable.name(_) == p) } def suggester: Suggester = new Suggester { override def suggest(prefix: String): Suggestions = Suggestions( SuggestionGroup(None, options .to[Vector] .map { opt => (optable.name(opt), optable.description(opt)) } .filter(_._1 startsWith prefix) .toMap, false)) } } object NoSuggestions extends Suggester case class Param[+T: ParamParser](longName: String = null, shortName: String = null, description: String = null, suggester: Suggester = NoSuggestions, repeatable: Boolean = false) { def parse(s: List[String]): Option[T] = implicitly[ParamParser[T]].parse(s) } trait ParamParser_1 { implicit val intParamParser: ParamParser[Int] = new ParamParser[Int] { def parse(s: List[String]) = s.headOption.map(_.toInt) } implicit val booleanParamParser: ParamParser[Boolean] = new ParamParser[Boolean] { def parse(s: List[String]): Option[Boolean] = Some(true) } } object ParamParser extends ParamParser_1 { implicit val defaultParamParser: ParamParser[String] = new ParamParser[String] { def parse(s: List[String]) = s.headOption } def of[T: ParamParser]: ParamParser[T] = implicitly[ParamParser[T]] } trait ParamParser[+T] { paramParser => def parse(s: List[String]): Option[T] def map[S](fn: T => S): ParamParser[S] = new ParamParser[S] { def parse(s: List[String]): Option[S] = paramParser.parse(s).map(fn) } } object Paramable { implicit def paramParamable[T]: Paramable[T, Param] = new Paramable[T, Param] { def longName(p: Param[T]): String = Option(p.longName).map("--" + _).getOrElse("") def shortName(p: Param[T]): String = Option(p.shortName).map("-" + _).getOrElse("") def description(p: Param[T]): Vector[String] = Option(p.description).to[Vector] def hidden(p: Param[T]): Boolean = false def suggester(p: Param[T]): Suggester = p.suggester def repeatable(p: Param[T]): Boolean = p.repeatable def parse(p: Param[T], s: List[String]): Option[T] = p.parse(s) } } trait Paramable[T, P[_]] { def longName(t: P[T]): String def shortName(t: P[T]): String def description(t: P[T]): Vector[String] def hidden(t: P[T]): Boolean def suggester(t: P[T]): Suggester def repeatable(t: P[T]): Boolean def parse(p: P[T], s: List[String]): Option[T] } case class Params[T, P[_]](options: P[T]*)(implicit paramable: Paramable[T, P]) { def suggester(exclusions: Set[String]): Suggester = new Suggester { override def suggest(prefix: String): Suggestions = Suggestions( SuggestionGroup(None, options.filterNot { opt => exclusions.contains(paramable.longName(opt).drop(2)) || exclusions.contains(paramable.shortName(opt).drop(1)) }.map { opt => (paramable.longName(opt), paramable.shortName(opt) +: paramable.description(opt)) }.filter(_._1 startsWith prefix).toMap, false), SuggestionGroup(None, options.filterNot { opt => exclusions.contains(paramable.longName(opt).drop(2)) || exclusions.contains(paramable.shortName(opt).drop(1)) }.map { opt => paramable.shortName(opt) -> Vector() }.filter(_._1 startsWith prefix).toMap, true) ) } private object LongOpt { def unapply(arg: Arg): Option[String] = if (arg.param.startsWith("--")) Some(arg(suggester(Set())).drop(2)) else None } private object ShortOpt { def unapply(arg: Arg): Option[String] = if (arg.param.startsWith("-")) Some(arg(suggester(Set())).slice(1, 2)) else None } @tailrec private def organize(xs: Seq[Arg], acc: ListMap[String, List[Arg]] = ListMap()): Map[String, List[Arg]] = xs match { case Seq() => acc case LongOpt(dashParam) +: tail => organize(tail, acc + (dashParam -> Nil)) case (arg @ ShortOpt(dashParam)) +: tail => if (arg.param.length == 2) organize(tail, acc + (dashParam -> Nil)) else organize(arg.copy(param = "-" + arg.param.drop(2)) +: tail, acc + (dashParam.take(2) -> Nil)) case v +: tail => acc.lastOption match { case Some((key, entry)) => organize(tail, acc + (key -> (entry :+ v))) case None => val exclusions = acc.keys.to[Set] v(suggester(exclusions)) organize(tail, acc) } } def unapply(cmdLine: CmdLine): Option[ParamMap] = Some(ParamMap(organize(cmdLine.params), cmdLine.completer)) } case class ParamMap(params: Map[String, List[Arg]], completer: Option[Completer]) { def get[T, P[_]](param: P[T], suggesters: Suggester*)(implicit paramable: Paramable[T, P]): Option[T] = { params.find { case (k, v) => k == implicitly[Paramable[T, P]] .longName(param) .substring(2) || k == implicitly[Paramable[T, P]].shortName(param).substring(1) }.flatMap { case (_, args) => val values = suggesters padTo (args.length, NoSuggestions) zip args map { case (suggester, arg) => arg(suggester) } paramable.parse(param, values.to[List]) } } def apply[T, P[_]](param: P[T], suggesters: Suggester*)(implicit paramable: Paramable[T, P]): T = get(param, suggesters: _*).getOrElse { if (completer.isDefined) null.asInstanceOf[T] else throw ParamGetException(paramable.longName(param)) } } object -- { def unapply(cmdLine: CmdLine): Option[(CmdLine, CmdLine)] = { val left = cmdLine.params.takeWhile(_.param != "--") Some( if (cmdLine.params.length == left.length) (cmdLine, CmdLine(cmdLine.pwd, Vector(), cmdLine.completer)) else ( CmdLine(cmdLine.pwd, left, cmdLine.completer), CmdLine(cmdLine.pwd, cmdLine.params.drop(left.length + 1), cmdLine.completer) ) ) } } case class Arg(param: String, completer: Option[Completer], current: Boolean) { def apply(suggester: Suggester) = completer match { case Some(c) if current => c.process(suggester.suggest(c.prefix)) case _ => param } } case class CmdLine(pwd: FsUrl, params: Vector[Arg], completer: Option[Completer]) extends collection.SeqLike[Arg, CmdLine] { def seq = params def apply(idx: Int) = params(idx) def length = params.length def iterator: Iterator[Arg] = params.iterator def newBuilder: collection.mutable.Builder[Arg, CmdLine] = new collection.mutable.Builder[Arg, CmdLine] { var vect: Vector[Arg] = Vector() def clear(): Unit = vect = Vector() def result(): CmdLine = CmdLine(pwd, vect, completer) def +=(elem: Arg) = { vect = vect :+ elem this } } override def toString = params.map { case Arg(p, _, false) => p case Arg(p, _, true) => completer.get.prefix + "^" + p.drop(completer.get.prefix.length + 1) }.mkString(" ") } class Suggester { def suggest(prefix: String): Suggestions = Suggestions() } object Suggestions { def from[T](it: Iterable[T])(fn: T => String, fns: (T => Any)*) = new Suggester { override def suggest(prefix: String) = Suggestions(SuggestionGroup(None, it.map { e => fn(e) -> ((if (fns.isEmpty) "" else "--") +: fns.to[Vector].map(_ (e).toString)) }.filter(_._1.startsWith(prefix)).toMap, false)) } /*def from[T](fromPrefix: String => Iterable[T])(fn: T => String, fns: (T => Any)*) = new Suggester { override def suggest(prefix: String) = Suggestions(SuggestionGroup(None, fromPrefix(prefix).map { e => fn(e) -> ((if(fns.isEmpty) "" else "--") +: fns.to[Vector].map(_(e).toString)) }.toMap, false)) }*/ } case class Suggestions(groups: SuggestionGroup*) case class SuggestionGroup(title: Option[String], suggestions: Map[String, Vector[String]], hidden: Boolean) case class Completer(prefix: String, shellCompleter: Suggestions => Nothing) { def process(suggestions: Suggestions): Nothing = shellCompleter(suggestions) } trait Completions[ShellTypes <: Shell] { this: CliApp => override def makeCmdLine(pwd: FsUrl, args: Vector[String]): CmdLine = shellCompleter().makeCmdLine(pwd, args) def shellCompleter(): ShellTypes } ================================================ FILE: cli/shared/src/main/scala/rapture/cli/params2.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.cli import scala.util.Try import scala.annotation.tailrec import rapture.core._ object New { case class ParamUsage(map: ParamMap, used: Set[String]) { def -(key: String): ParamUsage = copy(used = used + key) def --(keys: Set[String]): ParamUsage = copy(used = used ++ keys) def unexpected = map.groups.filterNot { p => used contains p.key() } } case class ParamMap(args: String*) { def ++(pm2: ParamMap) = ParamMap(pm2.args ++ args: _*) case class Part(no: Int, start: Int, end: Int) { def apply() = args(no).substring(start, end) } case class Parameter(key: Part, values: Vector[Part] = Vector()) { override def toString = { val prefix = if (key().length == 1) "-" else "--" s"$prefix${key()} ${values.map(_ ()) mkString " "}" } } val groups: Set[Parameter] = parseArgs().to[Set] @tailrec private def parseArgs(gs: List[Parameter] = Nil, n: Int = 0, off: Int = 0): List[Parameter] = { if (n == args.length) gs else if (args(n) startsWith "--") { val idx = args(n).indexOf('=') if (idx < off) parseArgs(Parameter(Part(n, 2, args(n).length)) :: gs, n + 1) else parseArgs(Parameter(Part(n, 2, idx)) :: gs, n, idx + 1) } else if (args(n) startsWith "-") { if (off == 0) parseArgs(gs, n, 1) else if (args(n).length == off + 1) parseArgs(Parameter(Part(n, off, off + 1)) :: gs, n + 1) else parseArgs(Parameter(Part(n, off, off + 1)) :: gs, n, off + 1) } else { if (gs.isEmpty) parseArgs(gs, n + 1) else parseArgs(gs.head.copy(values = gs.head.values :+ Part(n, 0, args(n).length)) :: gs.tail, n + 1) } } def find(key: String): Option[Parameter] = groups.find(_.key() == key) def apply(names: Vector[String]): Option[Parameter] = names match { case Vector() => None case h +: t => find(h) orElse apply(t) } def isEmpty = args.isEmpty override def toString = groups.mkString } trait `Params.parse` extends MethodConstraint sealed class ParamException(msg: String) extends Exception(msg) case class MissingParam(name: String) extends ParamException(s"the parameter --$name was missing") case class InvalidValue(value: String, name: String) extends ParamException(s"the value '$value' is not valid for the parameter --$name") case class UnexpectedParam(param: String) extends ParamException(s"found unexpected parameter '$param'") @implicitNotFound("Can not combine elements of type ${A} and ${B}") trait Construct[-A <: Params, -B <: Params] { construct => type And <: Params type Or <: Params def and(a: A, b: B): ProductParams[And] def or(a: A, b: B): CoproductParams[Or] def swap: Construct[B, A] { type And = construct.And; type Or = construct.Or } = new Construct[B, A] { type And = construct.And type Or = construct.Or def and(a: B, b: A): ProductParams[And] = construct.and(b, a) def or(a: B, b: A): CoproductParams[Or] = construct.or(b, a) } } trait Construct_1 { implicit def general[A <: Params, B <: Params]: Construct[A, B] { type And = A with B; type Or = A with B } = { new Construct[A, B] { type And = A with B type Or = A with B def and(a: A, b: B) = ProductParams[A with B](Set(a, b)) def or(a: A, b: B) = CoproductParams[A with B](Vector(a, b)) } } } object Construct extends Construct_1 { implicit def leftProduct[A <: Params, B <: SimpleParam[_]]: Construct[ProductParams[A], B] { type And = A with B; type Or = ProductParams[A] with B } = { new Construct[ProductParams[A], B] { type And = A with B type Or = ProductParams[A] with B def and(a: ProductParams[A], b: B) = ProductParams[A with B](a.elements + b) def or(a: ProductParams[A], b: B) = CoproductParams[ProductParams[A] with B](Vector(a, b)) } } implicit def rightProduct[A <: SimpleParam[_], B <: Params]: Construct[A, ProductParams[B]] { type And = B with A; type Or = ProductParams[B] with A } = leftProduct[B, A].swap implicit def leftCoproduct[A <: Params, B <: SimpleParam[_]] : Construct[CoproductParams[A], B] { type And = CoproductParams[A] with B; type Or = A with B } = { new Construct[CoproductParams[A], B] { type And = CoproductParams[A] with B type Or = A with B def and(a: CoproductParams[A], b: B) = ProductParams[CoproductParams[A] with B](Set(a, b)) def or(a: CoproductParams[A], b: B) = CoproductParams[A with B](a.elements :+ b) } } implicit def rightCoproduct[A <: SimpleParam[_], B <: Params] : Construct[A, CoproductParams[B]] { type And = CoproductParams[B] with A; type Or = B with A } = leftCoproduct[B, A].swap } case class Suggestions(output: Option[Seq[Vector[String]]]) { def orElse(ss: Suggestions) = Suggestions(output orElse ss.output) } object SuggestionOutput { implicit val defaultOutput: SuggestionOutput = new SuggestionOutput { def output(ss: Suggestions) = () } } trait SuggestionOutput { def output(ss: Suggestions): Unit } trait Params { params => type Result def parse(args: ParamMap, tabArg: Int = -1)(implicit suggestOutput: SuggestionOutput, mode: Mode[`Params.parse`]): mode.Wrap[Result, ParamException] = mode.wrap { val (result, lastArgs, ss) = check(ParamUsage(args, Set()), mode, tabArg, Suggestions(None)) suggestOutput.output(ss) lastArgs.unexpected foreach { p => mode.exception(UnexpectedParam(p.key())) } result } def check(args: ParamUsage, mode: Mode[_], tabArg: Int, ss: Suggestions): (Result, ParamUsage, Suggestions) def &[B <: Params](b: B)(implicit con: Construct[params.type, b.type]): ProductParams[con.And] = { con.and(this, b) } def |[B <: Params](b: B)(implicit con: Construct[params.type, b.type]): CoproductParams[con.Or] = { con.or(this, b) } def unary_~ : OptionParams[this.type] = OptionParams(this) def by[R](fn: Result => R): Param.Handler[this.type, R] = new Param.Handler[this.type, R](this) { type From = Result def handle(v: From): R = fn(v) } } case class OptionParams[Ps <: Params](params: Ps) extends Params { type Result = Option[params.Result] def check(args: ParamUsage, mode: Mode[_], tabArg: Int, ss: Suggestions): (Result, ParamUsage, Suggestions) = try { val (res, newArgs, newSs) = params.check(args, mode, tabArg, ss) (Some(res), newArgs, newSs) } catch { case e: Exception => (None, args, ss) } override def toString = s"[$params]" } case class ProductParams[Ps <: Params](elements: Set[Params]) extends Params { type ProductTypes = Ps type Result = Product[ProductTypes] def check(args: ParamUsage, mode: Mode[_], tabArg: Int, ss: Suggestions): (Result, ParamUsage, Suggestions) = { val (finalArgs, finalElems, newSs) = elements.foldLeft((args, Set[(Params, Any)](), ss)) { case ((args, es, ss), key) => val (res, nextArgs, newSs) = key.check(args, mode, tabArg, ss) (nextArgs, es + (key -> res), ss orElse newSs) } (new Product[Ps](finalElems.toMap), finalArgs, newSs) } override def toString = elements.mkString("( ", " & ", " )") } case class CoproductParams[Ps <: Params](elements: Vector[Params]) extends Params { type CoproductTypes = Ps type Result = Coproduct[CoproductTypes] def check(args: ParamUsage, mode: Mode[_], tabArg: Int, ss: Suggestions): (Result, ParamUsage, Suggestions) = { val elems = elements.to[List].flatMap { k => Try(Option(k.check(args, mode, tabArg, ss)).get).toOption.map(k -> _) } elems match { case (key, (res, args, newSs)) :: Nil => (Coproduct[CoproductTypes](key -> res), args, newSs) case Nil => mode.exception(MissingParam(toString)) case _ :: (key, _) :: _ => mode.exception(UnexpectedParam(key.toString)) } } override def toString = elements.mkString("( ", " | ", " )") } object ToSuggestion { implicit val stringSuggestion: ToSuggestion[String] = new ToSuggestion[String] { def suggestion(value: String): Vector[String] = Vector(value) } } trait ToSuggestion[T] { def suggestion(value: T): Vector[String] } case class SimpleParam[T: Param.Extractor](keys: Vector[String]) extends Params { simpleParam => type Result = T def toSuggestion(t: T): Vector[String] = Vector() def suggestions(s: String): Seq[T] = Seq() def checkValue: Option[T] = None def suggest(suggestions: T*)(implicit sug: ToSuggestion[T]): SimpleParam[T] = suggest { s => suggestions } def suggest(suggestions: String => Seq[T])(implicit sug: ToSuggestion[T]): SimpleParam[T] = { val ss = suggestions new SimpleParam[T](keys) { override def toSuggestion(value: T): Vector[String] = sug.suggestion(value) override def suggestions(s: String) = ss(s) override def checkValue = simpleParam.checkValue } } def filter(fn: T => Boolean): SimpleParam[T] = new SimpleParam[T](keys) { override def toSuggestion(value: T): Vector[String] = simpleParam.toSuggestion(value) override def suggestions(str: String) = simpleParam.suggestions(str).filter(fn) override def checkValue = simpleParam.checkValue } protected val extractor = ?[Param.Extractor[T]] def check(args: ParamUsage, mode: Mode[_], tabArg: Int, ss: Suggestions): (Result, ParamUsage, Suggestions) = { val parameter = args.map(keys) getOrElse mode.exception(MissingParam(keys.head)) val res = extractor.extract(parameter.values.map(_ ())) getOrElse { mode.exception(InvalidValue(parameter.key(), parameter.values.mkString(" "))) } checkValue.foreach { v => if (v != res) mode.exception(InvalidValue(keys.head, "invalid")) } val newSs = Suggestions(parameter.values.find(tabArg == _.no).map { p => suggestions(p()).map(toSuggestion) }) (res, args -- keys.to[Set], ss orElse newSs) } // Also consider `extractor` in `hashCode` and `equals` override def hashCode = keys.hashCode ^ extractor.hashCode override def equals(that: Any) = that match { case that: SimpleParam[_] => keys.to[Set] == that.keys.to[Set] && that.extractor == extractor case _ => false } override def toString = keys.map { k => if (k.length == 1) s"-$k" else s"--$k" }.mkString("/") def of(v: T): SimpleParam[T] = new SimpleParam[T](keys) { override def toSuggestion(value: T): Vector[String] = simpleParam.toSuggestion(value) override def suggestions(str: String) = simpleParam.suggestions(str) override def checkValue = Some(v) } } object Param { object Extractor { implicit val stringExtractor: Extractor[String] = new Extractor[String] { // FIXME: Add mode parameter, and capture failure types def extract(values: Vector[String]): Option[String] = Some(values.mkString(" ")) } implicit val intExtractor: Extractor[Int] = new Extractor[Int] { def extract(values: Vector[String]): Option[Int] = values match { case Vector(v) => try Some(v.toInt) catch { case e: Exception => None } case _ => None } } implicit val unitExtractor: Extractor[Unit] = new Extractor[Unit] { def extract(values: Vector[String]): Option[Unit] = Some(()) } } trait Extractor[T] { def extract(values: Vector[String]): Option[T] } abstract class Handler[-K, +H](val params: Params) { type From def handle(v: From): H } def apply[T: Extractor](shortName: Char, longName: Symbol): SimpleParam[T] = alloc(Vector(shortName.toString, longName.name)) def apply[T: Extractor](shortName: Char): SimpleParam[T] = alloc(Vector(shortName.toString)) def apply[T: Extractor](longName: Symbol): SimpleParam[T] = alloc(Vector(longName.name)) def flag(shortName: Char, longName: Symbol): SimpleParam[Unit] = alloc(Vector(shortName.toString, longName.name)) def flag(shortName: Char): SimpleParam[Unit] = alloc(Vector(shortName.toString)) def flag(longName: Symbol): SimpleParam[Unit] = alloc(Vector(longName.name)) } @implicitNotFound("product does not contain this value") trait ProductContainsParam[V, T] object ProductContainsParam extends ProductContainsParam_1 { implicit def optional[V <: OptionParams[_ <: Params], P <: Params]: ProductContainsParam[V, P] = null } trait ProductContainsParam_1 { implicit def generic[V, T <: V]: ProductContainsParam[V, T] = null } @implicitNotFound("coproduct cannot contain this value") trait CoproductContainsParam[V, T] object CoproductContainsParam { implicit def acceptable[V, T <: V]: CoproductContainsParam[V, T] = null } case class Product[T <: Params](tmap: Map[Params, Any]) { def apply[V <: Params](value: V)(implicit acc: ProductContainsParam[value.type, T]): value.Result = tmap(value).asInstanceOf[value.Result] override def toString = tmap.map { case (k, v) => s"$k: $v" }.mkString(", ") } case class Coproduct[T <: Params](value: (Params, Any)) { def handle[K, R](handlers: Param.Handler[K, R]*)(implicit ev: K <:< T): R = { val h = handlers.find(_.params == value._1).get h.handle(value._2.asInstanceOf[h.From]) } override def toString = s"${value._1}: ${value._2}" } } ================================================ FILE: cli/shared/src/main/scala/rapture/cli/process.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.cli import rapture.io._ import rapture.core._ import rapture.codec._ import java.io.{Reader => _, _} import encodings.system._ import language.higherKinds trait `Process#exec` extends MethodConstraint case class Process(params: Vector[String]) { def exec[T: ProcessInterpreter](implicit mode: Mode[`Process#exec`], env: Environment): mode.Wrap[T, CliException] = mode.wrap { val javaProcess = Runtime .getRuntime() .exec(params.to[Array], env().map { case (k, v) => s"$k=$v" } .to[Array], new File(env.workDir.getOrElse(System.getenv("HOME")))) val stream = new ByteInput(new BufferedInputStream(javaProcess.getInputStream)) val stderr = new ByteInput(new BufferedInputStream(javaProcess.getErrorStream)) ?[ProcessInterpreter[T]].interpret(stream, stderr, () => javaProcess.waitFor()) } override def toString = { val escaped = params.map(_.flatMap { case '\'' => "\\'" case '"' => "\\\"" case '\\' => "\\\\" case ' ' => "\\ " case chr => chr.toString }) s"""sh"${escaped.mkString(" ")}"""" } } object Environment { implicit val defaultEnvironment = new Environment { def apply(): Map[String, String] = { import scala.collection.JavaConverters._ mapAsScalaMapConverter(System.getenv()).asScala.toMap } def workDir: Option[String] = Option(System.getProperty("user.dir")) } } trait Environment { def apply(): Map[String, String] def workDir: Option[String] } package environments { object empty { def apply(): Environment = implicitEnvironment implicit val implicitEnvironment: Environment = new Environment { def apply(): Map[String, String] = Map() def workDir = None } } object enclosing { def apply(): Environment = implicitEnvironment implicit val implicitEnvironment: Environment = Environment.defaultEnvironment } } trait ProcessInterpreter_1 { implicit def genSeqInterpreter[Coll[_], T](implicit cbf: collection.generic.CanBuildFrom[Nothing, T, Coll[T]], stringParser: StringParser[T]): ProcessInterpreter[Coll[T]] = new ProcessInterpreter[Coll[T]] { def interpret(input: Input[Byte], stderr: Input[Byte], exitStatus: () => Int): Coll[T] = { val out = input.slurp[Char] exitStatus() match { case 0 => val builder = cbf() // FIXME: Reimplement this using a streaming method out.split("\n").foreach { s => builder += stringParser.parse(s, modes.throwExceptions()) } builder.result() case n => throw ShellProcessException(n, out.trim) } } } } object ProcessInterpreter extends ProcessInterpreter_1 { implicit def stringProcessInterpreter[T](implicit stringParser: StringParser[T]): ProcessInterpreter[T] = new ProcessInterpreter[T] { def interpret(input: Input[Byte], stderr: Input[Byte], exitStatus: () => Int): T = { val out = input.slurp[Char] val err = stderr.slurp[Char] exitStatus() match { case n => stringParser.parse(if (out == "" || out.last != '\n') out else out.init, modes.throwExceptions()) //case n => throw ShellProcessException(n, out.trim) } } } implicit val bytesProcessInterpreter: ProcessInterpreter[Bytes] = new ProcessInterpreter[Bytes] { def interpret(input: Input[Byte], stderr: Input[Byte], exitStatus: () => Int): Bytes = { val out = input.slurp[Byte] exitStatus() match { case 0 => out case n => throw ShellProcessException(n, "Binary data") } } } implicit val byteInputProcessInterpreter: ProcessInterpreter[Input[Byte]] = new ProcessInterpreter[Input[Byte]] { def interpret(input: Input[Byte], stderr: Input[Byte], exitStatus: () => Int): Input[Byte] = input } implicit def inputProcessInterpreter[T](implicit rdr: Reader[Input[Byte], T]): ProcessInterpreter[Input[T]] = new ProcessInterpreter[Input[T]] { def interpret(input: Input[Byte], stderr: Input[Byte], exitStatus: () => Int): Input[T] = input.input[T] } implicit val exitStatusProcessInterpreter: ProcessInterpreter[ExitStatus] = new ProcessInterpreter[ExitStatus] { def interpret(input: Input[Byte], stderr: Input[Byte], exitStatus: () => Int): ExitStatus = ExitStatus(exitStatus()) } } trait ProcessInterpreter[T] { def interpret(input: Input[Byte], stderr: Input[Byte], exitStatus: () => Int): T } case class ShellProcessException(exitStatus: Int, output: String) extends Exception("Shell process returned non-zero exit status: " + exitStatus) case class ExitStatus(value: Int) ================================================ FILE: cli/shared/src/main/scala/rapture/cli/tabulate.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.cli import language.implicitConversions import language.higherKinds object Tabulation { sealed trait Position case object Right extends Position case object Left extends Position trait Col_1 { implicit def anyToCol(s: Any): Col = Col(s.toString, Left) } object Col extends Col_1 { implicit def intToCol(s: Int): Col = Col(s.toString, Right) implicit def stringToCol(s: String): Col = Col(s, Left) } case class Col(content: String, position: Position = Left) def tabulate[C[E] <: Seq[E], T](collection: Seq[T], titles: Option[Seq[Col]] = None)( cols: (T => Col)*): Seq[String] = { val contents = collection.map { e => cols.map(_ (e)) } val contentsWithTitles = titles.map { ts => ts +: ts.map { case Col(s, p) => Col(s.map(c => '-'), p) } +: contents }.getOrElse(contents) val widths = contentsWithTitles.map { _.map(_.content.length) }.reduce(_ zip _ map { case (a, b) => a max b }) contentsWithTitles.map(_.zip(widths).map { case (Col(s, Right), w) => " " * (w - s.length) + s + " " case (Col(s, Left), w) => s + (" " * (w - s.length)) + " " }.mkString) } } ================================================ FILE: cli/shared/src/main/scala/rapture/cli/zsh.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.cli import rapture.core._ object Encoder { private val Translations = "<>#{}|\\^~[]`/?:@=&$!*:; ".to[Vector] def encode(s: String): String = s flatMap { c => if (Translations contains c) { "%" + (c / 16 + c / 160 * 7 + 48).toChar + (c % 16 + c % 16 / 10 * 7 + 48).toChar } else c.toString } } object Compadd { def maxWidths(lists: Vector[Vector[String]]): Vector[Int] = lists.map(_.map(_.length)).reduce(_ zip _ map (Math.max _).tupled) def padRows(lists: Vector[Vector[String]]): Vector[String] = { val widths = maxWidths(lists) lists.map(_.zip(widths).map { case (s, w) => s.padTo(w, ' ') }.mkString(" ")) } def apply(groupTitle: Option[String] = None, completions: Vector[String] = Vector(), columns: Boolean = true, descriptions: String => Vector[String] = _ => Vector(), colWidth: Int = 1000, hidden: Boolean): Vector[String] = { val display: Option[Vector[String]] = { val ds: Vector[Vector[String]] = completions map descriptions if (ds.forall(_.isEmpty)) None else Some(padRows(completions zip ds map { case (c, d) => c +: d })) } display.toVector.flatten.map(s => "let " + Encoder.encode(s.take(colWidth - 1))) :+ Vector .strap( "compadd", groupTitle.to[Vector].flatMap(Seq("-J", _)), if (columns) Some("-l") else None, if (hidden) None else Some("-d matches"), if (hidden) Some("-n") else None, "--", completions.map(Encoder.encode) ) .mkString(" ") } } ================================================ FILE: cli/shared/src/test/scala/rapture/cli/tests.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.cli.test /*import rapture.core._ import rapture.cli._ import rapture.test._ import scala.util class TestRun extends Programme { include(CliTests) } object CliTests extends TestSuite { import New._ implicit object Captured extends SuggestionOutput { private var suggestions: Seq[Vector[String]] = Seq() def output(ss: Suggestions) = ss.output.foreach(suggestions = _) def last() = suggestions } val Alpha = Param[String]('a', 'alpha) val Beta = Param[Int]('b', 'beta) val Gamma = Param.flag('c', 'gamma) val Delta = Param[String]('d', 'delta) val alpha = ParamMap("-a", "alpha") val alphaIsBeta = ParamMap("-a", "beta") val alphaIsGamma = ParamMap("-a", "gamma") val beta = ParamMap("-b", "0") val gamma = ParamMap("-c") val delta = ParamMap("-d", "delta") val empty = ParamMap() val `Parse short flag` = test { ParamMap("-a").find("a").map(_.values.map(_())) } returns Some(Vector()) val `Parse short param with value` = test { ParamMap("-a", "alpha").find("a").map(_.values.map(_())) } returns Some(Vector("alpha")) val `Parse multiple short params` = test { ParamMap("-abc").find("b").map(_.values.map(_())) } returns Some(Vector()) val `Parse multiple short params with value` = test { ParamMap("-abc", "gamma").find("c").map(_.values.map(_())) } returns Some(Vector("gamma")) val `Parse long flag` = test { ParamMap("--alpha").find("alpha").map(_.values.map(_())) } returns Some(Vector()) val `Parse long param` = test { ParamMap("--alpha", "value").find("alpha").map(_.values.map(_())) } returns Some(Vector("value")) val `Parse long param (multiple args)` = test { ParamMap("--alpha", "one", "two", "three").find("alpha").map(_.values.map(_())) } returns Some(Vector("one", "two", "three")) val `Parse last of multiple long flags` = test { ParamMap("--alpha", "--beta", "--gamma", "--delta").find("delta").map(_.values.map(_())) } returns Some(Vector()) val `Parse first of multiple long flags` = test { ParamMap("--alpha", "--beta", "--gamma", "--delta").find("alpha").map(_.values.map(_())) } returns Some(Vector()) val `Parse first of multiple long params` = test { ParamMap("--alpha", "one", "--beta", "--gamma", "--delta").find("alpha").map(_.values.map(_())) } returns Some(Vector("one")) val `Parse last of multiple long params` = test { ParamMap("--alpha", "one", "--beta", "--gamma", "--delta", "a", "b", "c").find("delta").map(_.values.map(_())) } returns Some(Vector("a", "b", "c")) val `Extract simple param` = test { Alpha.parse(ParamMap("-a", "alpha")) } returns "alpha" val `Extract int` = test { Beta.parse(ParamMap("-b", "1")) } returns 1 val `Simple coproduct 1` = test { val parsed = (Alpha | Beta).parse(alpha) parsed.handle( Alpha by identity, Beta by { b => "beta" } ) } returns "alpha" val `Simple coproduct 2` = test { val parsed = (Alpha | Beta).parse(beta) parsed.handle( Alpha by identity, Beta by { b => "beta" } ) } returns "beta" val `Coproduct handler is total 1` = test { typeMismatch { val parsed = (Alpha | Beta).parse(beta) import deferTypeErrors._ parsed.handle( Alpha by identity ) } } returns true val `Coproduct handler is total 2` = test { typeMismatch { val parsed = (Alpha | Beta).parse(beta) import deferTypeErrors._ parsed.handle( Beta by { b => "beta" }, Alpha by identity ) } } returns false val `Can't access invalid field` = test { typeMismatch { val parsed = (Alpha & Beta).parse(beta) import deferTypeErrors._ parsed(Gamma) } } returns true //val `Refuse coproduct duplicates` = test { // import modes.returnOption._ // (Alpha | Beta).parse(alpha ++ beta) //} returns None val `Simple product` = test { val parsed = (Alpha & Beta).parse(alpha ++ beta) parsed(Alpha) -> parsed(Beta) } returns ("alpha", 0) //val `Missing product value fails 1` = test { // import modes.returnOption._ // (Alpha & Beta).parse(alpha) //} returns None //val `Missing product value fails 2` = test { // import modes.returnOption._ // (Alpha & Beta).parse(beta) //} returns None //val `Missing product value fails 3` = test { // import modes.returnOption._ // (Alpha & Beta).parse(empty) //} returns None val `Coproduct and product 1` = test { val parsed = (Alpha & Beta | Delta).parse(alpha ++ beta) parsed.handle( Alpha & Beta by { p => p(Alpha) -> p(Beta) }, Delta by { d => ("delta", -1) } ) } returns ("alpha", 0) val `Coproduct and product 2` = test { val parsed = (Alpha & Beta | Delta).parse(delta) parsed.handle( Alpha & Beta by { p => p(Alpha) -> p(Beta) }, Delta by { d => ("delta", -1) } ) } returns ("delta", -1) val `Coproduct and product failure 1` = test { import modes.returnOption._ val parsed = (Alpha & Beta | Delta).parse(alpha) } returns None val `Coproduct and product failure 2` = test { import modes.returnOption._ val parsed = (Alpha & Beta | Delta).parse(beta) } returns None //val `Coproduct and product failure 3` = test { // import modes.returnOption._ // (Alpha & Beta | Delta).parse(alpha ++ delta) //} returns None //val `Coproduct and product failure 4` = test { // import modes.returnOption._ // (Alpha & Beta | Delta).parse(beta ++ delta) //} returns None val `Optional value 1` = test { (~Alpha).parse(alpha) } returns Some("alpha") val `Optional value 2` = test { (~Alpha).parse(empty) } returns None val `Optional Product 1` = test { val parsed = (~Alpha & ~Beta).parse(empty) (parsed(~Alpha), parsed(~Beta)) } returns (None, None) val `Optional Product 2` = test { val parsed = (~Alpha & ~Beta).parse(alpha) (parsed(~Alpha), parsed(~Beta)) } returns (Some("alpha"), None) val `Optional Product 3` = test { val parsed = (~Alpha & ~Beta).parse(beta) (parsed(~Alpha), parsed(~Beta)) } returns (None, Some(0)) val `Optional Product 4` = test { val parsed = (~Alpha & ~Beta).parse(beta ++ alpha) (parsed(~Alpha), parsed(~Beta)) } returns (Some("alpha"), Some(0)) val `Complex extraction successes` = test { import modes.returnOption._ val pattern = (Alpha & ~Beta | Beta & (Gamma | Delta)) val successes = List(alpha, alpha ++ beta, beta ++ gamma, beta ++ delta) successes.map(pattern.parse(_)).forall(_.isDefined) } returns true val `Complex extraction failures` = test { import modes.returnOption._ val pattern = (Alpha & ~Beta | Beta & (Gamma | Delta)) val failures = List(beta, gamma, delta, beta ++ gamma ++ delta, alpha ++ gamma, alpha ++ delta) failures.map(pattern.parse(_)).forall(_ == None) } returns true val `Neither or both` = test { import modes.returnOption._ val pattern = ~(Alpha & Beta) val successes = List(alpha ++ beta, empty) val failures = List(alpha, beta, gamma) successes.map(pattern.parse(_)).forall(_.isDefined) && failures.map(pattern.parse(_)).forall(_ == None) } returns true val `Check param value` = test { Alpha.of("alpha").parse(alpha) } returns "alpha" val `Check param value 2` = test { val parsed = (Alpha.of("beta") | Alpha.of("alpha")).parse(alpha) parsed.handle( Alpha by identity ) } returns "alpha" val `Check param value 3` = test { import modes.returnOption._ (Alpha.of("beta") | Alpha.of("gamma")).parse(alpha) } returns None val `Check param values combined` = test { import modes.returnOption._ val pattern = Alpha.of("beta") & Beta | Alpha.of("gamma") & Gamma val successes = List(alphaIsGamma ++ gamma, alphaIsBeta ++ beta) val failures = List(alpha, beta, gamma, alphaIsGamma ++ beta, alphaIsBeta ++ gamma, alpha ++ beta, alpha ++ gamma) successes.map(pattern.parse(_)).forall(_.isDefined) && failures.map(pattern.parse(_)).forall(_ == None) } returns true val `Check suggestions` = test { val Color = Param[String]('c', 'color).suggest("red", "green", "blue") Color.parse(ParamMap("--color", ""), 1) Captured.last() } returns Vector(Vector("red"), Vector("green"), Vector("blue")) } */ ================================================ FILE: codec/shared/src/main/scala/rapture/codec/base64.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.codec import rapture.core._ /** RFC2045 base-64 codec, based on http://migbase64.sourceforge.net/. */ class Base64Codec[C <: CodecType](val char62: Char = '+', val char63: Char = '/', val padChar: Char = '=', val lineBreaks: Boolean = false, val endPadding: Boolean = false) extends ByteCodec[C] { private val alphabet = ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" + char62 + char63).toCharArray private lazy val decodabet = { val x: Array[Int] = alloc(256) for (i <- 0 until alphabet.length) x(alphabet(i)) = i x } /** Non-RFC-compliant encoder. */ /** Encoder. The RFC requires that line breaks be added every 76 chars, and * that the data be padded to a multiple of 4 chars, but we do these things * optionally. */ def encode(in: Array[Byte]): String = { var inLen = in.length if (inLen == 0) "" else { val evenLen = (inLen / 3) * 3 val outDataLen = if (endPadding) ((inLen - 1) / 3 + 1) << 2 else ((inLen << 2) + 2) / 3 val outLen = if (lineBreaks) outDataLen + (outDataLen - 1) / 76 << 1 else outDataLen val out: Array[Char] = alloc(outLen) var inPos = 0 var outPos = 0 var blockCount = 0 while (inPos < evenLen) { val block = (in(inPos) & 0xFF) << 16 | (in(inPos + 1) & 0xFF) << 8 | (in(inPos + 2) & 0xFF) inPos += 3 out(outPos) = alphabet((block >>> 18) & 0x3F) out(outPos + 1) = alphabet((block >>> 12) & 0x3F) out(outPos + 2) = alphabet((block >>> 6) & 0x3F) out(outPos + 3) = alphabet(block & 0x3F) outPos += 4 if (lineBreaks) { blockCount += 1 if (blockCount == 19 && outPos < outLen - 2) { out(outPos) = '\r' out(outPos + 1) = '\n' outPos += 2 blockCount = 0 } } } val left = inLen - evenLen if (left > 0) { val block = ((in(evenLen) & 0xFF) << 10) | (if (left == 2) (in(inLen - 1) & 0xFF) << 2 else 0) out(outPos) = alphabet(block >>> 12) out(outPos + 1) = alphabet((block >>> 6) & 0x3F) if (left == 2) out(outPos + 2) = alphabet(block & 0x3F) else if (endPadding) out(outPos + 2) = padChar if (endPadding) out(outPos + 3) = padChar } alloc(out) } } /** Decoder. Supports all the variants produced by the encoder above, but * does not tolerate any other illegal characters, including line breaks at * positions other than 76-char boundaries, in which case the result will * be garbage. */ def decode(data: String): Either[Int, Array[Byte]] = { val in = data.toCharArray() val inLen = in.length if (inLen == 0) Right(alloc(0)) else { val padding = if (in(inLen - 1) == padChar) (if (in(inLen - 2) == padChar) 2 else 1) else 0 // FIXME: This doesn't seem to accommodate different kinds of linebreak val lineBreaks = if (inLen > 76) (if (in(76) == '\r') inLen / 78 else 0) << 1 else 0 val outLen = ((inLen - lineBreaks) * 6 >> 3) - padding val out: Array[Byte] = alloc(outLen) var inPos = 0 var outPos = 0 var blockCount = 0 val evenLen = (outLen / 3) * 3 while (outPos < evenLen) { val block = decodabet(in(inPos)) << 18 | decodabet(in(inPos + 1)) << 12 | decodabet(in(inPos + 2)) << 6 | decodabet(in(inPos + 3)) inPos += 4 out(outPos) = (block >> 16).toByte out(outPos + 1) = (block >> 8).toByte out(outPos + 2) = block.toByte outPos += 3 if (lineBreaks > 0) { blockCount += 1 if (blockCount == 19) { inPos += 2 blockCount = 0 } } } if (outPos < outLen) { val block = decodabet(in(inPos)) << 18 | decodabet(in(inPos + 1)) << 12 | (if (inPos + 2 < inLen - padding) decodabet(in(inPos + 2)) << 6 else 0) out(outPos) = (block >> 16).toByte if (outPos + 1 < outLen) out(outPos + 1) = (block >> 8).toByte } Right(out) } } } ================================================ FILE: codec/shared/src/main/scala/rapture/codec/bytes.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.codec import rapture.core._ import scala.collection.generic.CanBuildFrom import language.higherKinds object `package` { implicit def bytesParser(implicit enc: Encoding): StringParser[Bytes] { type Throws = Nothing } = new StringParser[Bytes] { type Throws = Nothing // We would like to throw an EncodingException if we try to decode an invalid byte def parse(s: String, mode: Mode[_ <: MethodConstraint]): mode.Wrap[Bytes, Nothing] = mode.wrap { Bytes(s.getBytes("UTF-8")) } } } trait FromBytes[T] { def build(bytes: Array[Byte]): T } object FromBytes { implicit def stringFromBytes(implicit enc: Encoding): FromBytes[String] = new FromBytes[String] { def build(bytes: Array[Byte]): String = new String(bytes, enc.name) } implicit def bytesFromBytes = new FromBytes[Array[Byte]] { def build(bytes: Array[Byte]): Array[Byte] = bytes } } trait `decode.apply` extends MethodConstraint case class DecodeException(position: Option[Int]) extends Exception trait CodecType trait Hex extends CodecType trait Base64 extends CodecType trait Base64Url extends CodecType trait Base32 extends CodecType trait Binary extends CodecType object decode { def apply[C <: CodecType: ByteCodec](s: String)( implicit mode: Mode[`decode.apply`]): mode.Wrap[Bytes, DecodeException] = mode wrap { try { implicitly[ByteCodec[C]].decode(s) match { case Left(pos) => mode.exception(DecodeException(Some(pos))) case Right(res) => Bytes(res) } } catch { case e: Exception => mode.exception(DecodeException(None)) } } } object ByteCodec { implicit val base64Url: ByteCodec[Base64Url] = new Base64Codec('-', '_', '=', false, false) implicit val base64: ByteCodec[Base64] = new Base64Codec('+', '/', '=', false, false) implicit val hex: ByteCodec[Hex] = new ByteCodec[Hex] { def encode(array: Array[Byte]): String = alloc(array flatMap { n => Array((n & 255) >> 4 & 15, n & 15) } map { _ + 48 } map { i => (if (i > 57) i + 39 else i).toChar }) def decode(s: String): Either[Int, Array[Byte]] = Right((if (s.length % 2 == 0) s else "0" + s).to[Array].grouped(2).to[Array] map { case Array(l, r) => (((l - 48) % 39 << 4) + (r - 48) % 39).toByte }) } implicit val binary: ByteCodec[Binary] = new ByteCodec[Binary] { def encode(array: Array[Byte]): String = alloc(Array.range(0, array.length * 8) map { i => if ((array(i / 8) & (1 << (7 - i % 8))) > 0) 49.toByte else 48.toByte }) def decode(s: String): Either[Int, Array[Byte]] = { null } } } trait ByteCodec[Codec <: CodecType] { def encode(bytes: Array[Byte]): String def decode(string: String): Either[Int, Array[Byte]] } object Bytes { implicit def arrayBytes(bytes: Array[Byte]): Bytes = Bytes(bytes) } case class Bytes(bytes: Array[Byte]) { def encode[Codec <: CodecType: ByteCodec]: String = implicitly[ByteCodec[Codec]].encode(bytes) override def toString = encode[Hex] def ++(that: Bytes): Bytes = Bytes(bytes ++ that.bytes) override def equals(that: Any) = that match { case Bytes(bs) => bs.length == bytes.length && (bs zip bytes forall { case (a, b) => a == b }) case _ => false } override def hashCode = bytes.foldLeft(bytes.length)(_ * 131 + _) /** Sets all values in the underlying byte array to zeroes. This is useful if the `Bytes` * instance was storing sensitive data, such as a private key. */ def zero() = bytes.indices foreach { bytes(_) = 0 } def as[T: FromBytes](implicit mode: Mode[_ <: MethodConstraint]): mode.Wrap[T, DecodeException] = mode.wrap { try ?[FromBytes[T]].build(bytes) catch { case e: Exception => mode.exception[T, DecodeException](DecodeException(None)) } } def apply(index: Int): Byte = bytes(index) def slice(start: Int, end: Int) = Bytes(bytes.slice(start, end)) def length: Int = bytes.length def to[Coll[_]](implicit cbf: CanBuildFrom[Nothing, Byte, Coll[Byte]]): Coll[Byte] = bytes.to[Coll] } ================================================ FILE: codec/shared/src/main/scala/rapture/codec/encodings.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.codec import rapture.core._ @implicitNotFound( "Character encoding has not been provided. Please specify an implicit " + "Encoding value, e.g. import encodings.system._ or import encodings.`UTF-8`._.") case class Encoding(name: String) { def index = name } case class EncodingImplicit(name: String) { implicit val implicitEncoding: Encoding = Encoding(name) def apply() = implicitEncoding } /** Provides references to standard character encodings provided by Java. Encodings are * represented by instances of the Encoding case class, which is a simple wrapper over a String * of the encoding's name. Several standard encodings are provided and identified by the * encoding's canonical name for the avoidance of ambiguity. These instances will typically * require escaping with backticks in order to be referenced, however type safety will be * ensured. */ object encodings { implicit val `US-ASCII` = EncodingImplicit("US-ASCII") implicit val `windows-1250` = EncodingImplicit("windows-1250") implicit val `windows-1251` = EncodingImplicit("windows-1251") implicit val `windows-1252` = EncodingImplicit("windows-1252") implicit val `windows-1253` = EncodingImplicit("windows-1253") implicit val `windows-1254` = EncodingImplicit("windows-1254") implicit val `windows-1257` = EncodingImplicit("windows-1257") implicit val `ISO-8859-1` = EncodingImplicit("ISO-8859-1") implicit val `ISO-8859-2` = EncodingImplicit("ISO-8859-2") implicit val `ISO-8859-4` = EncodingImplicit("ISO-8859-4") implicit val `ISO-8859-5` = EncodingImplicit("ISO-8859-5") implicit val `ISO-8859-7` = EncodingImplicit("ISO-8859-7") implicit val `ISO-8859-9` = EncodingImplicit("ISO-8859-9") implicit val `ISO-8859-13` = EncodingImplicit("ISO-8859-13") implicit val `ISO-8859-15` = EncodingImplicit("ISO-8859-15") implicit val `KOI8-R` = EncodingImplicit("KOI8-R") implicit val `UTF-8` = EncodingImplicit("UTF-8") implicit val `UTF-16` = EncodingImplicit("UTF-16") implicit val `UTF-16BE` = EncodingImplicit("UTF-16BE") implicit val `UTF-16LE` = EncodingImplicit("UTF-16LE") /** The default file system encoding for this system */ implicit lazy val system = EncodingImplicit(System.getProperty("file.encoding")) private val allEncodings: Map[String, Encoding] = Map( ("US-ASCII", `US-ASCII`()), ("windows-1250", `windows-1250`()), ("windows-1251", `windows-1251`()), ("windows-1252", `windows-1252`()), ("windows-1253", `windows-1253`()), ("windows-1254", `windows-1254`()), ("windows-1257", `windows-1257`()), ("ISO-8859-1", `ISO-8859-1`()), ("ISO-8859-2", `ISO-8859-2`()), ("ISO-8859-4", `ISO-8859-4`()), ("ISO-8859-5", `ISO-8859-5`()), ("ISO-8859-7", `ISO-8859-7`()), ("ISO-8859-9", `ISO-8859-9`()), ("ISO-8859-13", `ISO-8859-13`()), ("ISO-8859-15", `ISO-8859-15`()), ("KOI8-R", `KOI8-R`()), ("UTF-8", `UTF-8`()), ("UTF-16", `UTF-16`()), ("UTF-16BE", `UTF-16BE`()), ("UTF-16LE", `UTF-16LE`()) ) def lookup(enc: String): Encoding = allEncodings(enc) } ================================================ FILE: core/shared/src/main/scala/rapture/core/actor.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.core import scala.concurrent._ import scala.util._ sealed trait ActorResponse[+T, +S] case class Reply[+T](reply: T) extends ActorResponse[T, Nothing] { def andUpdate[S](state: S) = Update[T, S](reply, state) } case class Update[+T, +S](reply: T, state: S) extends ActorResponse[T, S] case object Ignore extends ActorResponse[Nothing, Nothing] object Actor { class ActorOf[Msg] { def apply[Res, State](init: State)(fn: Transition[Msg, State] => ActorResponse[Res, State])( implicit ec: ExecutionContext): Actor[Msg, Res, State] = new Actor[Msg, Res, State](init) { def handle(trans: Transition[Msg, State]): ActorResponse[Res, State] = fn(trans) } } def of[Msg] = new ActorOf[Msg] } case class IgnoredException() extends Exception("Message was ignored") abstract class Actor[Msg, Res, State](init: State)(implicit executionContext: ExecutionContext) { private var future: Future[Res] = Future.successful(null.asInstanceOf[Res]) private var stateVar: State = init protected def enqueue(fn: => ActorResponse[Res, State]): Future[Res] = future.synchronized { val promise = Promise[Res] future = future.andThen { case _ => val result = Try(fn) match { case Success(Ignore) => promise.failure(IgnoredException()) case Success(Update(r, s)) => promise.success(r) stateVar = s case Success(Reply(r)) => promise.success(r) case Failure(err) => promise.failure(err) } } promise.future } def state: State = stateVar def cue(msg: Msg): Future[Res] = enqueue { handle(Transition(msg, stateVar)) } def handle(trans: Transition[Msg, State]): ActorResponse[Res, State] } case class Transition[Msg, State](msg: Msg, state: State) ================================================ FILE: core/shared/src/main/scala/rapture/core/alloc.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.core import language.experimental.macros import scala.reflect._ import annotation.unchecked._ class AllocApply[T](val unit: Int) extends AnyVal { def apply()(implicit inst: Alloc0.Invariant[T]): T = inst.instantiate() def apply[P1](p1: P1)(implicit inst: Alloc1.Invariant[T, P1]): T = inst.instantiate(p1) def apply[P1, P2](p1: P1, p2: P2)(implicit inst: Alloc2.Invariant[T, P1, P2]): T = inst.instantiate(p1, p2) def apply[P1, P2, P3](p1: P1, p2: P2, p3: P3)(implicit inst: Alloc3.Invariant[T, P1, P2, P3]): T = inst.instantiate(p1, p2, p3) def apply[P1, P2, P3, P4](p1: P1, p2: P2, p3: P3, p4: P4)(implicit inst: Alloc4.Invariant[T, P1, P2, P3, P4]): T = inst.instantiate(p1, p2, p3, p4) } object Alloc0 { implicit def alloc0[T]: Alloc0[T] = macro CoreMacros.allocMacro[T] type Invariant[+T] = Alloc0[T @uncheckedVariance] } object Alloc1 { implicit def alloc1[T, P1]: Alloc1[T, P1] = macro CoreMacros.allocMacro1[T, P1] type Invariant[+T, P1] = Alloc1[T @uncheckedVariance, P1] } object Alloc2 { implicit def alloc2[T, P1, P2]: Alloc2[T, P1, P2] = macro CoreMacros.allocMacro2[T, P1, P2] type Invariant[+T, P1, P2] = Alloc2[T @uncheckedVariance, P1, P2] } object Alloc3 { implicit def alloc3[T, P1, P2, P3]: Alloc3[T, P1, P2, P3] = macro CoreMacros.allocMacro3[T, P1, P2, P3] type Invariant[+T, P1, P2, P3] = Alloc3[T @uncheckedVariance, P1, P2, P3] } object Alloc4 { implicit def alloc4[T, P1, P2, P3, P4]: Alloc4[T, P1, P2, P3, P4] = macro CoreMacros.allocMacro4[T, P1, P2, P3, P4] type Invariant[+T, P1, P2, P3, P4] = Alloc4[T @uncheckedVariance, P1, P2, P3, P4] } @implicitNotFound("No constructor exists for instantiating an object of this type") trait Alloc0[T] { def instantiate(): T } @implicitNotFound("No constructor exists for instantiating an object of this type") trait Alloc1[T, P1] { def instantiate(p1: P1): T } @implicitNotFound("No constructor exists for instantiating an object of this type") trait Alloc2[T, P1, P2] { def instantiate(p1: P1, p2: P2): T } @implicitNotFound("No constructor exists for instantiating an object of this type") trait Alloc3[T, P1, P2, P3] { def instantiate(p1: P1, p2: P2, p3: P3): T } @implicitNotFound("No constructor exists for instantiating an object of this type") trait Alloc4[T, P1, P2, P3, P4] { def instantiate(p1: P1, p2: P2, p3: P3, p4: P4): T } ================================================ FILE: core/shared/src/main/scala/rapture/core/app.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.core object Main extends App { Console.println("Running this JAR file does nothing. To use it, please include it on your classpath.") } ================================================ FILE: core/shared/src/main/scala/rapture/core/core.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.core import language.experimental.macros import language.higherKinds object AssignedName { implicit def assignedNameImplicit: AssignedName = macro CoreMacros.assignedNameMacro } case class AssignedName(name: String) extends AnyVal { override def toString = s"`$name`" } object MethodName { implicit def assignedMethodNameImplicit: MethodName = macro CoreMacros.assignedMethodNameMacro } class MethodName(val name: String) extends AnyVal trait Cell[T] { def apply(): T def update(t: T): Unit } object Cell { def apply[T](get: => T)(set: T => Unit): Cell[T] = new Cell[T] { def apply() = get def update(t: T) = set(t) } } object Var { def apply[T](t: T) = new Cell[T] { private var value = t def apply(): T = value def update(t: T) = value = t } } object OptionalParameter { implicit def autoWrapSpecifiedParameter[T](value: T): OptionalParameter[T] = SpecifiedParameter[T](value) } sealed trait OptionalParameter[+T] { def apply(): Option[T] } object SeqParameter { implicit def listToSeqParameter[T](seq: Seq[T]): SeqParameter[T] = SeqParameter(seq: _*) implicit def optionToSeqParameter[T](opt: Option[T]): SeqParameter[T] = SeqParameter(opt.to[Seq]: _*) implicit def anyToSeqParameter[T](value: T): SeqParameter[T] = SeqParameter(value) } case class SeqParameter[T](elements: T*) case class SpecifiedParameter[+T] (value: T) extends OptionalParameter[T] { def apply(): Option[T] = Some(value) } case object UnspecifiedParameter extends OptionalParameter[Nothing] { def apply(): Option[Nothing] = None } object Annex { implicit def annexValueWithTypeclass[V, Tc[_]](v: V)(implicit tc: Tc[V]): Annex[Tc] = new Annex[Tc] { type Value = V def value: Value = v def typeclass: Tc[Value] = tc } } abstract class Annex[Typeclass[_]] { type Value def value: Value def typeclass: Typeclass[Value] def apply[Return](fn: Typeclass[Value] => Value => Return): Return = fn(typeclass)(value) } ================================================ FILE: core/shared/src/main/scala/rapture/core/default.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.core private[core] trait DefaultsTo_1 { implicit def fallbackDefaultsTo[T, S]: DefaultsTo[T, S] = null.asInstanceOf[DefaultsTo[T, S]] } object DefaultsTo extends DefaultsTo_1 { implicit def defaultDefaultsTo[T]: DefaultsTo[T, T] = null.asInstanceOf[DefaultsTo[T, T]] } trait DefaultsTo[T, S] ================================================ FILE: core/shared/src/main/scala/rapture/core/functor.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.core import scala.reflect.ClassTag import language.higherKinds trait Functor[+F[x] <: Functor[F, x], A] { functor => type Throws <: Exception protected def rawMap[B](fn: (A, Mode[_ <: MethodConstraint]) => B): F[B] def map[B](fn: A => B): F[B] { type Throws = functor.Throws with Exception } = emap[Exception](fn) def emap[E <: Exception]: Emap[E] = new Emap[E]() def smap[B](fn: A => B): F[B] { type Throws = functor.Throws } = emap[Nothing](fn).asInstanceOf[F[B] { type Throws = functor.Throws }] // FIXME: Make this a value class class Emap[E <: Exception]() { def apply[B](fn: A => B)(implicit tt: ClassTag[E]): F[B] { type Throws = functor.Throws with E } = functor.rawMap { case (a, m) => try fn(a) catch { case e: Exception => m.exception[B, E](e.asInstanceOf[E]) } }.asInstanceOf[F[B] { type Throws = functor.Throws with E }] } } ================================================ FILE: core/shared/src/main/scala/rapture/core/macros.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.core import rapture.base._ import scala.reflect._ private[core] object CoreMacros { def enumerateMacro[Cls: c.WeakTypeTag, T: c.WeakTypeTag](c: BlackboxContext)(value: c.Expr[Cls]): c.Expr[List[T]] = { import c.universe._ import compatibility._ val cls = weakTypeOf[Cls] val allMethods = weakTypeOf[Cls].members.to[List].filter(_.isMethod).map(_.asMethod) val matchingMethods = allMethods filter { m => paramLists(c)(m).isEmpty && m.returnType.weak_<:<(weakTypeOf[T]) } val methodNames = matchingMethods map { m => Select(value.tree, termName(c, m.name.toString)) } val listApply = Select(reify(List).tree, termName(c, "apply")) c.Expr[List[T]](Apply(listApply, methodNames)) } def assignedMethodNameMacro(c: BlackboxContext): c.Expr[MethodName] = { import c.universe._ import compatibility._ val name = enclosingDef(c)(c.macroApplication.pos).map { name => c.Expr[MethodName](q"new _root_.rapture.core.MethodName(${name.decodedName.toString.trim})") } name getOrElse c.abort(c.enclosingPosition, "this method invocation must be assigned to a named identifier.") } object AssignedNameMacroState { var lastPoint: Option[api.Position] = None var assignmentCount: Int = 0 } def assignedNameMacro(c: BlackboxContext): c.Expr[AssignedName] = { import c.universe._ import AssignedNameMacroState._ import compatibility._ val currentPoint = c.macroApplication.pos if(Some(currentPoint) != lastPoint) assignmentCount = 0 val name = enclosingVals(c)(currentPoint, assignmentCount).map { name => c.Expr[AssignedName](q"_root_.rapture.core.AssignedName(${name.decodedName.toString.trim})") } lastPoint = Some(currentPoint) assignmentCount += 1 name getOrElse c.abort(c.enclosingPosition, "this method invocation must be assigned to a named identifier.") } def allocMacro[T: c.WeakTypeTag](c: BlackboxContext): c.Expr[Alloc0[T]] = { import c.universe._ import compatibility._ val construction = c.Expr[T](Apply(Select(New(TypeTree(weakTypeOf[T])), constructor(c)), List())) reify { new Alloc0[T] { def instantiate(): T = construction.splice } } } def allocMacro1[T: c.WeakTypeTag, P1: c.WeakTypeTag](c: BlackboxContext): c.Expr[Alloc1[T, P1]] = { import c.universe._ import compatibility._ val construction = c.Expr[T](Apply(Select(New(TypeTree(weakTypeOf[T])), constructor(c)), List(Ident(termName(c, "p1"))))) reify { new Alloc1[T, P1] { def instantiate(p1: P1): T = construction.splice } } } def allocMacro2[T: c.WeakTypeTag, P1: c.WeakTypeTag, P2: c.WeakTypeTag]( c: BlackboxContext): c.Expr[Alloc2[T, P1, P2]] = { import c.universe._ import compatibility._ val construction = c.Expr[T]( Apply(Select(New(TypeTree(weakTypeOf[T])), constructor(c)), List(Ident(termName(c, "p1")), Ident(termName(c, "p2"))))) reify { new Alloc2[T, P1, P2] { def instantiate(p1: P1, p2: P2): T = construction.splice } } } def allocMacro3[T: c.WeakTypeTag, P1: c.WeakTypeTag, P2: c.WeakTypeTag, P3: c.WeakTypeTag]( c: WhiteboxContext): c.Expr[Alloc3[T, P1, P2, P3]] = { import c.universe._ import compatibility._ val construction = c.Expr[T]( Apply(Select(New(TypeTree(weakTypeOf[T])), constructor(c)), List(Ident(termName(c, "p1")), Ident(termName(c, "p2")), Ident(termName(c, "p3"))))) reify { new Alloc3[T, P1, P2, P3] { def instantiate(p1: P1, p2: P2, p3: P3): T = construction.splice } } } def allocMacro4[T: c.WeakTypeTag, P1: c.WeakTypeTag, P2: c.WeakTypeTag, P3: c.WeakTypeTag, P4: c.WeakTypeTag]( c: WhiteboxContext): c.Expr[Alloc4[T, P1, P2, P3, P4]] = { import c.universe._ import compatibility._ val construction = c.Expr[T]( Apply(Select(New(TypeTree(weakTypeOf[T])), constructor(c)), List(Ident(termName(c, "p1")), Ident(termName(c, "p2")), Ident(termName(c, "p3")), Ident(termName(c, "p4"))))) reify { new Alloc4[T, P1, P2, P3, P4] { def instantiate(p1: P1, p2: P2, p3: P3, p4: P4): T = construction.splice } } } } ================================================ FILE: core/shared/src/main/scala/rapture/core/med.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.core object MinimumEditDistance { def difference(a: String, b: String): Int = { var d = Vector.fill(a.length + 1)(Vector.fill(b.length + 1)(0)) (0 to b.length).foreach { j => (0 to a.length).foreach { i => if (i == 0 || j == 0) d = d.updated(i, d(i).updated(j, i + j)) else if (a(i - 1) == b(j - 1)) d = d.updated(i, d(i).updated(j, d(i - 1)(j - 1))) else d = d.updated(i, d(i).updated(j, List(d(i - 1)(j), d(i)(j - 1), d(i - 1)(j - 1)).min + 1)) } } d(a.length)(b.length) } def filterStrings(words: Array[String], word: String, limit: Int): List[String] = { val arr = new Array[Int](256) val results = new collection.mutable.ListBuffer[String] for (i <- 0 to 15) { arr(i) = i arr(16 * i) = i } def difference(d: Array[Int], a: String): Int = { val amax = math.min(a.length, 15) val t = amax + word.length var i, j, n = 0 var min = Int.MaxValue var cont = true while (n <= t && cont) { val r = if (i == 0 || j == 0) i + j else if (a(i - 1) == word(j - 1)) d(16 * (i - 1) + j - 1) else math.min(math.min(d(16 * (i - 1) + j), d(16 * i + j - 1)), d(16 * (i - 1) + j - 1)) + 1 min = math.min(min, r) d(16 * i + j) = r if (j == 0 || i == amax) { n += 1 if (n <= word.length) { j = n; i = 0 } else { i = n - word.length; j = word.length } if (min > limit) cont = false min = Int.MaxValue } else { i += 1 j -= 1 } } if (cont) d(16 * amax + word.length) else Int.MaxValue } val len = word.length words.filter { w => difference(arr, w.take(len)) <= limit }.to[List] } } ================================================ FILE: core/shared/src/main/scala/rapture/core/modes.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.core import language.{existentials, higherKinds} import scala.reflect._ import scala.util._ import scala.concurrent._ trait MethodConstraint object Mode extends Mode_1 { abstract class Import[M[G <: MethodConstraint] <: Mode[G]] { def apply[G <: MethodConstraint](): M[G] = modeImplicit[G] implicit def modeImplicit[G <: MethodConstraint]: M[G] = mode[G] protected def mode[G <: MethodConstraint]: M[G] } } @implicitNotFound( msg = "No implicit mode was available for $"+"{Group} methods. " + "Please import a member of rapture.core.modes, e.g. modes.throwExceptions.") trait Mode[+Group <: MethodConstraint] { mode => type Wrap[+_, _ <: Exception] def wrap[Res, E <: Exception](blk: => Res): Wrap[Res, E] def flatWrap[Res, E <: Exception: ClassTag](blk: => Wrap[Res, E]): Wrap[Res, E] = wrap(unwrap(blk)) var callPath = "_" def unwrap[Res](value: => Wrap[Res, _ <: Exception]): Res def unwrap[Res](value: => Wrap[Res, _ <: Exception], path: String): Res = { val oldCallPath = callPath callPath += path val res = unwrap[Res](value) callPath = oldCallPath res } def generic[C <: MethodConstraint]: Mode[C] { type Wrap[+T, E <: Exception] = mode.Wrap[T, E] } = this.asInstanceOf[Mode[C] { type Wrap[+T, E <: Exception] = mode.Wrap[T, E] }] def compose[Group2 <: MethodConstraint](mode2: Mode[Group2]) = new Mode[Group] { type Wrap[+Res, E <: Exception] = mode.Wrap[mode2.Wrap[Res, E], E] def wrap[Res, E <: Exception](blk: => Res): Wrap[Res, E] = mode.wrap(mode2.wrap(blk)) def unwrap[Return](value: => Wrap[Return, _ <: Exception]): Return = mode2.unwrap(mode.unwrap(value)) } def catching[E <: Exception: ClassTag, T](blk: => T) = try blk catch { case e: E => exception(e) case e: Exception => throw e } def safe[T](blk: => T): T = { try blk catch { case e: Exception => exception(e) } } def exception[T, E <: Exception: ClassTag](e: E, continue: Boolean = true): T = throw e def wrapEither[Res, E <: Exception: ClassTag](blk: => Either[E, Res]): Wrap[Res, E] = wrap { blk match { case Left(e) => throw e case Right(r) => r } } def wrapOption[Res](blk: => Option[Res]): Wrap[Res, Exception] = wrap(blk.get) def wrapTry[Res, E <: Exception: ClassTag](blk: => Try[Res]): Wrap[Res, E] = wrap(blk.get) } object repl { var showStackTraces: Boolean = false private var lastExceptionValue: Throwable = new SilentException def lastException: Nothing = throw lastExceptionValue implicit def modeImplicit[Group <: MethodConstraint] = new Repl[Group] class SilentException extends Throwable { override def printStackTrace(pw: java.io.PrintWriter) = () } class Repl[+Group <: MethodConstraint] extends Mode[Group] { type Wrap[+Return, E <: Exception] = T2 forSome { type T2 <: Return } def wrap[Return, E <: Exception](blk: => Return): Return = try blk catch { case e: Exception => if (showStackTraces) throw e else { Console.println("Execution failed with exception: " + e.toString) Console.print("For the full stacktrace, see repl.lastException.") lastExceptionValue = e throw new SilentException() } } def unwrap[Return](value: => Wrap[Return, _ <: Exception]): Return = value } } package modes { object throwExceptions extends Mode.Import[ThrowExceptionsMode] { protected def mode[G <: MethodConstraint] = new ThrowExceptionsMode[G] } object explicit extends Mode.Import[ExplicitMode] { protected def mode[G <: MethodConstraint] = new ExplicitMode[G] } /*object returnEither extends Mode.Import[ReturnEitherMode] { protected def mode[G <: MethodConstraint] = new ReturnEitherMode[G] }*/ object returnResult extends Mode.Import[ReturnResultMode] { protected def mode[G <: MethodConstraint] = new ReturnResultMode[G] } object returnTry extends Mode.Import[ReturnTryMode] { protected def mode[G <: MethodConstraint] = new ReturnTryMode[G] } object exponentialBackoff extends Mode.Import[ExponentialBackoffMode] { protected def mode[G <: MethodConstraint] = new ExponentialBackoffMode[G]() } object keepCalmAndCarryOn extends Mode.Import[KeepCalmAndCarryOnMode] { protected def mode[G <: MethodConstraint] = new KeepCalmAndCarryOnMode[G]() } object returnOption extends Mode.Import[ReturnOptionMode] { protected def mode[G <: MethodConstraint] = new ReturnOptionMode[G]() } object returnFuture { implicit def modeImplicit[G <: MethodConstraint](implicit ec: ExecutionContext) = new ReturnFutureMode[G] def apply[G <: MethodConstraint](implicit ec: ExecutionContext) = modeImplicit[G] } object timeExecution { implicit def modeImplicit[D: TimeSystem.ByDuration, G <: MethodConstraint] = new TimeExecution[D, G] def apply[D: TimeSystem.ByDuration, G <: MethodConstraint] = modeImplicit[D, G] } class Explicitly[+Res, E <: Exception](blk: => Res) { def get: Res = blk def opt: Option[Res] = returnOption[Nothing].wrap(blk) def getOrElse[Res2 >: Res](t: Res2): Res2 = opt.getOrElse(blk) //def either: Either[E, Res] = returnEither[Nothing].wrap(blk) def attempt: Try[Res] = returnTry[Nothing].wrap(blk) def backoff(maxRetries: Int = 10, initialPause: Long = 1000L, backoffRate: Double = 2.0): Res = new ExponentialBackoffMode(maxRetries, initialPause, backoffRate).wrap(blk) def time[D: TimeSystem.ByDuration] = timeExecution[D, Nothing].wrap(blk) def future(implicit ec: ExecutionContext): Future[Res] = returnFuture[Nothing].wrap(blk) override def toString = "" } } private[core] trait Mode_1 { implicit def defaultMode: ThrowExceptionsMode[Nothing] = new ThrowExceptionsMode } private[core] class ThrowExceptionsMode[+G <: MethodConstraint] extends Mode[G] { type Wrap[+T, E <: Exception] = T2 forSome { type T2 <: T } def wrap[T, E <: Exception](t: => T): T = t def unwrap[Return](value: => Wrap[Return, _ <: Exception]): Return = value } private[core] class ExplicitMode[+G <: MethodConstraint] extends Mode[G] { type Wrap[+T, E <: Exception] = modes.Explicitly[T, E] def wrap[T, E <: Exception](t: => T): modes.Explicitly[T, E] = new modes.Explicitly[T, E](t) def unwrap[Return](value: => Wrap[Return, _ <: Exception]): Return = value.get } private[core] class ReturnTryMode[+G <: MethodConstraint] extends Mode[G] { type Wrap[+T, E <: Exception] = Try[T] def wrap[T, E <: Exception](t: => T): Try[T] = Try(t) def unwrap[Return](value: => Wrap[Return, _ <: Exception]): Return = value.get override def toString = "[modes.returnTry]" } private[core] class ExponentialBackoffMode[+G <: MethodConstraint](maxRetries: Int = 10, initialPause: Long = 1000L, backoffRate: Double = 2.0) extends Mode[G] { type Wrap[+T, E <: Exception] = T2 forSome { type T2 <: T } def wrap[T, E <: Exception](t: => T): T = { var multiplier = 1.0 var count = 1 var result: T = null.asInstanceOf[T] var exception: Exception = null.asInstanceOf[Exception] while (result == null && count < maxRetries) try { result = t } catch { case e: Exception => exception = e import timeSystems.numeric._ Thread.sleep((multiplier * initialPause).toLong) multiplier *= backoffRate count += 1 } if (result != null) result else throw exception } def unwrap[Return](value: => Wrap[Return, _ <: Exception]): Return = value } private[core] class KeepCalmAndCarryOnMode[+G <: MethodConstraint] extends Mode[G] { type Wrap[+T, E <: Exception] = T2 forSome { type T2 <: T } def wrap[T, E <: Exception](t: => T): T = try t catch { case e: Exception => null.asInstanceOf[T] } def unwrap[Return](value: => Wrap[Return, _ <: Exception]): Return = Option[Return](value).get override def toString = "[modes.kcaco]" } private[core] class ReturnOptionMode[+G <: MethodConstraint] extends Mode[G] { type Wrap[+T, E <: Exception] = Option[T] def wrap[T, E <: Exception](t: => T): Option[T] = try Some(t) catch { case e: Exception => None } def unwrap[Return](value: => Wrap[Return, _ <: Exception]): Return = value.get override def toString = "[modes.returnOption]" } private[core] class ReturnFutureMode[+G <: MethodConstraint](implicit ec: ExecutionContext) extends Mode[G] { type Wrap[+T, E <: Exception] = Future[T] def wrap[T, E <: Exception](t: => T): Future[T] = Future { t } def unwrap[Return](value: => Wrap[Return, _ <: Exception]): Return = Await.result(value, duration.Duration.Inf) override def flatWrap[Res, E <: Exception: ClassTag](blk: => Wrap[Res, E]): Wrap[Res, E] = blk override def toString = "[modes.returnFuture]" } private[core] class TimeExecution[D: TimeSystem.ByDuration, +G <: MethodConstraint] extends Mode[G] { val ts = ?[TimeSystem.ByDuration[D]] type Wrap[+T, E <: Exception] = (T, D) def wrap[T, E <: Exception](r: => T): (T, D) = { val t0 = System.currentTimeMillis (r, ts.duration(t0, System.currentTimeMillis)) } def unwrap[Return](value: => Wrap[Return, _ <: Exception]): Return = value._1 override def toString = "[modes.timeExecution]" } ================================================ FILE: core/shared/src/main/scala/rapture/core/package.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.core import language.higherKinds import language.experimental.macros import reflect.runtime.universe._ import reflect.ClassTag object `package` { type CanBuildFrom[-From, -Elem, +To] = collection.generic.CanBuildFrom[From, Elem, To] def alloc[T] = new AllocApply[T](0) def each[E <: Exception] = EachUnapplied[E]() implicit class EnrichedString(val string: String) extends AnyVal { def as[T](implicit parser: StringParser[T], mode: Mode[`String#as`]): mode.Wrap[T, parser.Throws] = parser.parse(string, mode) } def indentTree(s: String): String = { var indent = 0 s flatMap { case '(' => indent += 1; s"(\n${" " * indent}" case ')' => indent -= 1; s"\n${" " * indent})" case ',' => s",\n${" " * (indent - 1)}" case ' ' => "" case o => o.toString } } implicit class EnrichedCollection[Coll[X] <: Seq[X]](val coll: Coll[String]) extends AnyVal { def mapAs[T](implicit parser: StringParser[T], cbf: CanBuildFrom[Coll[String], T, Coll[T]], mode: Mode[`Seq#mapAs`]): mode.Wrap[Coll[T], parser.Throws] = mode.wrap[Coll[T], parser.Throws] { val b = cbf(coll) coll foreach { x => b += mode.unwrap(parser.parse(x, mode)) } b.result } } private[rapture] type implicitNotFound = annotation.implicitNotFound private[rapture] implicit val implicitConversions: languageFeature.implicitConversions = language.implicitConversions @inline final def ?[T](implicit t: T) = t def modally[G <: MethodConstraint, E <: Exception] = new Modal[G, E] def yCombinator[A, B](fn: (A => B) => (A => B)): A => B = fn(yCombinator(fn))(_) /** Times how long it takes to perform an operation, returning a pair of the result and the * duration of the operation in milliseconds. */ def time[T, D: TimeSystem.ByDuration](blk: => T): (T, D) = { val t = System.currentTimeMillis (blk, ?[TimeSystem.ByDuration[D]].duration(t, System.currentTimeMillis)) } def enumerateMembers[T] = new Enumerator[T] @inline implicit class SeqExtras[A, C[A] <: Seq[A]](val xs: C[A]) { /** Inserts an element between each of the elements of the sequence. */ def intersperse[B >: A, That](between: B)(implicit bf: CanBuildFrom[C[A], B, That]): That = { val b = bf(xs) xs.init foreach { x => b += x b += between } b += xs.last b.result } /** Inserts an element between each of the elements of the sequence, and additionally * prepends and affixes the sequence with `before` and `after`. */ def intersperse[B >: A, That](before: B, between: B, after: B)(implicit bf: CanBuildFrom[C[A], B, That]): That = { val b = bf(xs) b += before xs.init foreach { x => b += x b += between } b += xs.last b += after b.result } /** Convenience method for zipping a sequence with a value derived from each element. */ def zipWith[T](fn: A => T)(implicit bf: CanBuildFrom[C[A], (A, T), C[(A, T)]]): C[(A, T)] = { val b = bf(xs) xs.foreach { x => b += ((x, fn(x))) } b.result } } implicit class EnrichedCollectionCompanion[+C[X] <: collection.GenTraversable[X]]( val cc: collection.generic.GenericCompanion[C]) extends AnyVal { def strap[T](xs: Strapped[T]*): C[T] = { val b = cc.newBuilder[T] xs foreach { b ++= _.elems } b.result() } } implicit class EnrichedArrayCompanion(val arr: Array.type) extends AnyVal { def strap[T: ClassTag](xs: Strapped[T]*): Array[T] = { val b = Array.newBuilder[T] xs foreach { b ++= _.elems } b.result() } } implicit class EitherExtras[L, R](either: Either[L, R]) { def bimap[T](leftFn: L => T, rightFn: R => T) = either match { case Left(left) => leftFn(left) case Right(right) => rightFn(right) } } } trait `Seq#mapAs` extends MethodConstraint trait `String#as` extends MethodConstraint private[core] object Strapped { implicit def basicStrapping[T](t: T): Strapped[T] = Strapped(List(t)) implicit def iterableStrapping[T](elems: Iterable[T]): Strapped[T] = Strapped(elems) implicit def optionStrapping[T](opt: Option[T]): Strapped[T] = Strapped(opt.toList) } private[core] case class Strapped[+T](elems: Iterable[T]) extends AnyVal private[core] class Enumerator[T] { def apply[Cls](value: Cls): List[T] = macro CoreMacros.enumerateMacro[Cls, T] } private[core] class Modal[G <: MethodConstraint, E <: Exception] { def apply[T](fn: => T)(implicit mode: Mode[G], typeTag: TypeTag[E]): mode.Wrap[T, E] = mode.wrap(fn) } ================================================ FILE: core/shared/src/main/scala/rapture/core/parser.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.core import scala.util.Try object ParseException case class ParseException(bad: String, typ: String) extends Exception(s"could not parse '$bad' as $typ") package booleanParsing { object strict { def apply() = implicitBooleanParsing implicit def implicitBooleanParsing(implicit br: BooleanRepresentation): BooleanParser = new BooleanParser { def parse(s: String, mode: Mode[_]): mode.Wrap[Boolean, InvalidBoolean] = mode.wrap { if (s == br.trueValue) true else if (s == br.falseValue) false else mode.exception(InvalidBoolean(s)) } } } object permissive { def apply(): BooleanParser = implicitBooleanParsing private val trueValues = List("true", "yes", "on", "1") private val falseValues = List("false", "no", "off", "0") implicit val implicitBooleanParsing: BooleanParser = new BooleanParser { def parse(b: String, mode: Mode[_]): mode.Wrap[Boolean, InvalidBoolean] = mode.wrap { if (trueValues.contains(b.toLowerCase)) true else if (falseValues.contains(b.toLowerCase)) false else mode.exception(ParseException(b, "boolean using permissive parser")) } } } } object BooleanParser { implicit val implicitBooleanParser: BooleanParser = booleanParsing.permissive() } trait BooleanParser { def parse(s: String, mode: Mode[_]): mode.Wrap[Boolean, InvalidBoolean] } abstract class StringParser[T] extends Functor[StringParser, T] { strp => type Throws <: Exception def parse(string: String, mode: Mode[_ <: MethodConstraint]): mode.Wrap[T, Throws] def rawMap[T2](fn: (T, Mode[_ <: MethodConstraint]) => T2): StringParser[T2] { type Throws = strp.Throws } = new StringParser[T2] { type Throws = strp.Throws def parse(s: String, mode: Mode[_ <: MethodConstraint]): mode.Wrap[T2, Throws] = mode.wrap(fn(mode.unwrap(strp.parse(s, mode)), mode)) } } case class InvalidBoolean(value: String) extends Exception(s"""The value "$value" is not a valid boolean.""") case class InvalidNumber(value: String, numberType: String) extends Exception(s"""The value "$value" is not a valid $numberType.""") trait StringParser_1 { implicit def optParser[T: StringParser]: StringParser[Option[T]] { type Throws = Nothing } = new StringParser[Option[T]] { type Throws = Nothing def parse(s: String, mode: Mode[_ <: MethodConstraint]): mode.Wrap[Option[T], Nothing] = mode.wrap { try Some(mode.unwrap(?[StringParser[T]].parse(s, mode))) catch { case e: Exception => None } } } implicit def tryParser[T: StringParser]: StringParser[Try[T]] { type Throws = Nothing } = new StringParser[Try[T]] { type Throws = Nothing def parse(s: String, mode: Mode[_ <: MethodConstraint]): mode.Wrap[Try[T], Nothing] = mode.wrap { ?[StringParser[T]].parse(s, modes.returnTry()) } } } object StringParser extends StringParser_1 { def apply[T](f: String => T): StringParser[T] { type Throws = ParseException } = { new StringParser[T] { type Throws = ParseException def parse(str: String, mode: Mode[_ <: MethodConstraint]): mode.Wrap[T, ParseException] = mode.wrap { try f(str) catch { case e: Exception => mode.exception(ParseException(str, e.getMessage)) } } } } implicit def booleanParser(implicit bp: BooleanParser): StringParser[Boolean] { type Throws = InvalidBoolean } = new StringParser[Boolean] { type Throws = InvalidBoolean def parse(s: String, mode: Mode[_ <: MethodConstraint]): mode.Wrap[Boolean, InvalidBoolean] = bp.parse(s, mode.generic) } implicit val byteParser: StringParser[Byte] { type Throws = InvalidNumber } = new StringParser[Byte] { type Throws = InvalidNumber def parse(s: String, mode: Mode[_ <: MethodConstraint]): mode.Wrap[Byte, InvalidNumber] = mode.wrap { try java.lang.Byte.parseByte(s) catch { case e: NumberFormatException => mode.exception(InvalidNumber(s, "byte")) } } } implicit val charParser: StringParser[Char] { type Throws = InvalidNumber } = new StringParser[Char] { type Throws = InvalidNumber def parse(s: String, mode: Mode[_ <: MethodConstraint]): mode.Wrap[Char, InvalidNumber] = mode.wrap { if (s.length == 1) s.charAt(0) else mode.exception(InvalidNumber(s, "character")) } } implicit val shortParser: StringParser[Short] { type Throws = InvalidNumber } = new StringParser[Short] { type Throws = InvalidNumber def parse(s: String, mode: Mode[_ <: MethodConstraint]): mode.Wrap[Short, InvalidNumber] = mode.wrap { try java.lang.Short.parseShort(s) catch { case e: NumberFormatException => mode.exception(InvalidNumber(s, "short")) } } } implicit val intParser: StringParser[Int] { type Throws = InvalidNumber } = new StringParser[Int] { type Throws = InvalidNumber def parse(s: String, mode: Mode[_ <: MethodConstraint]): mode.Wrap[Int, InvalidNumber] = mode.wrap { try java.lang.Integer.parseInt(s) catch { case e: NumberFormatException => mode.exception(InvalidNumber(s, "integer")) } } } implicit val longParser: StringParser[Long] { type Throws = InvalidNumber } = new StringParser[Long] { type Throws = InvalidNumber def parse(s: String, mode: Mode[_ <: MethodConstraint]): mode.Wrap[Long, InvalidNumber] = mode.wrap { try java.lang.Long.parseLong(s) catch { case e: NumberFormatException => mode.exception(InvalidNumber(s, "long")) } } } implicit val stringParser: StringParser[String] { type Throws = Nothing } = new StringParser[String] { type Throws = Nothing def parse(s: String, mode: Mode[_ <: MethodConstraint]): mode.Wrap[String, Nothing] = mode.wrap(s) } implicit val doubleParser: StringParser[Double] = new StringParser[Double] { type Throws = InvalidNumber def parse(s: String, mode: Mode[_ <: MethodConstraint]): mode.Wrap[Double, InvalidNumber] = mode.wrap { try java.lang.Double.parseDouble(s) catch { case e: NumberFormatException => mode.exception(ParseException(s, "double")) } } } implicit val floatParser: StringParser[Float] = new StringParser[Float] { type Throws = InvalidNumber def parse(s: String, mode: Mode[_ <: MethodConstraint]): mode.Wrap[Float, InvalidNumber] = mode.wrap { try java.lang.Float.parseFloat(s) catch { case e: NumberFormatException => mode.exception(InvalidNumber(s, "float")) } } } } ================================================ FILE: core/shared/src/main/scala/rapture/core/pool.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.core import scala.collection.mutable /** Implements a dynamic pool of some resource, e.g. database connections. */ abstract class Pool[Resource] { /** Implement to make new resources. */ protected def make(): Resource /** Implement to dispose of surplus resources. */ protected def dispose(x: Resource): Unit /** Implement to check resource is still usable. */ protected def check(x: Resource): Boolean /** Number of resource to always keep in reserve, if we have them. */ protected def spare = 5 /** How long to leave surplus resources unused before discarding them. */ protected def timeout = 10 * 60000L private val pool = new mutable.Queue[Resource] private var poolCount = 0 private var lastLow = 0L /** Acquire a resource for the duration of the body. */ def acquireFor[A](body: Resource => A): A = { val res = acquireDirect() try body(res) finally releaseDirect(res) } /** Acquire a resource without any nesting guarantees. Avoid this method. */ def acquireDirect(): Resource = pool.synchronized { if (poolCount == 0) make() else { val r = pool.dequeue poolCount = poolCount - 1 if (check(r)) r else { dispose(r) make() } } } /** Release a directly-acquired resource. */ def releaseDirect(r: Resource): Unit = pool.synchronized { val now = System.currentTimeMillis() if (poolCount < spare) lastLow = now if (lastLow > now - timeout) { pool.enqueue(r) poolCount = poolCount + 1 } else dispose(r) } /** Dispose of all resources not currently in use. */ def disposeAll() = pool.synchronized { while (poolCount > 0) { dispose(pool.dequeue) poolCount = poolCount - 1 } } } ================================================ FILE: core/shared/src/main/scala/rapture/core/result.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.core import scala.language.higherKinds import scala.reflect.ClassTag import scala.annotation.unchecked._ object Result { private[core] def apply[T, E <: Exception](result: => T, errors: Seq[(ClassTag[_], (String, Exception))]) = try { if (errors.isEmpty) Answer[T, E](result) else Errata[T, E](errors) } catch { case e: Throwable => if (errors.isEmpty) Unforeseen[T, E](e) else Errata[T, E](errors) } def apply[T](result: => T): Result[T, Nothing] = try Answer[T, Nothing](result) catch { case e: Throwable => Unforeseen[T, Nothing](e) } def catching[E <: Exception]: Catching[E] = new Catching[E]() /** Construct an answer. */ def answer[T, E <: Exception](a: T): Result[T, E] = Answer[T, E](a) /** Construct an errata. */ def errata[T, E <: Exception](e: E)(implicit cte: ClassTag[E]) = Errata[T, E](e) } class Catching[E <: Exception]() { def apply[T](blk: => T)(implicit classTag: ClassTag[E]): Result[T, E] = try Answer(blk) catch { case e: E => Errata(Vector((?[ClassTag[E]], ("", e)))) case e: Throwable => Unforeseen(e) } } sealed abstract class Result[+T, E <: Exception](val answer: T, val errors: Seq[(ClassTag[_], (String, Exception))], val unforeseen: Option[Throwable] = None) { def errata[E2 >: E: ClassTag]: Seq[E2] = errors.filter(_._1 == ?[ClassTag[E2]]).map(_._2.asInstanceOf[E2]) def exceptions: Seq[Exception] = errors.map(_._2._2) def get: T = { unforeseen.foreach(throw _) errors.foreach { case (k, (p, e)) => throw e } answer } def flatMap[T2, E2 <: Exception](fn: T => Result[T2, E2]): Result[T2, E with E2] = try { val res = fn(answer) val probs = res.errors ++ errors Result[T2, E with E2](res.get, probs) } catch { case e: NullPointerException if errors.nonEmpty => Errata[T2, E with E2](errors) case e: Throwable => Unforeseen[T2, E with E2](e) } def map[T2](fn: T => T2) = Result[T2, E](fn(answer), errors) def resolve[E2, T2 >: T](handlers: Each[E2, T2]*)(implicit ev: E2 <:< E): Resolved[T2, Nothing] = this match { case Unforeseen(e) => Unforeseen[T2, Nothing](e) case Answer(a) => Answer[T2, Nothing](a) case Errata((t, (_, err)) +: _) => Answer[T2, Nothing](handlers.find { case Each(fn, ct) => ct == t }.get.fn(err.asInstanceOf[E2])) } def reconcile[E2, E3 <: Exception](handlers: Each[E2, E3]*) = { val hs = handlers.map { case Each(e, typ) => typ -> e }.toMap[ClassTag[_], E2 => E3] errors.map { case (t, (p, e)) => hs(t)(e.asInstanceOf[E2]) } } /** Return `true` if this result contains errors. */ def isErrata: Boolean = this match { case Errata(_) => true case _ => false } /** Return `true` if this result is an Answer. */ def isAnswer: Boolean = this match { case Answer(_) => true case _ => false } /** Return `true` if this result is Unforeseen. */ def isUnforeseen: Boolean = this match { case Unforeseen(_) => true case _ => false } /** Catamorphism. Run the first given function if answer, otherwise, the second given function over the errata. */ def fold[X](l: T => X, r: Seq[(ClassTag[_], (String, Exception))] => X): X = this match { case Answer(a) => l(a) case Errata(e) => r(e) case Unforeseen(e) => throw e } /** Return `true` if this result is an answer satisfying the given predicate. */ def exists(p: T => Boolean): Boolean = this match { case Answer(b) => p(b) case _ => false } /** Return `true` if this result is an errata or the answer satisfies the given predicate. */ def forall(p: T => Boolean): Boolean = this match { case Answer(b) => p(b) case _ => true } /** Return a collection containing -- if the result was successful -- the answer. */ def to[Col[_]](implicit cbf: CanBuildFrom[Nothing, T, Col[T @uncheckedVariance]]): Col[T @uncheckedVariance] = this match { case Answer(ans) => val builder = cbf() builder += ans builder.result case _ => cbf().result } /** Return `None` or a `Some` of the answer. Useful to sweep errors under the carpet. */ def toOption: Option[T] = this match { case Answer(b) => Some(b) case _ => None } /** Convert to a core `scala.Either` at your own peril. blows up if an unforeseen exception is found */ def toEither: Either[Seq[(ClassTag[_], (String, Exception))], T] = this match { case Answer(b) => Right(b) case Errata(a) => Left(a) case Unforeseen(e) => throw e } /** Return the answer of this result or the given default if errata. Alias for `|` */ def getOrElse[T2 >: T](x: => T2): T2 = this match { case Answer(b) => b case _ => x } /** Return the answer value of this result or the given default if errata. Alias for `getOrElse` */ def |[T2 >: T](x: => T2): T2 = getOrElse(x) /** Return the answer of this result or run the given function on the errata. */ def valueOr[T2 >: T](x: Seq[(ClassTag[_], (String, Exception))] => T2): T2 = this match { case Answer(b) => b case Errata(a) => x(a) case Unforeseen(e) => throw e } /** Filter on the answer of this result. */ def filter(p: T => Boolean): Result[T, E with NotMatchingFilter] = this match { case Answer(b) => val t = this.get if (p(b)) Answer(t) else Errata[T, E with NotMatchingFilter]( Seq((implicitly[ClassTag[NotMatchingFilter]], ("", NotMatchingFilter(t))))) case Errata(e) => Errata[T, E with NotMatchingFilter](e) case Unforeseen(e) => Unforeseen[T, E with NotMatchingFilter](e) } /** Alias for filter */ def withFilter(p: T => Boolean): Result[T, E with NotMatchingFilter] = filter(p) } object Resolved { def unapply[T, E <: Exception](res: Result[T, E]): Option[(T, Option[Throwable])] = Some(res.answer -> res.unforeseen) def apply[T, E <: Exception](answer: T, unforeseen: Option[E]) = if (unforeseen.isEmpty) Answer(answer) else Unforeseen(unforeseen.get) } sealed abstract class Resolved[+T, E <: Exception](answer: T, unforeseen: Option[Throwable]) extends Result[T, E](answer, Seq(), unforeseen) { override def equals(that: Any) = that match { case that: Resolved[_, _] => that.answer == answer && that.unforeseen == unforeseen case _ => false } override def hashCode = answer.hashCode ^ unforeseen.hashCode } case class Answer[T, E <: Exception](override val answer: T) extends Resolved[T, E](answer, None) case class Errata[T, E <: Exception](override val errors: Seq[(ClassTag[_], (String, Exception))]) extends Result[T, E](null.asInstanceOf[T], errors) { override def toString = "Errata(\n " + errors.map { case (t, (p, e)) => s"$t: ${e.getMessage} [$p]" }.mkString(",\n ") + "\n)" } object Errata { def apply[T, E <: Exception](e: => E)(implicit classTag: ClassTag[E]): Result[T, E] = Errata(Vector((?[ClassTag[E]], ("", e)))) } case class Unforeseen[T, E <: Exception](e: Throwable) extends Resolved[T, E](null.asInstanceOf[T], Some(e)) case class AbortException() extends Exception private[core] class ReturnResultMode[+Group <: MethodConstraint] extends Mode[Group] { type Wrap[+R, E <: Exception] = Result[R, E] def wrap[R, E <: Exception](blk: => R): Result[R, E] = { try { val res = blk Result[R, E](res, accumulated) } catch { case AbortException() => Result[R, E](null.asInstanceOf[R], accumulated) case e: Throwable => if (accumulated.isEmpty) Unforeseen[R, E](e) else Errata(accumulated) } } private var accumulated: Vector[(ClassTag[_], (String, Exception))] = Vector() override def exception[T, E <: Exception: ClassTag](e: E, continue: Boolean = true): T = { accumulated :+= ((?[ClassTag[E]], (callPath, e))) if (continue) null.asInstanceOf[T] else throw AbortException() } override def catching[E <: Exception: ClassTag, T](blk: => T) = try blk catch { case e: E => exception(e) case e: Exception => throw e } override def flatWrap[R, E <: Exception: ClassTag](blk: => Wrap[R, E]): Wrap[R, E] = blk def unwrap[Return](value: => Wrap[Return, _ <: Exception]): Return = value match { case Answer(a) => a case Errata(xs) => null.asInstanceOf[Return] case Unforeseen(e) => throw e case _ => ??? } override def toString = "[modes.returnResult]" } case class Each[-E, +T](fn: E => T, classTag: ClassTag[_]) case class EachUnapplied[E]() { def apply[R](fn: E => R)(implicit classTag: ClassTag[E]): Each[E, R] = Each(fn, classTag) } case class NotMatchingFilter(value: Any) extends Exception(s"value '$value' did not match filter") ================================================ FILE: core/shared/src/main/scala/rapture/core/serializer.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.core package decimalFormats { object to0dp { def apply() = implicitDecimalFormat implicit val implicitDecimalFormat: DecimalFormat = DecimalPlaces(0) } object to1dp { def apply() = implicitDecimalFormat implicit val implicitDecimalFormat: DecimalFormat = DecimalPlaces(1) } object to2dp { def apply() = implicitDecimalFormat implicit val implicitDecimalFormat: DecimalFormat = DecimalPlaces(2) } object to3dp { def apply() = implicitDecimalFormat implicit val implicitDecimalFormat: DecimalFormat = DecimalPlaces(3) } object to4dp { def apply() = implicitDecimalFormat implicit val implicitDecimalFormat: DecimalFormat = DecimalPlaces(4) } object to5dp { def apply() = implicitDecimalFormat implicit val implicitDecimalFormat: DecimalFormat = DecimalPlaces(5) } object to6dp { def apply() = implicitDecimalFormat implicit val implicitDecimalFormat: DecimalFormat = DecimalPlaces(6) } object to1sf { def apply() = implicitDecimalFormat implicit val implicitDecimalFormat: DecimalFormat = SignificantFigures(1) } object to2sf { def apply() = implicitDecimalFormat implicit val implicitDecimalFormat: DecimalFormat = SignificantFigures(2) } object to3sf { def apply() = implicitDecimalFormat implicit val implicitDecimalFormat: DecimalFormat = SignificantFigures(3) } object to4sf { def apply() = implicitDecimalFormat implicit val implicitDecimalFormat: DecimalFormat = SignificantFigures(4) } object to5sf { def apply() = implicitDecimalFormat implicit val implicitDecimalFormat: DecimalFormat = SignificantFigures(5) } object to6sf { def apply() = implicitDecimalFormat implicit val implicitDecimalFormat: DecimalFormat = SignificantFigures(6) } object exact { def apply() = implicitDecimalFormat implicit val implicitDecimalFormat: DecimalFormat = ExactDecimal } } package integerFormats { object exact { def apply() = implicitIntegerFormat implicit val implicitIntegerFormat: IntegerFormat = ExactInteger } object to1sf { def apply() = implicitIntegerFormat implicit val implicitIntegerFormat: IntegerFormat = IntegerSignificantFigures(1) } object to2sf { def apply() = implicitIntegerFormat implicit val implicitIntegerFormat: IntegerFormat = IntegerSignificantFigures(2) } object to3sf { def apply() = implicitIntegerFormat implicit val implicitIntegerFormat: IntegerFormat = IntegerSignificantFigures(3) } object to4sf { def apply() = implicitIntegerFormat implicit val implicitIntegerFormat: IntegerFormat = IntegerSignificantFigures(4) } object to5sf { def apply() = implicitIntegerFormat implicit val implicitIntegerFormat: IntegerFormat = IntegerSignificantFigures(5) } object to6sf { def apply() = implicitIntegerFormat implicit val implicitIntegerFormat: IntegerFormat = IntegerSignificantFigures(6) } } package booleanRepresentations { object trueFalse { def apply() = implicitBooleanRepresentation implicit val implicitBooleanRepresentation: BooleanRepresentation = BooleanRepresentation("true", "false") } object digital { def apply() = implicitBooleanRepresentation implicit val implicitBooleanRepresentation: BooleanRepresentation = BooleanRepresentation("1", "0") } object yesNo { def apply() = implicitBooleanRepresentation implicit val implicitBooleanRepresentation: BooleanRepresentation = BooleanRepresentation("yes", "no") } object onOff { def apply() = implicitBooleanRepresentation implicit val implicitBooleanRepresentation: BooleanRepresentation = BooleanRepresentation("on", "off") } } object BooleanRepresentation { implicit val defaultBooleanRepresentation: BooleanRepresentation = BooleanRepresentation("true", "false") } case class BooleanRepresentation(trueValue: String, falseValue: String) object DecimalFormat { implicit val defaultRounding: DecimalFormat = SignificantFigures(4) } trait DecimalFormat { def format(bigDecimal: BigDecimal): String } case class DecimalPlaces(n: Int) extends DecimalFormat { def format(bigDecimal: BigDecimal): String = { val integral = bigDecimal.toBigInt.toString.length bigDecimal.round(new java.math.MathContext(integral + n)).setScale(n).toString } } case class SignificantFigures(n: Int) extends DecimalFormat { def format(bigDecimal: BigDecimal) = bigDecimal.round(new java.math.MathContext(n)).toString } case object ExactDecimal extends DecimalFormat { def format(bigDecimal: BigDecimal) = bigDecimal.toString } object IntegerFormat { implicit val defaultRounding: IntegerFormat = ExactInteger } trait IntegerFormat { def format(bigInt: BigInt): String } case object ExactInteger extends IntegerFormat { def format(bigInt: BigInt) = bigInt.toString } case class IntegerSignificantFigures(n: Int) extends IntegerFormat { def format(bigInt: BigInt) = BigDecimal(bigInt).round(new java.math.MathContext(n)).toString } object StringSerializer { def apply[T](f: T => String): StringSerializer[T] = new StringSerializer[T]{ override def serialize(ser: T): String = f(ser) } implicit def booleanSerializer(implicit bs: BooleanRepresentation): StringSerializer[Boolean] = StringSerializer{s => if (s) bs.trueValue else bs.falseValue} implicit val charSerializer: StringSerializer[Char] = StringSerializer(_.toString) implicit def byteSerializer(implicit df: IntegerFormat): StringSerializer[Byte] = StringSerializer{s => df.format(BigInt(s)) } implicit def shortSerializer(implicit df: IntegerFormat): StringSerializer[Short] = StringSerializer{s => df.format(BigInt(s)) } implicit def longSerializer(implicit df: IntegerFormat): StringSerializer[Long] = StringSerializer{s => df.format(BigInt(s)) } implicit def intSerializer(implicit df: IntegerFormat): StringSerializer[Int] = StringSerializer{s => df.format(BigInt(s)) } implicit val stringSerializer: StringSerializer[String] = StringSerializer(identity) implicit def doubleSerializer(implicit df: DecimalFormat): StringSerializer[Double] = StringSerializer{s => df.format(BigDecimal(s)) } implicit def floatSerializer(implicit df: DecimalFormat): StringSerializer[Float] = StringSerializer{f => df.format(BigDecimal(f.toDouble)) } implicit def bigDecimalSerializer(implicit df: DecimalFormat): StringSerializer[BigDecimal] = StringSerializer{s => df.format(s) } implicit def bigIntSerializer(implicit df: IntegerFormat): StringSerializer[BigInt] = StringSerializer{s => df.format(s) } } /** A generic string serializer */ @implicitNotFound( "It is not possible to serialize a value of type $"+"{T} to a String without a" + " valid StringSerializer instance in scope.") trait StringSerializer[-T] { stringSerializer => def serialize(string: T): String def contramap[S](fn: S => T): StringSerializer[S] = new StringSerializer[S] { def serialize(string: S): String = stringSerializer.serialize(fn(string)) } } object String { def apply[T: StringSerializer](t: T): String = ?[StringSerializer[T]].serialize(t) // Proxied from java.lang.String def format(str: String, any: AnyRef*) = java.lang.String.format(str, any: _*) def format(locale: java.util.Locale, str: String, any: AnyRef*) = java.lang.String.format(locale, str, any: _*) def copyValueOf(arr: Array[Char]): String = java.lang.String.copyValueOf(arr) def vauleOf(x: Array[Char]): String = java.lang.String.valueOf(x) def vauleOf(x: Boolean): String = java.lang.String.valueOf(x) def vauleOf(x: Double): String = java.lang.String.valueOf(x) def vauleOf(x: Int): String = java.lang.String.valueOf(x) def vauleOf(x: Any): String = java.lang.String.valueOf(x) def vauleOf(x: Array[Char], a: Int, b: Int): String = java.lang.String.valueOf(x, a, b) def vauleOf(x: Char): String = java.lang.String.valueOf(x) def vauleOf(x: Float): String = java.lang.String.valueOf(x) def vauleOf(x: Long): String = java.lang.String.valueOf(x) val CASE_INSENSITIVE_ORDER = java.lang.String.CASE_INSENSITIVE_ORDER } ================================================ FILE: core/shared/src/main/scala/rapture/core/threads.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.core import java.lang.{ClassLoader => JClassLoader, Thread => JThread} object ClasspathUrlItem { implicit def toClasspathUrlItem[T: ClasspathUrlable](t: T): ClasspathUrlItem = ?[ClasspathUrlable[T]].toClasspathUrlItem(t) } case class ClasspathUrlItem(javaUrl: List[java.net.URL]) object ClasspathUrlable { implicit def seqUrlable[T](implicit urlable: ClasspathUrlable[T]): ClasspathUrlable[List[T]] = new ClasspathUrlable[List[T]] { def toClasspathUrlItem(xs: List[T]): ClasspathUrlItem = ClasspathUrlItem(xs.flatMap(urlable.toClasspathUrlItem(_).javaUrl)) } } trait ClasspathUrlable[T] { def toClasspathUrlItem(t: T): ClasspathUrlItem } object ClassLoader { implicit def defaultClassLoader: ClassLoader = new ClassLoader(JThread.currentThread.getContextClassLoader) def apply(urls: ClasspathUrlItem*): ClassLoader = new ClassLoader(new java.net.URLClassLoader(urls.flatMap(_.javaUrl).to[Array])) } class ClassLoader(val javaClassLoader: JClassLoader) { def applyTo[T](blk: => T): T = { val cur = java.lang.Thread.currentThread().getContextClassLoader java.lang.Thread.currentThread().setContextClassLoader(javaClassLoader) val result = blk java.lang.Thread.currentThread().setContextClassLoader(cur) result } } object Thread { def fork(threadName: String, daemon: Boolean = false)(blk: => Unit)(implicit cl: ClassLoader): Thread = ThreadSpec(threadName, daemon)(blk).spawn() def sleep[D: TimeSystem.ByDuration](duration: D) = JThread.sleep(?[TimeSystem.ByDuration[D]].fromDuration(duration)) } case class ThreadSpec(name: String, daemon: Boolean = false)(blk: => Unit)(implicit cl: ClassLoader) { def spawn(): Thread = { val parentThread = JThread.currentThread val javaThread = new JThread(name) { override def run() = { blk parentThread.join() } } javaThread.setDaemon(daemon) javaThread.setContextClassLoader(cl.javaClassLoader) javaThread.start() new Thread(this, javaThread) { def parentAlive = javaThread.isAlive } } } abstract class Thread(spec: ThreadSpec, javaThread: JThread) { def daemon: Boolean = spec.daemon def name: String = spec.name def alive: Boolean = javaThread.isAlive def interrupt(): Unit = javaThread.interrupt() def join(): Unit = javaThread.join() def priority = javaThread.getPriority def priority_=(p: Int) = javaThread.setPriority(p) override def toString = s"[$name]" } ================================================ FILE: core/shared/src/main/scala/rapture/core/time.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.core object TimeSystem { type ByInstant[T] = TimeSystem[T, _] type ByDuration[T] = TimeSystem[_, T] } @implicitNotFound( "an implicit TimeSystem is required; please import timeSystems.numeric._ or " + "timeSystems.javaUtil._") trait TimeSystem[Instant, Duration] { def instant(millis: Long): Instant def duration(from: Long, to: Long): Duration def fromInstant(inst: Instant): Long def fromDuration(dur: Duration): Long } package timeSystems { object numeric { def apply(): TimeSystem[Long, Long] = timeSystemImplicit implicit val timeSystemImplicit: TimeSystem[Long, Long] = new TimeSystem[Long, Long] { def instant(millis: Long): Long = millis def duration(from: Long, to: Long): Long = to - from def fromInstant(inst: Long): Long = inst def fromDuration(dur: Long): Long = dur } } object javaUtil { def apply(): TimeSystem[java.util.Date, Long] = timeSystemImplicit implicit val timeSystemImplicit = new TimeSystem[java.util.Date, Long] { import java.util.Date def instant(millis: Long) = new Date(millis) def duration(from: Long, to: Long): Long = to - from def fromInstant(inst: Date): Long = inst.getTime def fromDuration(dur: Long): Long = dur } } } ================================================ FILE: core-scalaz/shared/src/main/scala/rapture/core-scalaz/modes.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.core.scalazInterop import rapture.core._ import java.util.concurrent.ExecutorService import scalaz._ import scalaz.concurrent._ class ReturnTasks[+Group <: MethodConstraint](implicit pool: ExecutorService) extends Mode[Group] { type Wrap[+T, E <: Exception] = Task[T] def wrap[T, E <: Exception](t: => T): Task[T] = Task.delay(t) def unwrap[T](t: => Wrap[T, _ <: Exception]): T = t.unsafePerformSyncAttempt.valueOr { throw _ } } class ReturnValidation[+Group <: MethodConstraint] extends Mode[Group] { type Wrap[+T, E <: Exception] = Validation[E, T] def wrap[T, E <: Exception](t: => T): Validation[E, T] = try Success(t) catch { case e: Exception => Failure(e.asInstanceOf[E]) } def unwrap[T](t: => Validation[_ <: Exception, T]): T = t.valueOr { throw _ } } class ReturnDisjunction[+Group <: MethodConstraint] extends Mode[Group] { type Wrap[+T, E <: Exception] = \/[E, T] def wrap[T, E <: Exception](t: => T): \/[E, T] = try \/-(t) catch { case e: Exception => -\/(e.asInstanceOf[E]) } def unwrap[T](t: => \/[_ <: Exception, T]): T = t.valueOr { throw _ } } class ScalazExplicits[+T, E <: Exception](explicit: modes.Explicitly[T, E]) { def task(implicit pool: ExecutorService): Task[T] = returnTasks.wrap(explicit.get) def validation: Validation[E, T] = returnValidations.wrap(explicit.get) } object `package` { implicit def scalazExplicits[T, E <: Exception](explicit: modes.Explicitly[T, E]): ScalazExplicits[T, E] = new ScalazExplicits[T, E](explicit) implicit def returnTasks[Group <: MethodConstraint](implicit pool: ExecutorService) = new ReturnTasks[Group] // FIXME: This should be modified to collect multiple failures implicit def returnValidations[Group <: MethodConstraint] = new ReturnValidation[Group] implicit def returnDisjunction[Group <: MethodConstraint] = new ReturnDisjunction[Group] } ================================================ FILE: core-scalaz/shared/src/main/scala/rapture/core-scalaz/transformers.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.core.scalazInterop import rapture.core.{Errata, NotMatchingFilter, Result} import scala.reflect.ClassTag import scalaz.{Functor, _} import language.higherKinds /** * ResultT monad transformer * * Represents a computation of type `Result[A,B]`. * * Example: * {{{ * val x: Option[Result[String, E]] = Some(Answer(1)) * ResultT(x).map(1+).run // Some(Answer(2)) * }}} **/ sealed trait ResultT[F[_], T, E <: Exception] { val run: F[Result[T, E]] /** Map on the answer of this result. */ def map[C](f: T => C)(implicit functor: Functor[F], cte: ClassTag[E]): ResultT[F, C, E] = ResultT(functor.map(run)(_.map(f))) /** Bind through the answer of this result accumulating errors in the contents and type signature. */ def flatMap[C, E2 <: Exception](f: T => ResultT[F, C, E2])(implicit monad: Monad[F], cte: ClassTag[E2]): ResultT[F, C, E with E2] = { ResultT(monad.bind[Result[T, E], Result[C, E with E2]](run) { result => result.fold[F[Result[C, E with E2]]]({ a => monad.map(f(a).run)(r2 => Result[C, E with E2](r2.get, r2.errors ++ result.errors)) }, { e => monad.point(Errata[C, E with E2](e)) }) }) } /** Filter on the answer of this result. */ def filter(p: T => Boolean)(implicit functor: Functor[F], cte: ClassTag[E]): ResultT[F, T, E with NotMatchingFilter] = ResultT(functor.map(run)(_.filter(p))) /** Alias for `filter` */ def withFilter(p: T => Boolean)(implicit functor: Functor[F], cte: ClassTag[E]): ResultT[F, T, E with NotMatchingFilter] = filter(p) } object ResultT extends ResultTFunctions { /** Construct a result value. */ def apply[F[_], T, E <: Exception: ClassTag](a: F[Result[T, E]]): ResultT[F, T, E] = resultT[F, T, E](a) /** Construct an answer value. */ def answer[F[_], T, E <: Exception: ClassTag](a: F[T])(implicit functor: Functor[F]): ResultT[F, T, E] = apply[F, T, E](functor.map(a)(Result.answer[T, E])) /** Construct an errata value. */ def errata[F[_], T, E <: Exception: ClassTag](a: F[E])(implicit functor: Functor[F]): ResultT[F, T, E] = apply[F, T, E](functor.map(a)(Result.errata[T, E])) } private[scalazInterop] trait ResultTFunctions { def resultT[F[_], T, E <: Exception](a: F[Result[T, E]]): ResultT[F, T, E] = new ResultT[F, T, E] { val run = a } } ================================================ FILE: core-test/shared/src/test/scala/rapture/core/tests.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.core.test import rapture.core._ import rapture.core.scalazInterop.ResultT import rapture.test._ import scalaz.Scalaz._ class TestRun extends Programme { include(CoreTests) } object CoreTests extends TestSuite { case class AlphaException() extends Exception case class BetaException() extends Exception case class MiscException() extends Exception def alpha(x: Int)(implicit mode: Mode[_]): mode.Wrap[Int, AlphaException] = mode.wrap { if(x == 0) mode.exception(AlphaException()) else if(x == 1) throw MiscException() else 0 } def beta(x: Int)(implicit mode: Mode[_]): mode.Wrap[Int, BetaException] = mode.wrap { if(x == 0) mode.exception(BetaException()) else if(x == 1) throw MiscException() else 0 } val `Successful Result` = test { import modes.returnResult._ alpha(2) } returns Answer(0) val `Unforeseen Result` = test { import modes.returnResult._ alpha(1) } returns Unforeseen(MiscException()) val `Expected error Result` = test { import modes.returnResult._ alpha(0) } satisfies { case Errata(_) => true case _ => false } val `FlatMapped Successful Result` = test { import modes.returnResult._ for { a <- alpha(2) b <- beta(2) } yield a + b } returns Answer(0) val `FlatMapped first fails` = test { import modes.returnResult._ for { a <- alpha(0) b <- beta(2) } yield a + b } satisfies (_.exceptions == Vector(AlphaException())) val `FlatMapped second fails` = test { import modes.returnResult._ for { a <- alpha(2) b <- beta(0) } yield a + b } satisfies (_.exceptions == Vector(BetaException())) val `Resolving errata 1` = test { import modes.returnResult._ val result = for(a <- alpha(2); b <- beta(0)) yield a + b result.resolve( each[AlphaException] { e => 10 }, each[BetaException] { e => 20 } ) } returns Answer(20) val `Resolving errata 2` = test { import modes.returnResult._ val result = for(a <- alpha(0); b <- beta(2)) yield a + b result.resolve( each[AlphaException] { e => 10 }, each[BetaException] { e => 20 } ) } returns Answer(10) val `Catching success` = test { Result.catching[AlphaException] { "success" } } returns Answer("success") val `Catching failure` = test { Result.catching[AlphaException] { throw AlphaException() } } satisfies (_.exceptions == Vector(AlphaException())) val `Catching unforeseen` = test { Result.catching[AlphaException] { throw BetaException() } } returns Unforeseen(BetaException()) val `Checking isErrata with errata` = test { Result.errata(AlphaException()).isErrata } returns true val `Checking isErrata with answer` = test { Result.answer(1).isErrata } returns false val `Checking isAnswer with errata` = test { Result.errata(AlphaException()).isAnswer } returns false val `Checking isAnswer with answer` = test { Result.answer(1).isAnswer } returns true val `Checking isUnforeseen with answer` = test { Result.answer(1).isUnforeseen } returns false val `Checking isUnforeseen with errata` = test { Result.errata(AlphaException()).isUnforeseen } returns false val `Checking isUnforeseen with unforeseen` = test { Result.catching[AlphaException] { throw BetaException() }.isUnforeseen } returns true val `Fold answer` = test { Result.answer(1).fold( a => a + 1, e => 0 ) } returns 2 val `Fold errata` = test { Result.errata[Int, AlphaException](AlphaException()).fold( a => a + 1, e => 0 ) } returns 0 val `Exists answer` = test { Result.answer(1).exists(_ == 1) } returns true val `Exists answer none found` = test { Result.answer(1).exists(_ == 0) } returns false val `Exists errata` = test { Result.errata[Int, AlphaException](AlphaException()).exists(_ == 1) } returns false val `Forall answer` = test { Result.answer(1).forall(_ == 1) } returns true val `Forall answer none found` = test { Result.answer(1).forall(_ == 0) } returns false val `Forall errata` = test { Result.errata[Int, AlphaException](AlphaException()).forall(_ == 1) } returns true val `toList answer` = test { Result.answer(1).to[List] } returns List(1) val `toList errata` = test { Result.errata[Int, AlphaException](AlphaException()).to[List] } returns Nil val `toStream answer` = test { Result.answer(1).to[Stream] } returns Stream(1) val `toStream errata` = test { Result.errata[Int, AlphaException](AlphaException()).to[Stream] } returns Stream.empty[Int] val `toOption answer` = test { Result.answer(1).toOption } returns Some(1) val `toOption errata` = test { Result.errata[Int, AlphaException](AlphaException()).toOption } returns None val `toEither answer` = test { Result.answer(1).toEither } returns Right(1) val `toEither errata` = test { Result.errata[Int, AlphaException](AlphaException()).toEither } satisfies (v => v.isLeft) val `getOrElse answer` = test { Result.answer(1).getOrElse(0) } returns 1 val `getOrElse errata` = test { Result.errata[Int, AlphaException](AlphaException()).getOrElse(0) } returns 0 val `| answer` = test { Result.answer(1) | 0 } returns 1 val `| errata` = test { Result.errata[Int, AlphaException](AlphaException()) | 0 } returns 0 val `valueOr answer` = test { Result.answer(1).valueOr(_ => 0) } returns 1 val `valueOr errata` = test { Result.errata[Int, AlphaException](AlphaException()).valueOr(_ => 0) } returns 0 val `filter answer` = test { Result.answer(1) filter (_ == 1) } returns Answer(1) /*val `filter answer 2` = test { Result.answer(1) filter (_ == 0) } returns Errata(Nil)*/ val `filter errata` = test { Errata[String, Nothing](Nil) filter (_.isEmpty) } returns Errata(Nil) /*val `withFilter errata monadic` = test { for { x <- Answer(1) if x == 0 y = x + 1 } yield y } returns Errata(Nil)*/ val `withFilter answer monadic` = test { for { x <- Answer(1) if x == 1 y = x + 1 } yield y } returns Answer(2) val `ResultT Checking isErrata with errata` = test { ResultT.errata(Option(AlphaException())).run.get.isErrata } returns true val `ResultT Checking isErrata with answer` = test { ResultT.answer(Option(1)).run.get.isErrata } returns false val `ResultT Checking isAnswer with errata` = test { ResultT.errata(Option(AlphaException())).run.get.isAnswer } returns false val `ResultT Checking isAnswer with answer` = test { ResultT.answer(Option(1)).run.get.isAnswer } returns true val `ResultT Checking isUnforeseen with answer` = test { ResultT.answer(Option(1)).run.get.isUnforeseen } returns false val `ResultT Checking isUnforeseen with errata` = test { ResultT.errata(Option(AlphaException())).run.get.isUnforeseen } returns false val `ResultT withFilter accumulating exceptions` = test { val z: ResultT[Option, Int, NumberFormatException with IllegalArgumentException] = for { x <- ResultT(Option(Result.catching[NumberFormatException]("1".toInt))) y <- ResultT(Option(Result.catching[IllegalArgumentException]("1".toInt))) } yield x + y z.run.get.get } returns 2 } ================================================ FILE: crypto/shared/src/main/scala/rapture/crypto/aes.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.crypto import rapture.core._ import javax.crypto.spec._ import java.util._ import digests._ trait `AesEncryption#decrypt` extends MethodConstraint trait `Key#decrypt` extends MethodConstraint /** Provides a simple interface for AES encryption with SHA-256 digest * verification. This class is stateless. */ abstract class AesEncryption { /** Must be 16, 24 or 32 bytes long. */ protected def secretKey: Array[Byte] private val keySpec = new SecretKeySpec(secretKey, "AES") def encrypt(clearText: Array[Byte], iv: Array[Byte] = null): Array[Byte] = { val cipher = javax.crypto.Cipher.getInstance("AES/CBC/PKCS5Padding") if (iv == null) cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, keySpec) else cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(iv)) val digest = Hash.digest[Sha256](clearText).bytes val paddedLength = (clearText.length >> 4) + 1 << 4 val cipherText = new Array[Byte](paddedLength + (if (iv == null) 48 else 0)) if (iv == null) { Array.copy(cipher.getIV, 0, cipherText, 0, 16) cipher.update(digest, 0, 32, cipherText, 16) } cipher.doFinal(clearText, 0, clearText.length, cipherText, if (iv == null) 48 else 0) cipherText } def decrypt(cipherText: Array[Byte], iv: Array[Byte] = null)( implicit mode: Mode[`AesEncryption#decrypt`]): mode.Wrap[Array[Byte], DecryptionException] = mode.wrap { if (iv == null && cipherText.length < 48) mode.exception(DecryptionException()) val cipher = javax.crypto.Cipher.getInstance("AES/CBC/PKCS5Padding") val ips = if (iv == null) new IvParameterSpec(cipherText, 0, 16) else new IvParameterSpec(iv) cipher.init(javax.crypto.Cipher.DECRYPT_MODE, keySpec, ips) val n = if (iv == null) 64 else 0 val digest1 = if (iv == null) cipher.update(cipherText, 16, 48) else Array[Byte]() val clearText = cipher.doFinal(cipherText, n, cipherText.length - n) if (iv == null) { val digest2 = Hash.digest[Sha256](clearText).bytes var i = 0 var r = true while (i < 32) { if (digest1(i) != digest2(i)) r = false i += 1 } if (!r) { Arrays.fill(digest1, 0.toByte) Arrays.fill(digest2, 0.toByte) Arrays.fill(clearText, 0.toByte) mode.exception(DecryptionException()) } } clearText } def apply(clearText: Array[Byte]): Array[Byte] = encrypt(clearText) def unapply(cipherText: Array[Byte]): Option[Array[Byte]] = try Some(decrypt(cipherText)) catch { case DecryptionException() => None } } ================================================ FILE: crypto/shared/src/main/scala/rapture/crypto/digest.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.crypto import rapture.core._ import rapture.codec._ import java.security._ import javax.crypto.Mac trait DigestType trait Sha1 extends DigestType trait Sha256 extends DigestType trait Sha384 extends DigestType trait Sha512 extends DigestType trait Md2 extends DigestType trait Md5 extends DigestType class Digest[T <: DigestType](bytes: Array[Byte]) extends Bytes(bytes) package ciphers { object des { implicit def desGenerator: KeyGenerator[Des] = Des.keyGenerator implicit def desDecryption = Des.decryption implicit def desEncryption = Des.encryption } object blowfish { implicit def blowfishGenerator: KeyGenerator[Blowfish] = Blowfish.keyGenerator implicit def blowfishDecryption = Blowfish.decryption implicit def blowfishEncryption = Blowfish.encryption } object aes { implicit def aesGenerator: KeyGenerator[Aes] = Aes.keyGenerator implicit def aesDecryption = Aes.decryption implicit def aesEncryption = Aes.encryption } } class EncryptedData[C <: CipherType](bytes: Array[Byte]) extends Bytes(bytes) object Hash { def digest[D <: DigestType: Digester](msg: Bytes): Digest[D] = new Digest[D](?[Digester[D]].digest(msg.bytes)) } object Digester { implicit val sha1: Digester[Sha1] = digests.sha1 implicit val sha256: Digester[Sha256] = digests.sha256 implicit val sha512: Digester[Sha512] = digests.sha512 implicit val sha384: Digester[Sha384] = digests.sha384 implicit val md5: Digester[Md5] = digests.md5 implicit val md2: Digester[Md2] = digests.md2 } abstract class Digester[D <: DigestType] { /** Digests the array of bytes. */ def digest(msg: Array[Byte]): Array[Byte] } case class Salt(value: String) object Password { def apply(value: String)(implicit salt: Salt) = new HashedPassword(value)(salt) } class Password(private val value: String)(implicit salt: Salt) { def digest: String = Bytes(Digester.sha256.digest((value + salt).getBytes("UTF-8"))).encode[Hex] override def toString = s"password:$digest" def check(password: String) = new Password(password).digest == digest } class HashedPassword(hash: String)(implicit salt: Salt) extends Password(null)(salt) { override def digest: String = hash } object digests { implicit val sha1: Digester[Sha1] = new Digester[Sha1] { def digest(msg: Array[Byte]): Array[Byte] = MessageDigest.getInstance("SHA-1").digest(msg) } /** SHA-256 digester, with additional methods for secure password encoding. */ implicit val sha256: Digester[Sha256] = new Digester[Sha256] { /** Digests the given bytes. */ def digest(msg: Array[Byte]): Array[Byte] = MessageDigest.getInstance("SHA-256").digest(msg) } /** SHA-512 digester, with additional methods for secure password encoding. */ implicit val sha512: Digester[Sha512] = new Digester[Sha512] { def digest(msg: Array[Byte]): Array[Byte] = MessageDigest.getInstance("SHA-512").digest(msg) } /** SHA-384 digester, with additional methods for secure password encoding. */ implicit val sha384: Digester[Sha384] = new Digester[Sha384] { def digest(msg: Array[Byte]): Array[Byte] = MessageDigest.getInstance("SHA-384").digest(msg) } /** MD5 Digester. This is included for backwards compatibility. MD5 is no * longer considered future-proof and new designs should prefer SHA-256. */ implicit val md5: Digester[Md5] = new Digester[Md5] { def digest(msg: Array[Byte]): Array[Byte] = MessageDigest.getInstance("MD5").digest(msg) } implicit val md2: Digester[Md2] = new Digester[Md2] { def digest(msg: Array[Byte]): Array[Byte] = MessageDigest.getInstance("MD2").digest(msg) } } trait CipherType trait Blowfish extends CipherType class JavaxCryptoImplementations[Codec <: CipherType](codec: String) { implicit def encryption: Encryption[Codec, Bytes] = new Encryption[Codec, Bytes] { def encrypt(key: Array[Byte], message: Bytes) = { val cipher = javax.crypto.Cipher.getInstance(codec) cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, new javax.crypto.spec.SecretKeySpec(key, codec)) cipher.doFinal(message.bytes) } } implicit def decryption = new Decryption[Codec] { def decrypt(key: Array[Byte], message: Array[Byte]) = { val cipher = javax.crypto.Cipher.getInstance(codec) cipher.init(javax.crypto.Cipher.DECRYPT_MODE, new javax.crypto.spec.SecretKeySpec(key, codec)) cipher.doFinal(message) } } implicit def keyGenerator: KeyGenerator[Codec] = new KeyGenerator[Codec] { def generate(): Array[Byte] = { val keyGen = javax.crypto.KeyGenerator.getInstance(codec) keyGen.generateKey().getEncoded } } } trait Aes extends CipherType object Aes extends JavaxCryptoImplementations[Aes]("AES") object Des extends JavaxCryptoImplementations[Des]("DES") object Blowfish extends JavaxCryptoImplementations[Blowfish]("Blowfish") trait TripleDes extends CipherType trait Des extends CipherType trait KeyGenerator[K <: CipherType] { type KeyType = K def generate(): Array[Byte] } trait Encryption[-C <: CipherType, Msg] { def encrypt(key: Array[Byte], message: Msg): Array[Byte] } case class DecryptionException() extends Exception trait Decryption[C <: CipherType] { def decrypt(key: Array[Byte], message: Array[Byte]): Array[Byte] } object Key { def generate[K <: CipherType]()(implicit gen: KeyGenerator[K]): Key[gen.KeyType] = new Key[gen.KeyType](?[KeyGenerator[K]].generate()) def read[K <: CipherType](key: Bytes): Key[K] = new Key[K](key.bytes) } class Key[C <: CipherType](bytes: Array[Byte]) extends Bytes(bytes) { def encrypt[Msg](message: Msg)(implicit encryption: Encryption[C, Msg]): EncryptedData[C] = new EncryptedData[C](encryption.encrypt(bytes, message)) def decrypt(message: EncryptedData[C])(implicit mode: Mode[`Key#decrypt`], decryption: Decryption[C]): mode.Wrap[Bytes, DecryptionException] = mode wrap { try Bytes(decryption.decrypt(bytes, message.bytes)) catch { case e: Exception => mode.exception(DecryptionException()) } } } case class HmacSigner(key: Bytes) { type Sha256Hmac <: DigestType implicit val hmac: Digester[Sha256Hmac] = new Digester[Sha256Hmac] { def digest(msg: Array[Byte]): Array[Byte] = { val mac = Mac.getInstance("HmacSHA256") val secretKey = new javax.crypto.spec.SecretKeySpec(key.bytes, "HmacSHA256") mac.init(secretKey) mac.doFinal(msg) } } } ================================================ FILE: css/shared/src/main/scala/rapture/css/context.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.css import rapture.base._ import rapture.core._ import rapture.data._ import language.experimental.macros private[css] object CssMacros { def parseSource(s: List[String], substitutions: List[String], stylesheet: Boolean): Option[(Int, Int, String)] = try { if(stylesheet) CssParser.parseStylesheet(s, substitutions) else CssParser.parse(s, substitutions) None } catch { case CssParser.ValidationException(strNo, pos, msg) => Some((strNo, pos, s"failed to parse Css literal: $msg")) } // FIXME: Unify these three implementations, and use quasiquotes def cssClassContextMacro(c: BlackboxContext)( exprs: c.Expr[ForcedConversion[CssClass]]*): c.Expr[CssClass] = { import c.universe._ c.prefix.tree match { case Select(Apply(_, List(Apply(_, rawPart :: Nil))), _) => val Literal(Constant(className: String)) = rawPart if(!className.matches("-?[_a-zA-Z]+[_a-zA-Z0-9-]*")) c.abort(c.enclosingPosition, "this is not a valid CSS class identifier") c.Expr(q"_root_.rapture.css.CssClass(_root_.scala.collection.immutable.Set($rawPart))") } } def stylesheetContextMacro(c: BlackboxContext)( exprs: c.Expr[Embed[CssStylesheet]]*): c.Expr[CssStylesheet] = { import c.universe._ c.prefix.tree match { case Select(Apply(_, List(Apply(_, rawParts))), _) => val text = rawParts.map { case Literal(Constant(part: String)) => part } val listExprs = c.Expr[List[Embed[CssStylesheet]]](q"_root_.scala.List(..${exprs.map(_.tree).to[List]})") def resolveEmbeddableName(name: String) = name match { case "domId" => "#foo" case "css" => "color: red;" case "cssClass" => ".foo" case "int" => "1" case "double" => "1.0" case _ => "null" } val substitutions: List[String] = listExprs.tree match { case Apply(_, applications) => applications.map { case Apply(Apply(TypeApply(Select(_, _), _), _), List(Select(_, name))) => resolveEmbeddableName(name.toString) } } parseSource(text, substitutions, true).foreach { case (n, offset, msg) => val oldPos = rawParts(n).asInstanceOf[Literal].pos val newPos = oldPos.withPoint(oldPos.start + offset) c.error(newPos, msg) } val listParts = c.Expr[List[String]](q"_root_.scala.List(..$rawParts)") reify { val sb = new StringBuilder val textParts = listParts.splice.iterator val expressions: Iterator[Embed[CssStylesheet]] = listExprs.splice.iterator sb.append(textParts.next()) while (textParts.hasNext) { sb.append(expressions.next.content) sb.append(textParts.next) } CssParser.parseStylesheet(List(sb.toString), Nil) } } } def cssContextMacro(c: BlackboxContext)( exprs: c.Expr[Embed[Css]]*): c.Expr[Css] = { import c.universe._ import compatibility._ c.prefix.tree match { case Select(Apply(_, List(Apply(_, rawParts))), _) => val text = rawParts.map { case Literal(Constant(part: String)) => part } val listExprs = c.Expr[List[Embed[Css]]]( Apply( Select(reify(List).tree, termName(c, "apply")), exprs.map(_.tree).to[List] )) def resolveEmbeddableName(name: String) = name match { case "cssCss" => "color: red;" case "cssInt" => "1" case "cssDouble" => "1.0" case _ => "null" } val substitutions: List[String] = listExprs.tree match { case Apply(_, applications) => applications.map { case Apply(Apply(TypeApply(Select(_, _), _), _), List(Select(_, name))) => resolveEmbeddableName(name.toString) } } parseSource(text, substitutions, false).foreach { case (n, offset, msg) => val oldPos = rawParts(n).asInstanceOf[Literal].pos val newPos = oldPos.withPoint(oldPos.start + offset) c.error(newPos, msg) } val listParts = c.Expr[List[String]](q"_root_.scala.List(..$rawParts)") reify { val sb = new StringBuilder val textParts = listParts.splice.iterator val expressions: Iterator[Embed[Css]] = listExprs.splice.iterator sb.append(textParts.next()) while (textParts.hasNext) { sb.append(expressions.next.content) sb.append(textParts.next) } CssParser.parse(List(sb.toString), Nil) } } } } private[css] class CssStrings(sc: StringContext) { class CssContext() { def apply(exprs: Embed[Css]*): Css = macro CssMacros.cssContextMacro } class CssClassContext() { def apply(exprs: Nothing*): CssClass = macro CssMacros.cssClassContextMacro } class StylesheetContext() { def apply(exprs: Embed[CssStylesheet]*): CssStylesheet = macro CssMacros.stylesheetContextMacro } val css = new CssContext() val cls = new CssClassContext() val cssStylesheet = new StylesheetContext() } ================================================ FILE: css/shared/src/main/scala/rapture/css/css.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.css import rapture.dom._ import rapture.core._ import scala.collection.immutable.ListMap object Css { implicit def stringSerializer: StringSerializer[Css] = new StringSerializer[Css] { def serialize(css: Css): String = css.content } } case class Css(properties: ListMap[String, String]) { def content = properties.map { case (k, v) => s"$k: $v;" }.mkString(" ") override def toString = s"""css${"\"" * 3}$content${"\"" * 3}""" def +(css: Css) = Css(properties ++ css.properties) } object CssStylesheet { implicit def stringSerializer: StringSerializer[CssStylesheet] = new StringSerializer[CssStylesheet] { def serialize(css: CssStylesheet): String = css.content } } case class CssStylesheet(rules: List[CssRule]) { override def toString = s"""cssStylesheet${"\"" * 3}$content${"\"" * 3}""" def content = rules.mkString("\n") } object DomId { def auto(implicit assignedName: AssignedName) = DomId(assignedName.name) } case class DomId(id: String) { override def toString = s"#$id" } object CssClass { def auto(implicit assignedName: AssignedName) = CssClass(Set(assignedName.name)) val empty = CssClass(Set()) } case class CssClass(classes: Set[String]) { def +(cssClass: CssClass): CssClass = CssClass(classes ++ cssClass.classes) def asString = classes.mkString(" ") override def toString = classes.mkString(".", ".", "") } object CssEmbed { implicit def embedCssClass(cssClass: CssClass): CssEmbed = CssEmbed(cssClass.classes.mkString(".", ".", "")) implicit def embedDomId(domId: DomId): CssEmbed = CssEmbed(s"#${domId.id}") } case class CssEmbed(content: String) object Embeddable { implicit val domId: Embeddable[DomId, CssStylesheet] = new Embeddable[DomId, CssStylesheet] { def embed(value: DomId): String = s"#${value.id}" } implicit val cssClass: Embeddable[CssClass, CssStylesheet] = new Embeddable[CssClass, CssStylesheet] { def embed(value: CssClass): String = value.classes.mkString(".", ".", "") } implicit val domTag: Embeddable[Tag[_, _, _], CssStylesheet] = new Embeddable[Tag[_, _, _], CssStylesheet] { def embed(value: Tag[_, _, _]): String = value.tagName.toLowerCase } implicit val css: Embeddable[Css, CssStylesheet] = new Embeddable[Css, CssStylesheet] { def embed(value: Css): String = value.content } implicit val string: Embeddable[String, CssStylesheet] = new Embeddable[String, CssStylesheet] { def embed(value: String): String = value } implicit val int: Embeddable[Int, CssStylesheet] = new Embeddable[Int, CssStylesheet] { def embed(value: Int): String = value.toString } implicit val double: Embeddable[Double, CssStylesheet] = new Embeddable[Double, CssStylesheet] { def embed(value: Double): String = value.toString } implicit val cssString: Embeddable[String, Css] = new Embeddable[String, Css] { def embed(value: String): String = value } implicit val cssInt: Embeddable[Int, Css] = new Embeddable[Int, Css] { def embed(value: Int): String = value.toString } implicit val cssDouble: Embeddable[Double, Css] = new Embeddable[Double, Css] { def embed(value: Double): String = value.toString } implicit val cssCss: Embeddable[Css, Css] = new Embeddable[Css, Css] { def embed(value: Css): String = value.content } } trait Embeddable[-From, +To] { def embed(value: From): String } object Embed { implicit def embed[From, To](value: From)(implicit embeddable: Embeddable[From, To]): Embed[To] = Embed(embeddable.embed(value)) } case class Embed[To](content: String) ================================================ FILE: css/shared/src/main/scala/rapture/css/model.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.css import rapture.core._ sealed trait CssRule case class CssCharset(encoding: String) extends CssRule { override def toString = s"@charset '$encoding';" } case class CssFontFace(properties: Css) extends CssRule { override def toString = s"@font-face { ${properties.content} }" } case class CssImport(href: String, media: String) extends CssRule { override def toString = s"@import url('$href') $media;" } case class CssMedia(cssRules: List[CssRule], media: String) extends CssRule { override def toString = s"@media $media { ${cssRules.mkString(" ")} }" } case class CssPage(selectorText: String, properties: Css) extends CssRule { override def toString = s"@page $selectorText { ${properties.content} }" } case class CssStyle(selectorText: String, properties: Css) extends CssRule { override def toString = s"$selectorText { ${properties.content} }" } case class CssUnknown(text: String) extends CssRule { override def toString = text } ================================================ FILE: css/shared/src/main/scala/rapture/css/package.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.css import language.implicitConversions object `package` { implicit def cssStringContext(sc: StringContext): CssStrings = new CssStrings(sc) } ================================================ FILE: css/shared/src/main/scala/rapture/css/properties.scala ================================================ package rapture.css object Properties { // List of properties supported by Firefox val all = Set( "align-content", "align-items", "align-self", "alignment-adjust", "alignment-baseline", "anchor-point", "animation", "animation-delay", "animation-direction", "animation-duration", "animation-fill-mode", "animation-iteration-count", "animation-name", "animation-play-state", "animation-timing-function", "appearance", "azimuth", "backface-visibility", "background", "background-attachment", "background-blend-mode", "background-clip", "background-color", "background-image", "background-origin", "background-position", "background-repeat", "background-size", "baseline-shift", "binding", "bleed", "bookmark-label", "bookmark-level", "bookmark-state", "bookmark-target", "border", "border-bottom", "border-bottom-color", "border-bottom-left-radius", "border-bottom-right-radius", "border-bottom-style", "border-bottom-width", "border-collapse", "border-color", "border-image", "border-image-outset", "border-image-repeat", "border-image-slice", "border-image-source", "border-image-width", "border-left", "border-left-color", "border-left-style", "border-left-width", "border-radius", "border-right", "border-right-color", "border-right-style", "border-right-width", "border-spacing", "border-style", "border-top", "border-top-color", "border-top-left-radius", "border-top-right-radius", "border-top-style", "border-top-width", "border-width", "bottom", "box-decoration-break", "box-shadow", "box-sizing", "break-after", "break-before", "break-inside", "caption-side", "clear", "clip", "color", "color-profile", "column-count", "column-fill", "column-gap", "column-rule", "column-rule-color", "column-rule-style", "column-rule-width", "column-span", "column-width", "columns", "content", "counter-increment", "counter-reset", "crop", "cue", "cue-after", "cue-before", "cursor", "direction", "display", "dominant-baseline", "drop-initial-after-adjust", "drop-initial-after-align", "drop-initial-before-adjust", "drop-initial-before-align", "drop-initial-size", "drop-initial-value", "elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis", "flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap", "float", "float-offset", "flow-from", "flow-into", "font", "font-feature-settings", "font-family", "font-kerning", "font-language-override", "font-size", "font-size-adjust", "font-stretch", "font-style", "font-synthesis", "font-variant", "font-variant-alternates", "font-variant-caps", "font-variant-east-asian", "font-variant-ligatures", "font-variant-numeric", "font-variant-position", "font-weight", "grid", "grid-area", "grid-auto-columns", "grid-auto-flow", "grid-auto-position", "grid-auto-rows", "grid-column", "grid-column-end", "grid-column-start", "grid-row", "grid-row-end", "grid-row-start", "grid-template", "grid-template-areas", "grid-template-columns", "grid-template-rows", "hanging-punctuation", "height", "hyphens", "icon", "image-orientation", "image-rendering", "image-resolution", "inline-box-align", "justify-content", "left", "letter-spacing", "line-break", "line-height", "line-stacking", "line-stacking-ruby", "line-stacking-shift", "line-stacking-strategy", "list-style", "list-style-image", "list-style-position", "list-style-type", "margin", "margin-bottom", "margin-left", "margin-right", "margin-top", "marker-offset", "marks", "marquee-direction", "marquee-loop", "marquee-play-count", "marquee-speed", "marquee-style", "max-height", "max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index", "nav-left", "nav-right", "nav-up", "object-fit", "object-position", "opacity", "order", "orphans", "outline", "outline-color", "outline-offset", "outline-style", "outline-width", "overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y", "padding", "padding-bottom", "padding-left", "padding-right", "padding-top", "page", "page-break-after", "page-break-before", "page-break-inside", "page-policy", "pause", "pause-after", "pause-before", "perspective", "perspective-origin", "pitch", "pitch-range", "play-during", "position", "presentation-level", "punctuation-trim", "quotes", "region-break-after", "region-break-before", "region-break-inside", "region-fragment", "rendering-intent", "resize", "rest", "rest-after", "rest-before", "richness", "right", "rotation", "rotation-point", "ruby-align", "ruby-overhang", "ruby-position", "ruby-span", "shape-image-threshold", "shape-inside", "shape-margin", "shape-outside", "size", "speak", "speak-as", "speak-header", "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set", "tab-size", "table-layout", "target", "target-name", "target-new", "target-position", "text-align", "text-align-last", "text-decoration", "text-decoration-color", "text-decoration-line", "text-decoration-skip", "text-decoration-style", "text-emphasis", "text-emphasis-color", "text-emphasis-position", "text-emphasis-style", "text-height", "text-indent", "text-justify", "text-outline", "text-overflow", "text-shadow", "text-size-adjust", "text-space-collapse", "text-transform", "text-underline-position", "text-wrap", "top", "transform", "transform-origin", "transform-style", "transition", "transition-delay", "transition-duration", "transition-property", "transition-timing-function", "unicode-bidi", "vertical-align", "visibility", "voice-balance", "voice-duration", "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress", "voice-volume", "volume", "white-space", "widows", "width", "word-break", "word-spacing", "word-wrap", "z-index" ) } ================================================ FILE: css/shared/src/main/scala/rapture/css/validator.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.css import rapture.core._ import com.steadystate.css.parser._ import org.w3c.css.sac._ import org.w3c.dom.css._ import java.io._ import scala.collection.immutable.ListMap object CssParser { case class ValidationException(strNo: Int, pos: Int, msg: String) extends Exception def parseStylesheet(parts: List[String], substitutions: List[String]): CssStylesheet = { val errHandler = new ErrorHandler { def stringNo(c: Int): (Int, Int) = parts.zip(substitutions).map { case (p, s) => p.length + s.length }.foldLeft(0 -> 0) { case ((sum, count), n) => if(sum > c) (sum, count) else (sum + n, count + 1) } def error(e: CSSParseException) = { val c = e.getColumnNumber - 1 val (sum, cnt) = stringNo(c) throw ValidationException(cnt, c - sum, e.getMessage) } def fatalError(e: CSSParseException) = error(e) def warning(e: CSSParseException) = error(e) } val source = new InputSource(new StringReader(parts.zip(substitutions :+ "").map { case (k, v) => k+v }.mkString)) val parser = new CSSOMParser(new SACParserCSS3()) parser.setErrorHandler(errHandler) val stylesheet = CssStylesheet(convertStylesheet(parser.parseStyleSheet(source, null, null))) stylesheet.rules.foreach(checkRule) stylesheet } def checkRule(cssRule: CssRule): Unit = cssRule match { case CssFontFace(css) => checkProperties(css) case CssMedia(rules, _) => rules.foreach(checkRule) case CssPage(_, css) => checkProperties(css) case CssStyle(_, css) => checkProperties(css) case _ => () } def parse(parts: List[String], substitutions: List[String]): Css = { val errHandler = new ErrorHandler { def stringNo(c: Int): (Int, Int) = parts.zip(substitutions).map { case (p, s) => p.length + s.length }.foldLeft(0 -> 0) { case ((sum, count), n) => if(sum > c) (sum, count) else (sum + n, count + 1) } def error(e: CSSParseException) = { val c = e.getColumnNumber - 1 val (sum, cnt) = stringNo(c) throw ValidationException(cnt, c - sum, e.getMessage) } def fatalError(e: CSSParseException) = error(e) def warning(e: CSSParseException) = error(e) } val source = new InputSource(new StringReader(parts.zip(substitutions :+ "").map { case (k, v) => k+v }.mkString)) val parser = new CSSOMParser(new SACParserCSS3()) parser.setErrorHandler(errHandler) val styles = convertStyleDeclaration(parser.parseStyleDeclaration(source)) checkProperties(styles) styles } def checkProperties(css: Css) = { var prefixes = List("-webkit-", "-moz-", "-ms-", "-o-") for((k, v) <- css.properties) if (!prefixes.exists(k.startsWith) && !Properties.all.contains(k)) throw ValidationException(0, 0, s"invalid CSS attribute '$k'") } def convertStylesheet(cssStylesheet: CSSStyleSheet): List[CssRule] = { val rules = cssStylesheet.getCssRules() (0 until rules.getLength).map { r => convertRule(rules.item(r)) }.to[List] } def convertRule(rule: CSSRule): CssRule = rule match { case rule: CSSCharsetRule => CssCharset(rule.getEncoding) case rule: CSSFontFaceRule => CssFontFace(convertStyleDeclaration(rule.getStyle)) case rule: CSSImportRule => CssImport(rule.getHref, rule.getMedia.getMediaText) case rule: CSSMediaRule => val rules = rule.getCssRules val convertedRules = (0 until rules.getLength).map { r => convertRule(rules.item(r)) }.to[List] CssMedia(convertedRules, rule.getMedia.getMediaText) case rule: CSSPageRule => CssPage(rule.getSelectorText, convertStyleDeclaration(rule.getStyle)) case rule: CSSStyleRule => CssStyle(rule.getSelectorText, convertStyleDeclaration(rule.getStyle)) case rule: CSSUnknownRule => CssUnknown(rule.getCssText) } def convertStyleDeclaration(styleDecl: CSSStyleDeclaration): Css = { val decls = (0 until styleDecl.getLength).map(styleDecl.item(_)).map { p => p -> styleDecl.getPropertyValue(p) } Css(ListMap(decls: _*)) } } ================================================ FILE: css-test/shared/src/test/scala/rapture/css-test/tests.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.css.test import rapture.core._ import rapture.css._ import rapture.html._ import rapture.test._ class TestRun extends Programme { include(CssTests) } object CssTests extends TestSuite { val `Embed ID in CSS` = test { val identifier = DomId.auto cssStylesheet"""$identifier { color: red; }""" } returns cssStylesheet"""#identifier { color: red; }""" val `Embed class in CSS` = test { val identifier = CssClass.auto cssStylesheet"""$identifier { color: red; }""" } returns cssStylesheet""".identifier { color: red; }""" val `Embed properties in CSS stylesheet` = test { val properties = css"border-width: 1px; color: red" cssStylesheet""".class { display: block; $properties }""" } returns cssStylesheet""".class { display: block; border-width: 1px; color: red; }""" val `Embed string in CSS` = test { val color = "red" css"color: $color" } val `Embed HTML tag in CSS stylesheet` = test { import htmlSyntax._ cssStylesheet"""$Div { color: red; }""" } returns cssStylesheet"""div { color: red; }""" val `Check stylesheet well-formedness` = test { cssStylesheet"""div { color: red; }""" } returns cssStylesheet"""div { color: red; }""" } ================================================ FILE: csv/shared/src/main/scala/rapture/csv/csv.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.csv import rapture.core._ import rapture.io._ import rapture.uri._ import rapture.fs._ import rapture.codec._ import encodings.`UTF-8`._ import language.experimental.macros object ReadCsvHeader { implicit val readCsvHeader: ReadCsvHeader = ReadCsvHeader(true) } case class ReadCsvHeader(header: Boolean) object CsvTest { import csvBackends.simple._ def main(args: Seq[String]) = Csv.parse(uri"file:///home/jpretty/test.csv") } case class CsvParseException(line: Int, col: Int) extends RuntimeException { override def getMessage = s"""parse error: expected `"`""" } sealed trait CsvGetException extends RuntimeException case class CsvMissingValue(col: Int) extends CsvGetException { override def getMessage = s"missing value: Column $col does not exist" } case class CsvTypeMismatch(typeName: String, col: Int) extends CsvGetException { override def getMessage = s"type mismatch: Could not read value of type $typeName in column $col" } trait `Csv.parse` extends MethodConstraint trait `CsvRow#as` extends MethodConstraint trait `Csv#mapAs` extends MethodConstraint trait `CsvCell#as` extends MethodConstraint class KeyedCsvRow(val csvRow: CsvRow, val keys: Map[String, Int]) { def apply(key: String): CsvCell = csvRow.apply(keys(key)) } class CsvRow(val elems: Seq[String]) { def apply(idx: Int): CsvCell = CsvCell(elems(idx), idx) def as[T](implicit mode: Mode[`CsvRow#as`], extractor: CsvRowExtractor[T]): mode.Wrap[T, CsvGetException] = extractor.extract(elems, mode) override def toString = elems.mkString("\"", "\",\"", "\"") } object CsvRowExtractor { implicit def generateExtractor[T]: CsvRowExtractor[T] = macro Macros.extractorMacro[T] } trait CsvRowExtractor[T] { def extract(values: Seq[String], mode: Mode[_]): mode.Wrap[T, CsvGetException] } case class BadLength(len: Int) extends Exception("Bad length: " + len) object CsvCellExtractor { implicit val stringExtractor: CsvCellExtractor[String] = new CsvCellExtractor[String] { type ExceptionType = CsvGetException def extract(value: String, c: Int, mode: Mode[_]): mode.Wrap[String, CsvGetException] = mode.wrap(value) } implicit val intExtractor: CsvCellExtractor[Int] = new CsvCellExtractor[Int] { type ExceptionType = CsvGetException def extract(value: String, c: Int, mode: Mode[_]): mode.Wrap[Int, CsvGetException] = mode.wrap { try value.toInt catch { case e: Exception => mode.exception(CsvTypeMismatch("integer", c)) } } } implicit val doubleExtractor: CsvCellExtractor[Double] = new CsvCellExtractor[Double] { type ExceptionType = CsvGetException def extract(value: String, c: Int, mode: Mode[_]): mode.Wrap[Double, CsvGetException] = mode.wrap { try value.toDouble catch { case e: Exception => mode.exception(CsvTypeMismatch("double", c)) } } } implicit val charExtractor: CsvCellExtractor[Char] = new CsvCellExtractor[Char] { type ExceptionType = BadLength def extract(value: String, c: Int, mode: Mode[_]): mode.Wrap[Char, CsvGetException with BadLength] = mode.wrap { if (value.length == 1) value.head else { mode.exception(CsvTypeMismatch("character", c)) '\u0000' } } } } trait CsvCellExtractor[T] { type ExceptionType <: Exception def extract(value: String, c: Int, mode: Mode[_]): mode.Wrap[T, CsvGetException with ExceptionType] } case class CsvCell(value: String, col: Int) { def as[T](mode: Mode[`CsvCell#as`])(ext: CsvCellExtractor[T]): mode.Wrap[T, CsvGetException with ext.ExceptionType] = ext.extract(value, col, mode) override def toString = value } case class Csv(rows: Vector[CsvRow]) extends Seq[CsvRow] { def iterator = rows.iterator def apply(idx: Int) = rows(idx) def length = rows.length override def toString = rows.take(4).mkString("\n") + (if (rows.length > 4) "\n..." else "") def mapAs[T: CsvRowExtractor](implicit mode: Mode[`Csv#mapAs`]): mode.Wrap[Seq[T], CsvGetException] = mode.wrap { rows.map { row => mode.unwrap(?[CsvRowExtractor[T]].extract(row.elems, mode)) } } } object Csv { def parse[Res](resource: Res)(implicit mode: Mode[`Csv.parse`], reader: Reader[Res, String], backend: CsvBackend): mode.Wrap[Csv, CsvParseException] = mode.wrap { Csv(reader.input(resource).to[Vector].zipWithIndex.map { case (ln, idx) => new CsvRow(mode.unwrap(backend.parseRow(ln, idx, mode))) }) } def extractor[T: CsvCellExtractor] = ?[CsvCellExtractor[T]] } trait CsvBackend { def parseRow(s: String, line: Int, mode: Mode[_]): mode.Wrap[Seq[String], CsvParseException] } object csvBackends { object simple { implicit val simpleBackend: CsvBackend = new CsvBackend { def parseRow(s: String, line: Int, mode: Mode[_]): mode.Wrap[Seq[String], CsvParseException] = mode.wrap { var cells: Vector[String] = Vector() var cur = 0 var quoted = false var expectingQuote = false var sb = new StringBuilder while (cur < s.length) { s(cur) match { case '"' => if (sb.isEmpty && !quoted) { quoted = true } else if (quoted) { quoted = false } else if (expectingQuote) { sb.append('"') expectingQuote = false } else expectingQuote = true cur += 1 case _ if expectingQuote => mode.exception(CsvParseException(line, cells.length)) case ',' if !quoted => cells = cells :+ sb.toString.trim sb = new StringBuilder cur += 1 case other => sb.append(other) cur += 1 } } cells :+ sb.toString.trim } } } } ================================================ FILE: csv/shared/src/main/scala/rapture/csv/macros.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.csv import rapture.core._ import rapture.base._ object Macros { def extractorMacro[T: c.WeakTypeTag](c: BlackboxContext): c.Expr[CsvRowExtractor[T]] = { import c.universe._ import compatibility._ val cellExtractor = typeOf[CsvCellExtractor[_]].typeSymbol.asType.toTypeConstructor require(weakTypeOf[T].typeSymbol.asClass.isCaseClass) val params = declarations(c)(weakTypeOf[T]).collect { case m: MethodSymbol if m.isCaseAccessor => m.asMethod }.zipWithIndex.map { case (p, i) => val searchType = appliedType(cellExtractor, List(p.returnType)) val inferredImplicit = c.inferImplicitValue(searchType, false, false) q"mode.unwrap($inferredImplicit.extract(values($i), $i, mode))" } val construction = c.Expr[T](q"new ${weakTypeOf[T]}(..$params)") reify { new CsvRowExtractor[T] { def extract(values: Seq[String], mode: Mode[_]): mode.Wrap[T, CsvGetException] = mode.wrap(construction.splice) } } } } ================================================ FILE: currency/shared/src/main/scala/rapture/currency/currency.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.currency import rapture.core._ object Currency { trait Key case class Evidence[C <: Currency.Key](currency: Currency { type Key = C }) object Arithmetic extends Arithmetic_1 { implicit def sameCurrencies[C <: Currency.Key]: Arithmetic[Money[C], Money[C]] { type Return = Money[C] } = new Arithmetic[Money[C], Money[C]] { type Return = Money[C] def add(left: Money[C], right: Money[C]): Money[C] = Money(left.currency, left.amount + right.amount) } implicit def currencyBaskets[C <: Currency.Key, C2 <: Currency.Key] : Arithmetic[CurrencyBasket[C], CurrencyBasket[C2]] { type Return = CurrencyBasket[C with C2] } = new Arithmetic[CurrencyBasket[C], CurrencyBasket[C2]] { type Return = CurrencyBasket[C with C2] def add(left: CurrencyBasket[C], right: CurrencyBasket[C2]): CurrencyBasket[C with C2] = CurrencyBasket[C with C2](left.amounts.foldLeft(right.amounts) { case (acc, (k, v)) => acc.updated(k, acc.get(k).getOrElse(0.0) + v) }) } implicit def currencyBasketAndMoney[C <: Currency.Key, C2 <: Currency.Key] : Arithmetic[CurrencyBasket[C], Money[C2]] { type Return = CurrencyBasket[C with C2] } = new Arithmetic[CurrencyBasket[C], Money[C2]] { type Return = CurrencyBasket[C with C2] def add(left: CurrencyBasket[C], right: Money[C2]): CurrencyBasket[C with C2] = CurrencyBasket[C with C2]( left.amounts.updated(right.currency, left.amounts.get(right.currency).getOrElse(0.0) + right.amount)) } implicit def moneyAndCurrencyBasket[C <: Currency.Key, C2 <: Currency.Key] : Arithmetic[Money[C], CurrencyBasket[C2]] { type Return = CurrencyBasket[C with C2] } = new Arithmetic[Money[C], CurrencyBasket[C2]] { type Return = CurrencyBasket[C with C2] def add(left: Money[C], right: CurrencyBasket[C2]): CurrencyBasket[C with C2] = CurrencyBasket[C with C2]( right.amounts.updated(left.currency, right.amounts.get(left.currency).getOrElse(0.0) + left.amount)) } } trait Arithmetic_1 { implicit def differentCurrencies[C <: Currency.Key, C2 <: Currency.Key] : Arithmetic[Money[C], Money[C2]] { type Return = CurrencyBasket[C with C2] } = new Arithmetic[Money[C], Money[C2]] { type Return = CurrencyBasket[C with C2] def add(left: Money[C], right: Money[C2]): CurrencyBasket[C with C2] = if (left.currency == right.currency) CurrencyBasket[C with C2](Map(left.currency -> (left.amount + right.amount))) else CurrencyBasket[C with C2](Map(left.currency -> left.amount, right.currency -> right.amount)) } } trait Arithmetic[T1, T2] { type Return def add(left: T1, right: T2): Return } } case class Currency(code: String, name: String, decimalPlaces: Int, prefix: String, suffix: String) { type Key <: Currency.Key override def hashCode = code.hashCode override def toString = code override def equals(that: Any) = that match { case that: Currency if that.code == code => true case _ => false } def apply[N: Numeric](amount: N): Money[Key] = Money[Key](this, implicitly[Numeric[N]].toDouble(amount)) } case class InvalidMoney(currency: Currency) extends Exception(s"this is not a valid money value of currency ${currency.code}") object Money { implicit def moneySerializer[C <: Currency.Key]: StringSerializer[Money[C]] = new StringSerializer[Money[C]] { def serialize(m: Money[C]) = { implicit val df: DecimalFormat = DecimalPlaces(m.currency.decimalPlaces) s"${String(m.amount)}${m.currency.code}" } } implicit def moneyParser[C <: Currency.Key]( implicit ev: Currency.Evidence[C]): StringParser[Money[C]] { type Throws = InvalidMoney } = new StringParser[Money[C]] { type Throws = InvalidMoney def parse(s: String, mode: Mode[_ <: MethodConstraint]): mode.Wrap[Money[C], InvalidMoney] = mode.wrap { if (s.substring(s.length - 3) == ev.currency.code) { try Money[C](ev.currency, s.dropRight(3).toDouble) catch { case e: Exception => throw InvalidMoney(ev.currency) } } else throw InvalidMoney(ev.currency) } } } case class Money[C <: Currency.Key](currency: Currency { type Key = C }, amount: Double) { def unary_- = Money[C](currency, -amount) def +[C2 <: Currency.Key](m: Money[C2])( implicit arithmetic: Currency.Arithmetic[Money[C], Money[C2]]): arithmetic.Return = arithmetic.add(this, m) def -[C2 <: Currency.Key](m: Money[C2])( implicit arithmetic: Currency.Arithmetic[Money[C], Money[C2]]): arithmetic.Return = arithmetic.add(this, -m) def *[N: Numeric](times: N): Money[C] = Money(currency, amount * implicitly[Numeric[N]].toDouble(times)) def /[N: Numeric](divisor: N): Money[C] = *(1.0 / implicitly[Numeric[N]].toDouble(divisor)) def <(m: Money[C]) = amount < m.amount def <=(m: Money[C]) = amount <= m.amount def >(m: Money[C]) = amount > m.amount def >=(m: Money[C]) = amount >= m.amount override def toString = { implicit val df: DecimalFormat = DecimalPlaces(currency.decimalPlaces) s"${currency.prefix}${String(amount)}${currency.suffix}" } } case class CurrencyBasket[C <: Currency.Key](amounts: Map[Currency, Double]) { def +[M](m: M)(implicit arithmetic: Currency.Arithmetic[CurrencyBasket[C], M]): arithmetic.Return = arithmetic.add(this, m) def unary_- = CurrencyBasket[C](amounts.mapValues(-_)) def *[N: Numeric](times: N): CurrencyBasket[C] = CurrencyBasket(amounts.mapValues(_ * implicitly[Numeric[N]].toDouble(times))) def /[N: Numeric](divisor: N): CurrencyBasket[C] = *(1.0 / implicitly[Numeric[N]].toDouble(divisor)) def apply[C2 >: C <: Currency.Key](implicit ev: Currency.Evidence[C2]): Money[C2] = Money[C2](ev.currency, amounts(ev.currency)) override def toString = amounts.map { case (currency, value) => implicit val df: DecimalFormat = DecimalPlaces(currency.decimalPlaces) s"${String(value)}${currency.code}" }.mkString(", ") } ================================================ FILE: currency/shared/src/main/scala/rapture/currency/iso.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.currency /* Includes the twenty most-traded currencies, as listed on Wikipedia, with the exception of Turkish Lira, because its ISO 4217 code is `TRY`, which is an inconvenient identifier. */ object currencies { object Gbp extends Currency("GBP", "Pound sterling", 2, "£", "") { type Key = Gbp implicit val currencyEvidence: Currency.Evidence[Gbp] = Currency.Evidence(this) } trait Gbp extends Currency.Key object Usd extends Currency("USD", "United States dollar", 2, "$", "") { type Key = Usd implicit val currencyEvidence: Currency.Evidence[Usd] = Currency.Evidence(this) } trait Usd extends Currency.Key object Eur extends Currency("EUR", "Euros", 2, "€", "") { type Key = Eur implicit val currencyEvidence: Currency.Evidence[Eur] = Currency.Evidence(this) } trait Eur extends Currency.Key object Jpy extends Currency("JPY", "Japanese yen", 2, "¥", "") { type Key = Jpy implicit val currencyEvidence: Currency.Evidence[Jpy] = Currency.Evidence(this) } trait Jpy extends Currency.Key object Aud extends Currency("AUD", "Australian dollar", 2, "$", "") { type Key = Aud implicit val currencyEvidence: Currency.Evidence[Aud] = Currency.Evidence(this) } trait Aud extends Currency.Key object Chf extends Currency("CHF", "Swiss franc", 2, "Fr", "") { type Key = Chf implicit val currencyEvidence: Currency.Evidence[Chf] = Currency.Evidence(this) } trait Chf extends Currency.Key object Cad extends Currency("CAD", "Canadian dollar", 2, "$", "") { type Key = Cad implicit val currencyEvidence: Currency.Evidence[Cad] = Currency.Evidence(this) } trait Cad extends Currency.Key object Mxn extends Currency("MXN", "Mexican peso", 2, "$", "") { type Key = Mxn implicit val currencyEvidence: Currency.Evidence[Mxn] = Currency.Evidence(this) } trait Mxn extends Currency.Key object Cny extends Currency("CNY", "Chinese yuan", 2, "¥", "") { type Key = Cny implicit val currencyEvidence: Currency.Evidence[Cny] = Currency.Evidence(this) } trait Cny extends Currency.Key object Nzd extends Currency("NZD", "New Zealand dollar", 2, "$", "") { type Key = Nzd implicit val currencyEvidence: Currency.Evidence[Nzd] = Currency.Evidence(this) } trait Nzd extends Currency.Key object Sek extends Currency("SEK", "Swedish krona", 2, "kr", "") { type Key = Sek implicit val currencyEvidence: Currency.Evidence[Sek] = Currency.Evidence(this) } trait Sek extends Currency.Key object Rub extends Currency("RUB", "Russian ruble", 2, "₽", "") { type Key = Rub implicit val currencyEvidence: Currency.Evidence[Rub] = Currency.Evidence(this) } trait Rub extends Currency.Key object Hkd extends Currency("HKD", "Hong Kong dollar", 2, "$", "") { type Key = Hkd implicit val currencyEvidence: Currency.Evidence[Hkd] = Currency.Evidence(this) } trait Hkd extends Currency.Key object Nok extends Currency("NOK", "Norwegian krone", 2, "kr", "") { type Key = Nok implicit val currencyEvidence: Currency.Evidence[Nok] = Currency.Evidence(this) } trait Nok extends Currency.Key object Sgd extends Currency("SGD", "Singapore dollar", 2, "$", "") { type Key = Sgd implicit val currencyEvidence: Currency.Evidence[Sgd] = Currency.Evidence(this) } trait Sgd extends Currency.Key object Krw extends Currency("KRW", "South Korean won", 2, "₩", "") { type Key = Krw implicit val currencyEvidence: Currency.Evidence[Krw] = Currency.Evidence(this) } trait Krw extends Currency.Key object Zar extends Currency("ZAR", "South African Rand", 2, "R", "") { type Key = Zar implicit val currencyEvidence: Currency.Evidence[Zar] = Currency.Evidence(this) } trait Zar extends Currency.Key object Brl extends Currency("BRL", "Brazilian real", 2, "R$", "") { type Key = Brl implicit val currencyEvidence: Currency.Evidence[Brl] = Currency.Evidence(this) } trait Brl extends Currency.Key object Inr extends Currency("INR", "Indian rupee", 2, "₹", "") { type Key = Inr implicit val currencyEvidence: Currency.Evidence[Inr] = Currency.Evidence(this) } trait Inr extends Currency.Key } ================================================ FILE: data/shared/src/main/scala/rapture/data/ast.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.data import rapture.core._ object DataTypes { sealed class DataType(val name: String) case object Number extends DataType("number") case object String extends DataType("string") case object Null extends DataType("null") case object Boolean extends DataType("boolean") case object Array extends DataType("array") case object Object extends DataType("object") case object Scalar extends DataType("scalar") case object Container extends DataType("container") case object Any extends DataType("any") } @implicitNotFound(msg = "Cannot find ${Ast} parser for values of type ${Source}") trait Parser[-Source, +Ast <: DataAst] { val ast: Ast def parse(s: Source): Option[Any] } trait DataAst { /** Dereferences the named element within the JSON object. */ def dereferenceObject(obj: Any, element: String): Any = getObject(obj)(element) /** Returns at `Iterator[String]` over the names of the elements in the JSON object. */ def getKeys(obj: Any): Iterator[String] = getObject(obj).keys.iterator /** Gets the indexed element from the parsed JSON array. */ def dereferenceArray(array: Any, element: Int): Any = getArray(array)(element) /** Tests if the element represents an `Object` */ def isObject(any: Any): Boolean /** Tests if the element represents an `Array` */ def isArray(any: Any): Boolean def isNull(any: Any): Boolean /** Extracts a JSON object as a `Map[String, Any]` from the parsed JSON. */ def getObject(obj: Any): Map[String, Any] def getChildren(obj: Any): Seq[Any] = { val m = getObject(obj) getKeys(obj).to[List] map m } def fromObject(obj: Map[String, Any]): Any /** Extracts a JSON array as a `Seq[Any]` from the parsed JSON. */ def getArray(array: Any): Seq[Any] def fromArray(array: Seq[Any]): Any def isScalar(any: Any): Boolean def getString(any: Any): Any def isString(any: Any): Boolean def convert(any: Any, oldAst: DataAst): Any } trait MutableDataAst extends DataAst { def setObjectValue(obj: Any, name: String, value: Any): Any def setArrayValue(array: Any, index: Int, value: Any): Any def removeObjectValue(obj: Any, name: String): Any def addArrayValue(array: Any, value: Any): Any } ================================================ FILE: data/shared/src/main/scala/rapture/data/context.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.data import rapture.base._ import rapture.core._ object patternMatching { object exactArrays { implicit val implicitArrayMachingConfig = new ArrayMatchingConfig { def checkLengths = true } } object exactObjects { implicit val implicitArrayMachingConfig = new ObjectMatchingConfig { def checkSizes = true } } object exact { implicit val implicitArrayMachingConfig = new ObjectMatchingConfig with ArrayMatchingConfig { def checkSizes = true def checkLengths = true } } } object ArrayMatchingConfig { implicit val ignoreByDefault = new ArrayMatchingConfig { def checkLengths = false } } object ObjectMatchingConfig { implicit val ignoreByDefault = new ObjectMatchingConfig { def checkSizes = false } } trait ArrayMatchingConfig { def checkLengths: Boolean } trait ObjectMatchingConfig { def checkSizes: Boolean } abstract class DataContextMacros[+Data <: DataType[Data, DataAst], -AstType <: DataAst] { def parseSource(s: List[String], stringsUsed: List[Boolean]): Option[(Int, Int, String)] def dataCompanion(c: BlackboxContext): c.Expr[DataCompanion[Data, AstType]] def contextMacro(c: BlackboxContext)(exprs: c.Expr[ForcedConversion[Data]]*)( parser: c.Expr[Parser[String, AstType]]): c.Expr[Data] = { import c.universe._ c.prefix.tree match { case Select(Apply(Apply(_, List(Apply(_, rawParts))), _), _) => val ys = rawParts.to[List] val text = rawParts map { case lit @ Literal(Constant(part: String)) => part } val listExprs = c.Expr[List[ForcedConversion[Data]]](q"_root_.scala.List(..${exprs.map(_.tree).to[List]})") val stringsUsed: List[Boolean] = listExprs.tree match { case Apply(_, bs) => bs.map { case Apply(Apply(TypeApply(Select(_, nme), _), _), _) => nme.toString == "forceStringConversion" } } parseSource(text, stringsUsed) foreach { case (n, offset, msg) => val oldPos = ys(n).asInstanceOf[Literal].pos val newPos = oldPos.withPoint(oldPos.start + offset) c.error(newPos, msg) } val listParts = c.Expr[List[ForcedConversion[Data]]](q"_root_.scala.List(..$rawParts)") val comp = dataCompanion(c) reify { val sb = new StringBuilder val textParts = listParts.splice.iterator val expressions: Iterator[ForcedConversion[_]] = listExprs.splice.iterator sb.append(textParts.next()) while (textParts.hasNext) { sb.append( comp.splice.construct(MutableCell(expressions.next.value), Vector())(parser.splice.ast).toBareString) sb.append(textParts.next) } comp.splice.construct(MutableCell(parser.splice.parse(sb.toString).get), Vector())(parser.splice.ast) } } } } class DataContext[+Data <: DataType[Data, DataAst], -AstType <: DataAst](companion: DataCompanion[Data, AstType], sc: StringContext) { protected def uniqueNonSubstring(s: String) = { var cur, m = 0 s foreach { c => cur = if (c == '_') cur + 1 else 0 m = m max cur } "_" * (m + 1) } def unapplySeq[D <: DataType[D, DataAst]](data: D)( implicit arrayMatching: ArrayMatchingConfig, objectMatching: ObjectMatchingConfig, parser: Parser[String, AstType]): Option[Seq[DataType[D, DataAst]]] = try { val placeholder = uniqueNonSubstring(sc.parts.mkString) val PlaceholderNumber = (placeholder + "([0-9]+)" + placeholder).r val count = Iterator.from(0) val txt = sc.parts.reduceLeft(_ + s""""${placeholder}${count.next()}${placeholder}" """ + _) val paths: Array[Vector[Either[Int, String]]] = Array.fill[Vector[Either[Int, String]]](sc.parts.length - 1)(Vector()) val arrayLengths = new collection.mutable.HashMap[Vector[Either[Int, String]], Int] val objectSizes = new collection.mutable.HashMap[Vector[Either[Int, String]], Int] def extract(any: Any, path: Vector[Either[Int, String]]): Unit = { // FIXME: Avoid using isScalar if possible if (parser.ast.isScalar(any)) { val ext = data.$extract(path).as[Any] if (data.$extract(path).as[Any] != any) throw new Exception("Value doesn't match (1)") } else if (parser.ast.isObject(any)) { val obj = parser.ast.getObject(any) objectSizes(path) = obj.size obj foreach { case (k, v) => if (parser.ast.isString(v)) parser.ast.getString(v) match { case str: CharSequence => str match { case PlaceholderNumber(n) => paths(n.toInt) = path :+ Right(k) case _ => extract(v, path :+ Right(k)) } } else extract(v, path :+ Right(k)) } } else if (parser.ast.isArray(any)) { val array = parser.ast.getArray(any) if (arrayMatching.checkLengths) arrayLengths(path) = array.length array.zipWithIndex foreach { case (e, i) => if (parser.ast.isString(e)) parser.ast.getString(e) match { case str: CharSequence => str match { case PlaceholderNumber(n) => paths(n.toInt) = path :+ Left(i) case _ => extract(e, path :+ Left(i)) } } else extract(e, path :+ Left(i)) } } else throw new Exception("Value doesn't match (2)") } extract(parser.parse(txt).get, Vector()) // Using a ListBuffer to work around SI-8947 val extractsBuffer = new collection.mutable.ListBuffer[D] paths foreach { p => extractsBuffer += data.$extract(p) } val extracts = extractsBuffer.toList extracts.foreach(_.$normalize) val matchedArrayLengths = arrayLengths.forall { case (p, len) => parser.ast.getArray(data.$extract(p).$normalize).length == len } val matchedObjectSizes = objectSizes.forall { case (p, s) => if (objectMatching.checkSizes) parser.ast.getObject(data.$extract(p).$normalize).size == s else parser.ast.getObject(data.$extract(p).$normalize).size >= 0 } if (extracts.exists(_.$root.value == null) || !matchedArrayLengths || !matchedObjectSizes) None else Some(extracts) } catch { case e: Exception => None } } ================================================ FILE: data/shared/src/main/scala/rapture/data/data.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.data import rapture.core._ import scala.util.Try import language.dynamics @implicitNotFound("Cannot find an implicit Formatter for ${AstType} data.") trait Formatter[-AstType <: DataAst] { type Out def format(any: Any): Out } object DataCompanion { object Empty } object serializedNames { object inUnderscoreStyle { def apply[V, D] = nameMapperImplicit[V, D] implicit def nameMapperImplicit[V, D] = new NameMapper[V, D] { def encode(name: String): String = name.flatMap { case lower if lower.isLower => lower.toString case upper if upper.isUpper => s"_$upper".toLowerCase case other => other.toString } def decode(name: String): String = name.foldLeft(("", false)) { case ((acc, _), '_') => (acc, true) case ((acc, true), other) => (acc+other.toString.toUpperCase, false) case ((acc, false), other) => (acc+other, false) }._1 } } object inDashedStyle { def apply[V, D] = nameMapperImplicit[V, D] implicit def nameMapperImplicit[V, D] = new NameMapper[V, D] { def encode(name: String): String = name.flatMap { case lower if lower.isLower => lower.toString case upper if upper.isUpper => s"-$upper".toLowerCase case other => other.toString } def decode(name: String): String = name.foldLeft(("", false)) { case ((acc, _), '-') => (acc, true) case ((acc, true), other) => (acc+other.toString.toUpperCase, false) case ((acc, false), other) => (acc+other, false) }._1 } } object identical { def apply[V, D] = nameMapperImplicit[V, D] implicit def nameMapperImplicit[V, D] = new NameMapper[V, D] { def encode(name: String): String = name def decode(name: String): String = name } } } object NameMapper { implicit def identityNameMapper[V, D]: NameMapper[V, D] = serializedNames.identical[V, D] } trait NameMapper[+Value, +Data] { def encode(name: String): String def decode(name: String): String } trait DataCompanion[+Type <: DataType[Type, DataAst], -AstType <: DataAst] { type ParseMethodConstraint <: MethodConstraint def empty(implicit ast: AstType) = construct(MutableCell(ast.fromObject(Map())), Vector()) def construct(any: MutableCell, path: Vector[Either[Int, String]])(implicit ast: AstType): Type def parse[Source: StringSerializer](s: Source)(implicit mode: Mode[ParseMethodConstraint], parser: Parser[Source, AstType]): mode.Wrap[Type, ParseException] = mode wrap { construct(try MutableCell(parser.parse(s).get) catch { case e: NoSuchElementException => mode.exception(ParseException(String(s))) }, Vector())(parser.ast) } def raw(value: Any)(implicit ast: AstType): Type = construct(MutableCell(value), Vector()) def format[T <: DataType[T, AstType]](data: T)(implicit f: Formatter[_ <: AstType]): f.Out = f.format(data.$normalize) } case class DynamicApplication[D](path: List[Either[Int, String]], application: ForcedConversion2[D]) object dictionaries { object dynamic { implicit val implicitDictionary: Dictionary[Nothing] = new Dictionary[Nothing](Nil) } } object Dictionary { implicit def stringToDefineParam[S <: String](s: S): DefineParam[s.type] = new DefineParam[s.type](s) class DefineParam[-T <: String](val value: String) def define[T <: String](params: DefineParam[T]*): Dictionary[T] = new Dictionary[T](params.map(_.value)) } @implicitNotFound("the key ${S} is not in the dictionary") class Dictionary[+S <: String](params: Seq[String]) case class DynamicPath[D](path: List[Either[Int, String]]) extends Dynamic { def self = selectDynamic("self")(null) def selectDynamic[S <: String](v: S)(implicit dictionary: Dictionary[v.type]) = DynamicPath[D](Right(v) :: path) def applyDynamic[S <: String](v: String)(i: Int)(implicit dictionary: Dictionary[v.type]) = DynamicPath[D](Left(i) :: Right(v) :: path) def apply(i: Int) = DynamicPath[D](Left(i) :: path) def updateDynamic[S <: String](v: String)(value: ForcedConversion2[D])(implicit dictionary: Dictionary[v.type]) = DynamicApplication(Right(v) :: path, value) def update(i: Int, value: ForcedConversion2[D]) = DynamicApplication(Left(i) :: path, value) } case class DynamicAccess[D](path: List[Either[Int, String]]) extends Dynamic { def self = selectDynamic("self") def selectDynamic(v: String) = DynamicAccess[D](Right(v) :: path) def applyDynamic(v: String)(i: Int) = DynamicAccess[D](Left(i) :: Right(v) :: path) def apply(i: Int) = DynamicAccess[D](Left(i) :: path) } case class MutableCell(var value: Any) trait DynamicData[+T <: DynamicData[T, AstType], +AstType <: DataAst] extends Dynamic { /** Assumes the Json object wraps a `Map`, and extracts the element `key`. */ def selectDynamic[S <: String](key: S)(implicit dictionary: Dictionary[key.type]): T = $deref(Right(key) +: $path) def self = selectDynamic("self")(null) //def applyDynamic(key: String)(i: Int = 0): T = $deref(Left(i) +: Right(key) +: $path) def $deref($path: Vector[Either[Int, String]]): T def $path: Vector[Either[Int, String]] } object DataType { class DataClassOperations[T <: DataType[T, AstType], AstType <: DataAst](dataType: T) { def ++[S <: DataType[S, Rep] forSome { type Rep }](b: S): T = { val ast = dataType.$ast def merge(a: Any, b: Any): Any = { if (ast.isObject(a) && ast.isObject(b)) { ast.fromObject(ast.getKeys(b).foldLeft(ast.getObject(a)) { case (as, k) => as + (k -> { if (as contains k) merge(as(k), ast.dereferenceObject(b, k)) else ast.dereferenceObject(b, k) }) }) } else if (ast.isArray(a) && ast.isArray(b)) ast.fromArray(ast.getArray(a) ++ ast.getArray(b)) else b } val left = dataType.$normalize val right = if (ast != b.$ast) ast.convert(b.$normalize, b.$ast.asInstanceOf[DataAst]) else b.$normalize dataType.$wrap(merge(left, right), Vector()) } def delete(pvs: (DynamicAccess[T] => DynamicAccess[_ <: DataType[T, _ <: AstType]])*): T = { dataType.$wrap(pvs.foldLeft(dataType.$normalize) { case (cur, pv) => val dPath = pv(DynamicAccess(Nil)) val ast = dataType.$ast def nav(path: List[Either[Int, String]], dest: Any): Any = path match { case Nil => ??? case Right(next) :: Nil => ast.fromObject(ast.getObject(if(ast.isObject(dest)) dest else Map()) - next) case Right(next) :: list => val d = try ast.dereferenceObject(dest, next) catch { case e: Exception => ast.fromObject(Map()) } val src = ast.getObject(if (ast.isObject(dest)) dest else Map()) ast.fromObject(src + ((next, nav(list, d)))) case Left(next) :: Nil => val src = if (ast.isArray(dest)) ast.getArray(dest) else Nil ast.fromArray(src.slice(0, next) ++ src.slice(next + 1, src.length)) case Left(next) :: list => val d = try ast.dereferenceArray(dest, next) catch { case e: Exception => ast.fromArray(List()) } val src = if (ast.isArray(dest)) ast.getArray(dest) else Nil ast.fromArray(src.padTo(next + 1, ast.fromObject(Map())).updated(next, nav(list, d))) } nav(dPath.path.reverse, cur) }) } def copy(pvs: (DynamicPath[T] => DynamicApplication[_ <: DataType[T, _ <: AstType]])*): T = { dataType.$wrap(pvs.foldLeft(dataType.$normalize) { case (cur, pv) => val dPath = pv(DynamicPath(Nil)) val ast = dataType.$ast if (dPath.application.nothing) cur else { def nav(path: List[Either[Int, String]], dest: Any, v: Any): Any = path match { case Nil => v case Right(next) :: list => val d = try ast.dereferenceObject(dest, next) catch { case e: Exception => ast.fromObject(Map()) } val src = ast.getObject(if (ast.isObject(dest)) dest else Map()) ast.fromObject(src + ((next, nav(list, d, v)))) case Left(next) :: list => val d = try ast.dereferenceArray(dest, next) catch { case e: Exception => ast.fromArray(List()) } val src = if (ast.isArray(dest)) ast.getArray(dest) else Nil ast.fromArray(src.padTo(next + 1, ast.fromObject(Map())).updated(next, nav(list, d, v))) } nav(dPath.path.reverse, cur, dPath.application.value) } }) } } } trait DataType[+T <: DataType[T, AstType], +AstType <: DataAst] { val $root: MutableCell implicit def $ast: AstType def $path: Vector[Either[Int, String]] def $normalize: Any = doNormalize(false) def $wrap(any: Any, $path: Vector[Either[Int, String]] = Vector()): T def $deref($path: Vector[Either[Int, String]] = Vector()): T def $extract($path: Vector[Either[Int, String]]): T def \(key: String): T = $deref(Right(key) +: $path) def \(index: Int): T = $deref(Left(index) +: $path) def \\(key: String): T = $wrap($ast.fromArray(derefRecursive(key, $normalize))) def toBareString: String private def derefRecursive(key: String, any: Any): Seq[Any] = if ($ast.isObject(any)) $ast.getKeys(any).to[Seq].flatMap { case k if k == key => Seq($ast.dereferenceObject(any, k)) case k => derefRecursive(key, $ast.dereferenceObject(any, k)) } else if ($ast.isArray(any)) $ast.getArray(any).flatMap(derefRecursive(key, _)) else Nil protected def doNormalize(orEmpty: Boolean): Any = { yCombinator[(Any, Vector[Either[Int, String]]), Any] { fn => { case (j, Vector()) => j: Any case (j, t :+ e) => fn(({ if (e.bimap(x => $ast.isArray(j), x => $ast.isObject(j))) { try e.bimap($ast.dereferenceArray(j, _), $ast.dereferenceObject(j, _)) catch { case TypeMismatchException(exp, fnd) => throw TypeMismatchException(exp, fnd) case exc: Exception => if (orEmpty) DataCompanion.Empty else { e match { case Left(e) => throw MissingValueException(s"[$e]") case Right(e) => throw MissingValueException(e) } } } } else throw TypeMismatchException( if ($ast.isArray(j)) DataTypes.Array else DataTypes.Object, e.bimap(l => DataTypes.Array, r => DataTypes.Object) ) }, t)) } }($root.value -> $path) } /** Assumes the Json object is wrapping a `T`, and casts (intelligently) to that type. */ def as[S](implicit ext: Extractor[S, T], mode: Mode[`Data#as`]): mode.Wrap[S, ext.Throws] = ext.extract(this.asInstanceOf[T], $ast, mode) def is[S](implicit ext: Extractor[S, T]): Boolean = try { ext.extract(this.asInstanceOf[T], $ast, modes.throwExceptions()) true } catch { case e: Exception => false } def apply(i: Int = 0): T = $deref(Left(i) +: $path) override def equals(any: Any) = try { any match { case any: DataType[_, _] => $normalize == any.$normalize case _ => false } } catch { case e: Exception => false } override def hashCode = $root.value.hashCode ^ 3271912 } trait MutableDataType[+T <: DataType[T, AstType], AstType <: MutableDataAst] extends DataType[T, AstType] { def $updateParents(p: Vector[Either[Int, String]], newVal: Any): Unit = p match { case Vector() => $root.value = newVal case Left(idx) +: init => val jb = $deref(init) val newJb = $ast.setArrayValue(Try(jb.$normalize).getOrElse($ast.fromArray(Nil)), idx, newVal) if (jb match { case jb: AnyRef => newJb match { case newJb: AnyRef => jb ne newJb case _ => false } case jb => jb == newJb }) $updateParents(init, newJb) case Right(key) +: init => val jb = $deref(init) val newJb = $ast.setObjectValue(Try(jb.$normalize).getOrElse($ast.fromObject(Map())), key, newVal) if (jb match { case jb: AnyRef => newJb match { case newJb: AnyRef => jb ne newJb case _ => false } case jb => jb == newJb }) $updateParents(init, newJb) } /** Updates the element `key` of the JSON object with the value `v` */ def updateDynamic(key: String)(v: ForcedConversion2[T]): Unit = if (!v.nothing) $updateParents($path, $ast.setObjectValue(Try($normalize).getOrElse($ast.fromObject(Map())), key, v.value)) /** Updates the `i`th element of the JSON array with the value `v` */ def update[T2](i: Int, v: T2)(implicit ser: Serializer[T2, T]): Unit = $updateParents($path, $ast.setArrayValue(Try($normalize).getOrElse($ast.fromArray(Nil)), i, ser.serialize(v))) /** Removes the specified key from the JSON object */ def -=(k: String): Unit = $updateParents($path, $ast.removeObjectValue(doNormalize(true), k)) /** Adds the specified value to the JSON array */ def +=[T2](v: T2)(implicit ser: Serializer[T2, T]): Unit = { val r = doNormalize(true) val insert = if (r == DataCompanion.Empty) $ast.fromArray(Nil) else r $updateParents($path, $ast.addArrayValue(insert, ser.serialize(v))) } } trait `Data#as` extends MethodConstraint trait `Data#normalize` extends MethodConstraint object ForcedConversion2 extends ForcedConversion2_1 { implicit def forceOptConversion[T, D](opt: Option[T])(implicit ser: Serializer[T, D]) = opt.map(t => ForcedConversion2[D](ser.serialize(t), false)) getOrElse ForcedConversion2[D](null, true) } trait ForcedConversion2_1 { implicit def forceConversion[T, D](t: T)(implicit ser: Serializer[T, D]) = ForcedConversion2[D](ser.serialize(t), false) } case class ForcedConversion2[-D](value: Any, nothing: Boolean) object ForcedConversion extends ForcedConversion_1 { implicit def forceOptConversion[T, D](opt: Option[T])(implicit ser: Serializer[T, D]) = opt.map(t => ForcedConversion[D](ser.serialize(t), false)) getOrElse ForcedConversion[D](null, true) } trait ForcedConversion_1 extends ForcedConversion_2 { implicit def forceConversion[T, D](t: T)(implicit ser: Serializer[T, D]) = ForcedConversion[D](ser.serialize(t), false) } trait ForcedConversion_2 { // The name of this method is significant for some additional checking done in the macro `contextMacro`. implicit def forceStringConversion[D, T: StringSerializer](value: T)(implicit ser: Serializer[String, D]) = ForcedConversion[D](ser.serialize(?[StringSerializer[T]].serialize(value)), false) } case class ForcedConversion[-D](value: Any, nothing: Boolean) case class ParseException(source: String, line: Option[Int] = None, column: Option[Int] = None) extends Exception("Failed to parse source") ================================================ FILE: data/shared/src/main/scala/rapture/data/exceptions.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.data sealed abstract class DataGetException(msg: String) extends RuntimeException(msg) case class TypeMismatchException(found: DataTypes.DataType, expected: DataTypes.DataType) extends DataGetException(s"type mismatch: Expected ${expected.name} but found ${found.name}") case class MissingValueException(name: String = "") extends DataGetException( if (name.isEmpty) "missing value" else s"missing value: $name") ================================================ FILE: data/shared/src/main/scala/rapture/data/extractors.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.data import rapture.core._ import scala.annotation._ import language.higherKinds import scala.util._ case class FilterException() extends Exception("value was removed by filter") case class NotEmptyException() extends Exception object GeneralExtractors { def tryExtractor[Data <: DataType[Data, _ <: DataAst], T]( implicit ext: Extractor[T, Data]): Extractor[Try[T], Data] { type Throws = Nothing } = new Extractor[Try[T], Data] { type Throws = Nothing def extract(any: Data, ast: DataAst, mode: Mode[_ <: MethodConstraint]): mode.Wrap[Try[T], Throws] = mode.wrap { try ext.extract(any.$wrap(any.$normalize), any.$ast, modes.returnTry()) catch { case e: Exception => Failure(e) } } } def optionExtractor[Data <: DataType[Data, _ <: DataAst], T]( implicit ext: Extractor[T, Data]): Extractor[Option[T], Data] { type Throws = Nothing } = { new Extractor[Option[T], Data] { type Throws = Nothing def extract(any: Data, ast: DataAst, mode: Mode[_ <: MethodConstraint]): mode.Wrap[Option[T], Throws] = mode.wrap { try ext.extract(any.$wrap(any.$normalize), any.$ast, modes.returnOption()) catch { case e: Exception => None } } } } def noneExtractor[Data <: DataType[_, DataAst]] : Extractor[None.type, Data] { type Throws = DataGetException with NotEmptyException } = new Extractor[None.type, Data] { type Throws = DataGetException with NotEmptyException def extract(value: Data, ast: DataAst, mode: Mode[_ <: MethodConstraint]): mode.Wrap[None.type, Throws] = mode.wrap { val v = value.$wrap(value.$normalize) if (ast.isObject(v) && ast.getKeys(v).size == 0) None else mode.exception[None.type, NotEmptyException](NotEmptyException()) } } def genSeqExtractor[T, Coll[_], Data <: DataType[Data, _ <: DataAst]]( implicit cbf: scala.collection.generic.CanBuildFrom[Nothing, T, Coll[T]], ext: Extractor[T, Data]): Extractor[Coll[T], Data] { type Throws = ext.Throws } = { new Extractor[Coll[T], Data] { type Throws = ext.Throws def extract(value: Data, ast: DataAst, mode: Mode[_ <: MethodConstraint]): mode.Wrap[Coll[T], Throws] = mode.wrap { mode.catching[DataGetException, Coll[T]] { val v = value.$wrap(value.$normalize) v.$ast .getArray(v.$root.value) .to[List] .zipWithIndex .map { case (e, i) => mode.unwrap(ext.safeExtract(v.$wrap(e), v.$ast, Some(Left(i)), mode), s"($i)") } .to[Coll] } } } } def mapExtractor[K, T, Data <: DataType[Data, _ <: DataAst]]( implicit ext: Extractor[T, Data], ext2: StringParser[K]): Extractor[Map[K, T], Data] { type Throws = ext.Throws with ext2.Throws } = new Extractor[Map[K, T], Data] { type Throws = ext.Throws with ext2.Throws def extract(value: Data, ast: DataAst, mode: Mode[_ <: MethodConstraint]): mode.Wrap[Map[K, T], Throws] = mode.wrap { value.$ast.getObject(value.$normalize) map { case (k, v) => mode.unwrap(ext2.parse(k, mode)) -> mode.unwrap( ext.safeExtract(value.$wrap(v), value.$ast, Some(Right(k)), mode)) } } } } object Extractor { implicit def anyExtractor[Data <: DataType[_, DataAst]]: Extractor[Any, Data] { type Throws = Nothing } = new Extractor[Any, Data] { type Throws = Nothing def extract(value: Data, ast: DataAst, mode: Mode[_ <: MethodConstraint]): mode.Wrap[Any, Throws] = mode.wrap(value.$normalize) } } @implicitNotFound("cannot extract type ${T} from ${D}.") abstract class Extractor[T, -D] extends Functor[({ type L[x] = Extractor[x, D] })#L, T] { ext => type Throws <: Exception def safeExtract(any: D, ast: DataAst, prefix: Option[Either[Int, String]], mode: Mode[_ <: MethodConstraint]): mode.Wrap[T, Throws] = mode.wrap { try mode.unwrap(extract(any, ast, mode)) catch { case e @ TypeMismatchException(_, _) => mode.exception(e) case e @ MissingValueException(_) => mode.exception(e) } } def extract(any: D, ast: DataAst, mode: Mode[_ <: MethodConstraint]): mode.Wrap[T, Throws] def rawMap[T2](fn: (T, Mode[_ <: MethodConstraint]) => T2): Extractor[T2, D] = new Extractor[T2, D] { def extract(any: D, ast: DataAst, mode: Mode[_ <: MethodConstraint]): mode.Wrap[T2, Throws] = mode.wrap(fn(mode.unwrap(ext.extract(any, ast, mode)), mode.generic)) } def filter(pred: T => Boolean): Extractor[T, D] { type Throws = ext.Throws with FilterException } = new Extractor[T, D] { type Throws = ext.Throws with FilterException def extract(any: D, ast: DataAst, mode: Mode[_ <: MethodConstraint]): mode.Wrap[T, Throws] = mode.wrap { val result = mode.unwrap(ext.extract(any, ast, mode)) if (pred(result)) result else mode.exception[T, FilterException](FilterException()) } } def orElse[TS >: T, T2 <: TS, D2 <: D]( ext2: => Extractor[T2, D2]): Extractor[TS, D2] { type Throws = DataGetException } = new Extractor[TS, D2] { type Throws = DataGetException def extract(any: D2, ast: DataAst, mode: Mode[_ <: MethodConstraint]): mode.Wrap[TS, Throws] = mode.wrap { try mode.unwrap(ext.extract(any, ast, mode)) catch { case e: Exception => mode.unwrap(ext2.extract(any, ast, mode)) } } } } ================================================ FILE: data/shared/src/main/scala/rapture/data/macros.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.data import rapture.base._ import rapture.core._ object Macros { // FIXME: Include enclosing position in the HashSet too val emittedWarnings = new collection.mutable.HashSet[String] def extractorMacro[T: c.WeakTypeTag, Data: c.WeakTypeTag, Th]( c: WhiteboxContext): c.Expr[Extractor[T, Data]] = { import c.universe._ import compatibility._ val extractorType = typeOf[Extractor[_, _]].typeSymbol.asType.toTypeConstructor val nameMapperType = typeOf[NameMapper[_, _]].typeSymbol.asType.toTypeConstructor val implicitSearchFailures = collection.mutable.ListMap[String, List[String]]().withDefault(_ => Nil) val extractionType = weakTypeOf[T].typeSymbol.asClass if (weakTypeOf[T] <:< typeOf[AnyVal]) { val param = paramLists(c)(declarations(c)(weakTypeOf[T]).collect { case t: MethodSymbol => t.asMethod }.find(_.isPrimaryConstructor).get).head.head val paramType = param.typeSignature val inferredExtractor = c.inferImplicitValue(appliedType(extractorType, List(paramType, weakTypeOf[Data])), false, false) c.Expr[Extractor[T, Data]](q"$inferredExtractor.map { new ${weakTypeOf[T]}(_) }") }/* else if (extractionType.isSealed) { val subclasses = extractionType.knownDirectSubclasses.to[List] def tsort(todo: Map[Set[String], Symbol], done: List[Symbol] = Nil): List[Symbol] = if(todo.isEmpty) done else { val move = todo.filter { case (key, v) => (todo - key).forall(!_._1.subsetOf(key)) } tsort(todo -- move.map(_._1), move.map(_._2).to[List] ::: done) } val paramSets = subclasses.map { subclass => (declarations(c)(subclass.asType.toType).collect { case m: MethodSymbol if m.isCaseAccessor => m.asMethod }.map(_.name.decodedName.toString).to[Set], subclass) } val sortedExtractors = tsort(paramSets.toMap).map { subclass => val searchType = appliedType(extractorType, List(subclass.asType.toType, weakTypeOf[Data])) c.inferImplicitValue(searchType, false, false) } val NothingType = weakTypeOf[Nothing] val throwsType = sortedExtractors.last.tpe.member(typeName(c, "Throws")).typeSignature val combinedExtractors = sortedExtractors.reduceLeft { (left, right) => q"$left.orElse($right)" } c.Expr[Extractor[T, Data]](q""" (new _root_.rapture.data.Extractor[${weakTypeOf[T]}, ${weakTypeOf[Data]}] { def extract(data: ${weakTypeOf[Data]}, ast: _root_.rapture.data.DataAst, mode: _root_.rapture.core.Mode[_ <: _root_.rapture.core.MethodConstraint]): mode.Wrap[${weakTypeOf[ T]}, Throws] = mode.wrap { $combinedExtractors } }).asInstanceOf[_root_.rapture.data.Extractor[${weakTypeOf[T]}, ${weakTypeOf[Data]}] { type Throws = ${throwsType} }] """) }*/ else { require(weakTypeOf[T].typeSymbol.asClass.isCaseClass) val typeDeclaration = companion(c)(weakTypeOf[T].typeSymbol).typeSignature val valueParameters = paramLists(c)(declaration(c)(typeDeclaration, termName(c, "apply")).asMethod) val defaults = valueParameters.flatten.map(_.asTerm.isParamWithDefault).zipWithIndex.filter(_._1).map(_._2 + 1).to[Set] // FIXME integrate these into a fold var throwsTypes: Set[Type] = Set(typeOf[DataGetException]) val params = declarations(c)(weakTypeOf[T]).collect { case m: MethodSymbol if m.isCaseAccessor => m.asMethod }.zipWithIndex map { case (p, idx) => val nameMappingImplicit = c.inferImplicitValue(appliedType(nameMapperType, List(weakTypeOf[T], weakTypeOf[Data])), false, false) val deref = q"""data.selectDynamic($nameMappingImplicit.encode(${p.name.decodedName.toString}))""" val NothingType = weakTypeOf[Nothing] val inferredImplicit = try c.inferImplicitValue(appliedType(extractorType, List(p.returnType, weakTypeOf[Data])), false, false) catch { case e: Exception => implicitSearchFailures(p.returnType.toString) ::= p.name.decodedName.toString null } val t = try { inferredImplicit.tpe.member(typeName(c, "Throws")).typeSignature match { case NothingType => List() case refinedType: RefinedType => refinedType.parents case typ: Type => List(typ) case _ => ??? } } catch { case e: Exception => List() } if(!defaults.contains(idx + 1)) throwsTypes ++= t // Borrowed from Shapeless def companionRef(tpe: Type): Tree = { val global = c.universe.asInstanceOf[scala.tools.nsc.Global] val gTpe = tpe.asInstanceOf[global.Type] val pre = gTpe.prefix val sym = gTpe.typeSymbol val cSym = sym.companionSymbol if (cSym != NoSymbol) global.gen.mkAttributedRef(pre, cSym).asInstanceOf[Tree] else Ident(tpe.typeSymbol.name.toTermName) } if (defaults.contains(idx + 1)) q""" mode.unwrap(try $deref.as($inferredImplicit, mode.generic) catch { case e: Exception => mode.wrap(${companionRef(weakTypeOf[T])}.${termName( c, "apply$default$" + (idx + 1))}.asInstanceOf[${p.returnType}]) }, ${"." + p.name.decodedName}) """ else q""" mode.unwrap($deref.as($inferredImplicit, mode.generic), ${"." + p.name.decodedName}) """ } if (implicitSearchFailures.nonEmpty) { val paramStrings = implicitSearchFailures map { case (t, p1 :: Nil) => s"parameter `$p1' of type $t" case (t, p1 :: ps) => s"parameters ${ps.map(v => s"`$v'").mkString(", ")} and `$p1' of type $t" case (t, Nil) => ??? // Doesn't happen } val errorString = paramStrings.to[List].reverse match { case t1 :: Nil => t1 case t1 :: ts => s"${ts.mkString(", ")} and $t1" case Nil => ??? // Doesn't happen } val plural = if (implicitSearchFailures.flatMap(_._2).size > 1) s"${weakTypeOf[Data].typeSymbol.name} extractors" else s"a ${weakTypeOf[Data].typeSymbol.name} extractor" val err = s"Could not generate a ${weakTypeOf[Data].typeSymbol.name} extractor for case " + s"class ${weakTypeOf[T].typeSymbol.name} because $plural for $errorString could not " + s"be found" if (!emittedWarnings.contains(err)) { emittedWarnings += err c.warning(NoPosition, err) } } require(implicitSearchFailures.isEmpty) val construction = c.Expr[T](q"""new ${weakTypeOf[T]}(..$params)""") c.Expr[Extractor[T, Data]](q""" (new _root_.rapture.data.Extractor[${weakTypeOf[T]}, ${weakTypeOf[Data]}] { def extract(data: ${weakTypeOf[Data]}, ast: _root_.rapture.data.DataAst, mode: _root_.rapture.core.Mode[_ <: _root_.rapture.core.MethodConstraint]): mode.Wrap[${weakTypeOf[ T]}, Throws] = mode.wrap { ${construction} } }).asInstanceOf[_root_.rapture.data.Extractor[${weakTypeOf[T]}, ${weakTypeOf[Data]}] { type Throws = ${normalize(c)(typeIntersection(c)(throwsTypes.to[List]))} }] """) } } def serializerMacro[T: c.WeakTypeTag, Data: c.WeakTypeTag](c: WhiteboxContext)( ast: c.Expr[DataAst]): c.Expr[Serializer[T, Data]] = { import c.universe._ import compatibility._ val tpe = weakTypeOf[T].typeSymbol.asClass val nameMapperType = typeOf[NameMapper[_, _]].typeSymbol.asType.toTypeConstructor val serializer = typeOf[Serializer[_, _]].typeSymbol.asType.toTypeConstructor if (weakTypeOf[T] <:< typeOf[AnyVal]) { val param = paramLists(c)(declarations(c)(weakTypeOf[T]).collect { case t: MethodSymbol => t.asMethod }.find(_.isPrimaryConstructor).get).head.head val paramName = param.name.decodedName.toString val paramType = param.typeSignature val inferredSerializer = c.inferImplicitValue(appliedType(serializer, List(paramType, weakTypeOf[Data])), false, false) val newName = termName(c, freshName(c)("param$")) c.Expr[Serializer[T, Data]](q"$inferredSerializer.contramap[${weakTypeOf[T]}](_.${termName(c, paramName)})") } else { if (tpe.isCaseClass) { val params = declarations(c)(weakTypeOf[T]).collect { case m: MethodSymbol if m.isCaseAccessor => m.asMethod }.map { p => val imp = c.inferImplicitValue(appliedType(serializer, List(p.returnType, weakTypeOf[Data])), false, false) val appliedSerializerType = appliedType(nameMapperType, List(weakTypeOf[T], weakTypeOf[Data])) val nameMapperImplicit = c.inferImplicitValue(appliedSerializerType, false, false) q"""($nameMapperImplicit.encode(${p.name.decodedName.toString}), $imp.serialize(${termName(c, "t")}.${p}))""" } c.Expr[Serializer[T, Data]](q"""new _root_.rapture.data.Serializer[${weakTypeOf[T]}, ${weakTypeOf[Data]}] { def serialize(t: ${weakTypeOf[T]}): _root_.scala.Any = $ast.fromObject(_root_.scala.collection.immutable.ListMap(..$params).filterNot { v => $ast.isNull(v._2) }) }""") } else if (tpe.isSealed) { val cases = tpe.knownDirectSubclasses.to[List] val caseClauses = cases.map { sc => val fullySpecifiedSerializer = appliedType(serializer, List(sc.asType.toType, weakTypeOf[Data])) val caseSerializer = c.inferImplicitValue(fullySpecifiedSerializer, false, false) val pattern = pq"v: ${sc.asClass}" cq"${pattern} => $caseSerializer.serialize(v)" } c.Expr[Serializer[T, Data]](q"""new _root_.rapture.data.Serializer[${weakTypeOf[T]}, ${weakTypeOf[Data]}] { def serialize(t: ${weakTypeOf[T]}): _root_.scala.Any = t match { case ..$caseClauses } }""") } else throw new Exception() } } } ================================================ FILE: data/shared/src/main/scala/rapture/data/serializers.scala ================================================ /* Rapture, version 2.0.0. Copyright 2010-2016 Jon Pretty, Propensive Ltd. The primary distribution site is http://rapture.io/ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package rapture.data import rapture.core._ import scala.annotation._ @implicitNotFound( "Cannot serialize type $"+"{T} to $"+"{D}. Please provide an implicit Serializer " + "of type $"+"{T}.") trait Serializer[T, -D] { ser => def serialize(t: T): Any def contramap[T2](fn: T2 => T) = new Serializer[T2, D] { def serialize(t: T2): Any = ser.serialize(fn(t)) } } ================================================ FILE: doc/core-scalaz.md ================================================ [![Build Status](https://travis-ci.org/propensive/rapture-core-scalaz.png?branch=master)](https://travis-ci.org/propensive/rapture-core-scalaz) # Rapture Core/Scalaz The Rapture Core/Scalaz project provides integration between Rapture Core and Scalaz 7. So far this consists only of two additional return-type strategies: - A validation return-type strategy for returning Scalaz `Validation`s - A task return-type strategy, for returning Scalaz `Task`s ### Availability #### Building from source To build Rapture Core Scalaz from source, follow these steps: ``` git clone git@github.com:propensive/rapture-core-scalaz.git cd rapture-core-scalaz sbt package ``` If the compilation is successful, the compiled JAR file should be found in target/scala-2.10 ### Return-Type Strategies By importing `scalazStrategy.returnValidation`, every fallible method will return a result of type `Validation[E, T]`. ```scala > import scalazStrategy.returnValidation > Json.parse("{}") res: Validation[ParseException, Json] = {} ``` If using the standard `strategy.explicit` you can get a `Validation` by calling `.valid` on the unexpanded result. ```scala > import strategy.explicit > Json.parse("{}").valid res: Validation[ParseException, Json] = {} ``` Alternatively, by importing `scalazStrategy.returnTasks`, and providing a valid implicit `ExecutorService`, every fallible method in Rapture will return a Scalaz `Task`. ```scala > import scalazStrategy.returnTasks > implicit val exec = scalaz.concurrent.Strategy.DefaultExecutorService > Json.parse("{}") res: Task[Json] = Task@7961b5f3 ``` You can call `.task` on an unexpanded result when using `strategy.explicit` to get the same effect. See https://github.com/propensive/rapture-core#return-type-strategies for more information on Rapture Core return-type strategies. ================================================ FILE: doc/core.md ================================================ [![Build Status](https://travis-ci.org/propensive/rapture-core.png?branch=master)](https://travis-ci.org/propensive/rapture-core) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/propensive/rapture?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) # Rapture Core The Rapture Core project provides a common foundation upon which other Rapture projects are based, however it provides utilities which may be useful in any project. Namely, - Modes (previously called return-type strategies) - A lightweight abstraction on time libraries, and implementations for Java time - A tiny (but growing) collection of conveniences for working within the REPL - An alias for `implicitly` - A really simple implementation of actors, based on `scala.concurrent` - Reusable string serializers and deserializers, with implicit-based configurable number formatting - Miscellaneous other small tools and utilities ### Availability Rapture Core 1.1.0 is available under the Apache 2.0 License from Maven Central with group ID `com.propensive` and artifact ID `rapture-core_2.10`. #### SBT You can include Rapture Core as a dependency in your own project by adding the following library dependency to your build file: ```scala libraryDependencies ++= Seq("com.propensive" %% "rapture-core" % "1.1.0") ``` #### Maven If you use Maven, include the following dependency: ```xml com.propensive rapture-core_2.11 1.1.0 ``` #### Download You can download Rapture Core directly from the [Rapture website](http://rapture.io/) Rapture Core depends on Scala 2.11, but has no other dependencies. #### Building from source To build Rapture Core from source, follow these steps: ``` git clone -b scala-2.11 git@github.com:propensive/rapture-core.git cd rapture-core sbt package ``` If the compilation is successful, the compiled JAR file should be found in target/scala-2.11.0-RC4 ### Modes Rapture's modes allow library methods to be written in such a way that they may be wrapped in another function (and have a different return type) at the call site. This pattern allows *users* of the library to choose the return type and additional pre- and post-execution processing to be performed, depending on their needs. For example, using Rapture JSON, given one of the imported modes, ```scala import modes.returnEither._ Json.parse("[1, 2, 3]") ``` will have return type `Either[ParseException, Json]`. Hopefully the parsing succeeded, and the return type will be `Right[Json]` rather than `Left[ParseException]`. Alternatively, given a different imported mode, we will get a different return type. ```scala import modes.returnFuture._ Json.parse("[1, 2, 3]") ``` This will immediately return a `Future[Json]`, from which the result can be obtained once processing completes. A selection of modes are provided: - `modes.throwExceptions._` - does no additional processing, and simply returns the value, leaving any thrown exceptions unhandled. This is the default. - `modes.returnEither._` - captures successful results in the `Right` branch of an `Either`, or exceptions in the `Left` branch. - `modes.returnOption._` - returns an `Option` of the result, where the exceptional case collapses to `None`. - `modes.returnTry` - wraps the result in a `scala.util.Try`. - `modes.returnFuture._` - wraps the result in a `scala.concurrent.Future`; requires an implicit ExecutionContext. - `modes.timeExecution._` - times the duration of carrying out the execution, returning a tuple of the return value and the time taken; requires an implicit `rapture.core.TimeSystem`. - `modes.keepCalmAndCarryOn._` - catches exceptions and silently returns them as null; this is strongly discouraged! - `modes.explicit._` - returns an instance of `Explicit` which requires the mode to be explicitly specified at the call site every time. Multiple modes can be composed, should this be required, for example, ```scala implicit val handler = modes.returnTry compose modes.timeExecution ``` #### The `modally` method The `modally` method takes a lambda and evaluates it in the current mode, e.g. ```scala import modes.returnTry._ modally { Class.forName(className) } // Returns a Try[Class[_]] ``` This may provide a convenient (and adaptive) alternative to `try`/`catch`. #### Writing methods to use modes To transform a method like this, ```scala def doSomething[T](arg: String, arg2: T): Double = { // method body } ``` into one which offers end-users a choice of mode, include an implicit Mode parameter, and wrap your method body and return type, like this, ```scala def doSomething[T](arg: String, arg2: T)(implicit mode: Mode[_]): mode.Wrap[Double, Exception] = mode wrap { // method body } ``` If you know that your method body will only throw exceptions of a certain type, you can specify this in the method return type in place of `Exception`, which may make processing exceptions easier when using a mode which captures them (e.g. `modes.returnEither`). #### Conveniences for existing methods You may already have existing implementations of methods you would like to modify to utilize modes, but which already have a return type which captures (or discards) the exception rather than throwing it. The `wrapEither`, `wrapTry` and `wrapOption` methods are provided as alternatives to `wrap` to transform methods which already return `Either`, `Try` or `Option` respectively. Note, however, that because `wrapOption` loses its exception in the return type, that there is no way to recover it. This means that when using it with modes such as `returnEither`, that the exceptional cases will always return as a `java.util.NoSuchElementException`. ### REPL utilities When working within the REPL, it is useful to receive the immediate feedback that a fallible operation has failed (as the `throwExceptions` mode provides), but seeing large and unwieldy stack traces can be annoying. By including the following import, ```scala import rapture.core.repl._ ``` stack traces from fallible Rapture methods will be suppressed, and a short error message will be displayed instead. If you would like to view the stack trace, it can be rethrown by calling `repl.lastException`. ### Time System Abstraction Many APIs take parameters or return values which represent time. Unfortunately, there is no standard for representing entities like instants and durations. Rapture Core provides a general type class for defining these types and methods for creating them, and provides two simple implementations: - `timeSystems.numeric` - uses `Long`s to represent both instants and durations. - `timeSystems.javaUtil` - uses `java.util.Date`s to represent instants, and `Long`s to represent durations. ### Alias for `implicitly` Context-bounds provide a nice, lightweight syntax for working with type classes in Scala, however while explicitly specifying an implicit parameter necessarily provides a named handle for that implicit, context-bounds force us to make repeated use of the `implicitly` method in order to use the type class. This can make using context-bounds more cumbersome than they deserve. Rapture Core introduces an alias for `implicitly` named `?`. Generally speaking, any occurrence of `implicitly` can be replaced by a `?`. This is particularly useful when calling methods which take multiple implicit parameters and you would like to specify one of these explicitly. For example, a method like this: ```scala def performAction[T](v: Int)(implicit alpha: Alpha[T], beta: Beta, gamma: Gamma) = { ... } ``` may now be called quite concisely using ```scala performAction(42)(?, ?, myGamma) ``` if we only wanted to specify the parameter `myGamma` explicitly. ================================================ FILE: doc/html.md ================================================ # Rapture HTML Rapture HTML provides simple but typesafe support for working with HTML5 documents in Scala, enforcing the constraints of the document object model. It is a small library which works independently of any other web framework. Rapture HTML is part of the [Rapture](http://rapture.io/) project. ## Features - Clean, elegant and intuitive DSL for creating HTML tree structures - Nesting constraints on HTML nodes are enforced by the type system - HTML attributes may only be applied to appropriate elements - Customized explanatory error messages given for badly-nested elements and mismatched attributes - Attributes generally use rich Scala types, not `String`s - HTML Document object model specification is concise, extensible and maintainable - Lightweight, typesafe accessors for navigating elements and attributes ## Availability Rapture HTML 0.1.0 is available under the *Apache 2.0 License* from Maven Central with group ID `com.propensive` and artifact ID `rapture-html_2.10`. ### SBT You can include Rapture HTML as a dependency in your own project by adding the following library dependency to your build file: ```scala libraryDependencies ++= Seq("com.propensive" %% "rapture-html" % "0.1.0") ``` ### Maven If you use Maven, include the following dependency: ```xml com.propensive rapture-html_2.10 0.1.0 ``` ### Building from source with SBT To build Rapture HTML from source, follow these steps: ``` git clone git@github.com:propensive/rapture-html.git cd rapture-html sbt package ``` If the compilation is successful, the compiled JAR file should be found in the directory for the appropriate Scala version in the `target` directory. ## Status Rapture HTML is *experimental*. This means that the API is undergoing change, and new features may be added or removed without changes being documented. ## Contributing Rapture HTML -- like all the Rapture projects -- openly welcomes contributions! We would love to receive pull requests of bugfixes and enhancements from other developers. To avoid potential wasted effort, bugs should first be reported on the Github issue tracker, and it's normally a good idea to talk about enhancements on the Rapture mailing list before embarking on any development. Alternatively, just send Jon Pretty (@propensive) a tweet to start a conversation. # Using Rapture HTML First of all, import the following: ``` import rapture.html._ import htmlSyntax._ ``` The `htmlSyntax` package comprises a large namespace containing all HTML tag and attribute names, so it is recommended to import this only into scopes where it is to be immediately used. ## HTML Elements A simple HTML element, with no attributes or child elements is as simple as writing the tag name, with its first letter capitalized, for example, ``` //
val html = Form ``` To add attributes to an empty tag, list them like named parameters to the element, like this, ``` // val html = Form(id = 'signup, method = Post) ``` Note that the types of the attribute values are not, in general, `String`s. Each attribute is typed according to the values it allows to be assigned. A later version of Rapture HTML will offer multiple alternative types (originating from third-party projects) for HTML attribute values. Mostly, attribute names in Rapture HTML are identical to their native HTML5 counterparts, but in several cases it has been necessary to tweak the names. - Any attribute containing a `-` is converted to camel case, for example `http-equiv` becomes `httpEquiv` - The `class` attribute becomes `classes` - The `type` attrubute becomes `typeName` - The `for` attribute becomes `forName` - Custom `data-` attributes become `data.` Better suggestions for more consistent handling of these attributes would be very welcome. An HTML element may also have child elements, by adding them as parameters in a second parameter block, like so, ``` //