├── .github └── workflows │ ├── ci.yml │ └── clean.yml ├── .gitignore ├── .mergify.yml ├── .scala-steward.conf ├── .scalafmt.conf ├── CHANGELOG.md ├── LICENSE ├── README.md ├── build.sbt ├── core └── src │ ├── main │ └── scala │ │ └── org │ │ └── typelevel │ │ └── unique │ │ └── Unique.scala │ └── test │ └── scala │ └── org │ └── typelevel │ └── unique │ ├── CompileTest.scala │ ├── UniqueSuite.scala │ └── UniqueTests.scala ├── docs ├── Gemfile ├── Gemfile.lock └── src │ └── main │ └── mdoc │ └── index.md └── project ├── build.properties └── plugins.sbt /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by sbt-github-actions using the 2 | # githubWorkflowGenerate task. You should add and commit this file to 3 | # your git repository. It goes without saying that you shouldn't edit 4 | # this file by hand! Instead, if you wish to make changes, you should 5 | # change your sbt build configuration to revise the workflow description 6 | # to meet your needs, then regenerate this file. 7 | 8 | name: Continuous Integration 9 | 10 | on: 11 | pull_request: 12 | branches: ['*', series/*] 13 | push: 14 | branches: ['*', series/*] 15 | tags: [v*] 16 | 17 | env: 18 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 19 | SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} 20 | SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} 21 | PGP_SECRET: ${{ secrets.PGP_SECRET }} 22 | 23 | jobs: 24 | build: 25 | name: Build and Test 26 | strategy: 27 | matrix: 28 | os: [ubuntu-latest] 29 | scala: [2.12.15, 2.13.8, 3.0.2] 30 | java: [temurin@8, temurin@11, temurin@17] 31 | runs-on: ${{ matrix.os }} 32 | steps: 33 | - name: Checkout current branch (full) 34 | uses: actions/checkout@v2 35 | with: 36 | fetch-depth: 0 37 | 38 | - name: Setup Java (temurin@8) 39 | if: matrix.java == 'temurin@8' 40 | uses: actions/setup-java@v2 41 | with: 42 | distribution: temurin 43 | java-version: 8 44 | 45 | - name: Setup Java (temurin@11) 46 | if: matrix.java == 'temurin@11' 47 | uses: actions/setup-java@v2 48 | with: 49 | distribution: temurin 50 | java-version: 11 51 | 52 | - name: Setup Java (temurin@17) 53 | if: matrix.java == 'temurin@17' 54 | uses: actions/setup-java@v2 55 | with: 56 | distribution: temurin 57 | java-version: 17 58 | 59 | - name: Cache sbt 60 | uses: actions/cache@v2 61 | with: 62 | path: | 63 | ~/.sbt 64 | ~/.ivy2/cache 65 | ~/.coursier/cache/v1 66 | ~/.cache/coursier/v1 67 | ~/AppData/Local/Coursier/Cache/v1 68 | ~/Library/Caches/Coursier/v1 69 | key: ${{ runner.os }}-sbt-cache-v2-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }} 70 | 71 | - if: matrix.scala == '2.13.8' 72 | uses: ruby/setup-ruby@v1 73 | with: 74 | ruby-version: 2.7 75 | 76 | - if: matrix.scala == '2.13.8' 77 | run: gem install bundler 78 | 79 | - if: matrix.scala == '2.13.8' 80 | run: bundle install --gemfile=docs/Gemfile 81 | 82 | - name: Check that workflows are up to date 83 | run: sbt ++${{ matrix.scala }} githubWorkflowCheck 84 | 85 | - name: Check formatting 86 | run: sbt ++${{ matrix.scala }} fmtCheck 87 | 88 | - name: Check binary issues 89 | run: sbt ++${{ matrix.scala }} mimaReportBinaryIssues 90 | 91 | - name: Compile 92 | run: sbt ++${{ matrix.scala }} Test/compile 93 | 94 | - name: Run tests 95 | run: sbt ++${{ matrix.scala }} test 96 | 97 | - name: Build the microsite 98 | if: matrix.scala == '2.13.8' 99 | run: sbt ++${{ matrix.scala }} docs/makeMicrosite 100 | 101 | - name: Compress target directories 102 | run: tar cf targets.tar target core/.js/target core/.jvm/target project/target 103 | 104 | - name: Upload target directories 105 | uses: actions/upload-artifact@v2 106 | with: 107 | name: target-${{ matrix.os }}-${{ matrix.scala }}-${{ matrix.java }} 108 | path: targets.tar 109 | 110 | publish: 111 | name: Publish Artifacts 112 | needs: [build] 113 | if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v')) 114 | strategy: 115 | matrix: 116 | os: [ubuntu-latest] 117 | scala: [2.13.8] 118 | java: [temurin@8] 119 | runs-on: ${{ matrix.os }} 120 | steps: 121 | - name: Checkout current branch (full) 122 | uses: actions/checkout@v2 123 | with: 124 | fetch-depth: 0 125 | 126 | - name: Setup Java (temurin@8) 127 | if: matrix.java == 'temurin@8' 128 | uses: actions/setup-java@v2 129 | with: 130 | distribution: temurin 131 | java-version: 8 132 | 133 | - name: Setup Java (temurin@11) 134 | if: matrix.java == 'temurin@11' 135 | uses: actions/setup-java@v2 136 | with: 137 | distribution: temurin 138 | java-version: 11 139 | 140 | - name: Setup Java (temurin@17) 141 | if: matrix.java == 'temurin@17' 142 | uses: actions/setup-java@v2 143 | with: 144 | distribution: temurin 145 | java-version: 17 146 | 147 | - name: Cache sbt 148 | uses: actions/cache@v2 149 | with: 150 | path: | 151 | ~/.sbt 152 | ~/.ivy2/cache 153 | ~/.coursier/cache/v1 154 | ~/.cache/coursier/v1 155 | ~/AppData/Local/Coursier/Cache/v1 156 | ~/Library/Caches/Coursier/v1 157 | key: ${{ runner.os }}-sbt-cache-v2-${{ hashFiles('**/*.sbt') }}-${{ hashFiles('project/build.properties') }} 158 | 159 | - name: Download target directories (2.12.15) 160 | uses: actions/download-artifact@v2 161 | with: 162 | name: target-${{ matrix.os }}-2.12.15-${{ matrix.java }} 163 | 164 | - name: Inflate target directories (2.12.15) 165 | run: | 166 | tar xf targets.tar 167 | rm targets.tar 168 | 169 | - name: Download target directories (2.13.8) 170 | uses: actions/download-artifact@v2 171 | with: 172 | name: target-${{ matrix.os }}-2.13.8-${{ matrix.java }} 173 | 174 | - name: Inflate target directories (2.13.8) 175 | run: | 176 | tar xf targets.tar 177 | rm targets.tar 178 | 179 | - name: Download target directories (3.0.2) 180 | uses: actions/download-artifact@v2 181 | with: 182 | name: target-${{ matrix.os }}-3.0.2-${{ matrix.java }} 183 | 184 | - name: Inflate target directories (3.0.2) 185 | run: | 186 | tar xf targets.tar 187 | rm targets.tar 188 | 189 | - name: Import signing key 190 | run: echo $PGP_SECRET | base64 -d | gpg --import 191 | 192 | - name: Release 193 | run: sbt ++${{ matrix.scala }} release 194 | 195 | - name: Publish the microsite 196 | run: sbt ++${{ matrix.scala }} docs/publishMicrosite 197 | -------------------------------------------------------------------------------- /.github/workflows/clean.yml: -------------------------------------------------------------------------------- 1 | # This file was automatically generated by sbt-github-actions using the 2 | # githubWorkflowGenerate task. You should add and commit this file to 3 | # your git repository. It goes without saying that you shouldn't edit 4 | # this file by hand! Instead, if you wish to make changes, you should 5 | # change your sbt build configuration to revise the workflow description 6 | # to meet your needs, then regenerate this file. 7 | 8 | name: Clean 9 | 10 | on: push 11 | 12 | jobs: 13 | delete-artifacts: 14 | name: Delete Artifacts 15 | runs-on: ubuntu-latest 16 | env: 17 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 18 | steps: 19 | - name: Delete artifacts 20 | run: | 21 | # Customize those three lines with your repository and credentials: 22 | REPO=${GITHUB_API_URL}/repos/${{ github.repository }} 23 | 24 | # A shortcut to call GitHub API. 25 | ghapi() { curl --silent --location --user _:$GITHUB_TOKEN "$@"; } 26 | 27 | # A temporary file which receives HTTP response headers. 28 | TMPFILE=/tmp/tmp.$$ 29 | 30 | # An associative array, key: artifact name, value: number of artifacts of that name. 31 | declare -A ARTCOUNT 32 | 33 | # Process all artifacts on this repository, loop on returned "pages". 34 | URL=$REPO/actions/artifacts 35 | while [[ -n "$URL" ]]; do 36 | 37 | # Get current page, get response headers in a temporary file. 38 | JSON=$(ghapi --dump-header $TMPFILE "$URL") 39 | 40 | # Get URL of next page. Will be empty if we are at the last page. 41 | URL=$(grep '^Link:' "$TMPFILE" | tr ',' '\n' | grep 'rel="next"' | head -1 | sed -e 's/.*.*//') 42 | rm -f $TMPFILE 43 | 44 | # Number of artifacts on this page: 45 | COUNT=$(( $(jq <<<$JSON -r '.artifacts | length') )) 46 | 47 | # Loop on all artifacts on this page. 48 | for ((i=0; $i < $COUNT; i++)); do 49 | 50 | # Get name of artifact and count instances of this name. 51 | name=$(jq <<<$JSON -r ".artifacts[$i].name?") 52 | ARTCOUNT[$name]=$(( $(( ${ARTCOUNT[$name]} )) + 1)) 53 | 54 | id=$(jq <<<$JSON -r ".artifacts[$i].id?") 55 | size=$(( $(jq <<<$JSON -r ".artifacts[$i].size_in_bytes?") )) 56 | printf "Deleting '%s' #%d, %'d bytes\n" $name ${ARTCOUNT[$name]} $size 57 | ghapi -X DELETE $REPO/actions/artifacts/$id 58 | done 59 | done 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | .idea/ 3 | # vim 4 | *.sw? 5 | 6 | # Ignore [ce]tags filestags 7 | 8 | .bsp 9 | .bloop 10 | .metals 11 | -------------------------------------------------------------------------------- /.mergify.yml: -------------------------------------------------------------------------------- 1 | pull_request_rules: 2 | - name: automatically merge scala-steward's PRs 3 | conditions: 4 | - author=scala-steward 5 | - status-success=Travis CI - Pull Request 6 | - body~=labels:.*semver-patch.* 7 | actions: 8 | merge: 9 | method: merge 10 | -------------------------------------------------------------------------------- /.scala-steward.conf: -------------------------------------------------------------------------------- 1 | updates.pin = [ 2 | { groupId = "org.typelevel", artifactId="cats-effect", version = "2." }, 3 | { groupId = "org.scala-lang", artifactId = "scala3-library", version = "3.0." }, 4 | { groupId = "org.scala-lang", artifactId = "scala3-library_sjs1", version = "3.0." } 5 | ] 6 | -------------------------------------------------------------------------------- /.scalafmt.conf: -------------------------------------------------------------------------------- 1 | version = 3.5.3 2 | align.openParenCallSite = true 3 | align.openParenDefnSite = true 4 | maxColumn = 120 5 | continuationIndent.defnSite = 2 6 | assumeStandardLibraryStripMargin = true 7 | danglingParentheses.preset = true 8 | rewrite.rules = [AvoidInfix, SortImports, RedundantParens, SortModifiers] 9 | newlines.afterCurlyLambda = preserve 10 | docstrings.style = Asterisk 11 | docstrings.oneline = unfold 12 | runner.dialect = scala213source3 13 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # changelog 2 | 3 | This file summarizes **notable** changes for each release, but does not describe internal changes unless they are particularly exciting. This change log is ordered chronologically, so each release contains all changes described below it. 4 | 5 | ---- 6 | 7 | ## New and Noteworthy for Version 2.1.1 8 | 9 | - cats-2.4.2 10 | - cats-effect-2.3.3 11 | - Dropped Scala-3.0.0-M2 12 | - Added Scala 3.0.0-RC1 13 | 14 | ## New and Noteworthy for Version 2.1.0-M7 15 | 16 | - Published under `org.typelevel` 17 | - Base package moved to `org.typelevel.unique` 18 | - cats-2.3.1 19 | - cats-effect-2.3.1 20 | - Cross-built for Scala 3.0.0-M3, 3.0.0-M2, 2.13, and 2.12 21 | 22 | ## New and Noteworthy for Version 2.0.0 23 | 24 | - cats 2.0.0 25 | - cats-effect 2.0.0 26 | 27 | ## New and Noteworthy for Version 2.0.0-RC1 28 | 29 | - Update Off Scala 2.13 30 | - Cats 2.0-RC1 31 | - Cats-Effect 2.0-RC1 32 | - Got off Catsuite 33 | - Drop 2.11 Support 34 | 35 | ## New and Noteworthy for Version 1.0.0 36 | 37 | Stable Release of `unique`. This library will maintain binary compatibility moving forward for the forseeable future. 38 | 39 | - [#18](https://github.com/ChristopherDavenport/unique/pull/18) Scala 2.13 Integration 40 | 41 | Upgrades: 42 | 43 | - cats 1.6.0 44 | - cats-effect 1.2.0 45 | 46 | ## New and Noteworthy for Version 0.1.1 47 | 48 | Incorporate Useability Improvements 49 | 50 | - [#3](https://github.com/ChristopherDavenport/unique/pull/3) Better `toString` for readability, and extends `Serializable`. 51 | - [#2](https://github.com/ChristopherDavenport/unique/pull/2) Add MiMa for binary compatiblity checking. 52 | 53 | ## New and Noteworthy for Version 0.1.0 54 | 55 | Initial Release of `unique`. Create unique values that are equal only to the same effectfully created value. Scala.js project released as well. Fully law checked. [cats-effect](https://github.com/typelevel/cats-effect) is the only dependency and this version is based off 1.0.0. 56 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018 Christopher Davenport 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # unique [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.typelevel/unique_2.12/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.typelevel/unique_2.12) ![Continuous Integration](https://github.com/typelevel/unique/workflows/Continuous%20Integration/badge.svg) 2 | 3 | This is a shared library for creating and managing unique values in a referentially transparent way. 4 | 5 | ## End-of-life 6 | 7 | This project is now end-of-life. Its functionality has been [adopted into Cats-Effect 3](https://typelevel.github.io/unique). We will consider releases for [security](https://github.com/typelevel/unique/security/policy) or other significant tales of woe, but routine maintenance has ceased. 8 | 9 | ## [Head on over to the microsite](https://typelevel.github.io/unique) 10 | 11 | ## Quick Start 12 | 13 | To use Unique in an existing SBT project with Scala 2.12 or a later version, add the following dependencies to your 14 | `build.sbt` depending on your needs: 15 | 16 | ```scala 17 | libraryDependencies ++= Seq( 18 | "org.typelevel" %% "unique" % "" 19 | ) 20 | ``` 21 | -------------------------------------------------------------------------------- /build.sbt: -------------------------------------------------------------------------------- 1 | import sbtcrossproject.CrossPlugin.autoImport.{crossProject, CrossType} 2 | 3 | val Scala212 = "2.12.15" 4 | val Scala213 = "2.13.8" 5 | val Scala3 = "3.0.2" 6 | 7 | ThisBuild / organization := "org.typelevel" 8 | ThisBuild / baseVersion := "2.2" 9 | ThisBuild / crossScalaVersions := Seq(Scala212, Scala213, Scala3) 10 | ThisBuild / scalaVersion := crossScalaVersions.value.filter(_.startsWith("2.")).last 11 | 12 | ThisBuild / publishGithubUser := "christopherdavenport" 13 | ThisBuild / publishFullName := "Christopher Davenport" 14 | 15 | ThisBuild / spiewakMainBranches := Seq("main", "series/2.x") 16 | 17 | ThisBuild / sonatypeCredentialHost := "s01.oss.sonatype.org" 18 | 19 | enablePlugins(SonatypeCiReleasePlugin) 20 | 21 | lazy val unique = project 22 | .in(file(".")) 23 | .enablePlugins(NoPublishPlugin) 24 | .aggregate(coreJVM, coreJS) 25 | .settings(commonSettings, releaseSettings, publish / skip := true) 26 | 27 | lazy val core = crossProject(JSPlatform, JVMPlatform) 28 | .crossType(CrossType.Pure) 29 | .in(file("core")) 30 | .settings(commonSettings, releaseSettings) 31 | .settings( 32 | name := "unique" 33 | ) 34 | .jsSettings(scalaJSLinkerConfig ~= (_.withModuleKind(ModuleKind.CommonJSModule))) 35 | 36 | lazy val docs = project 37 | .in(file("docs")) 38 | .disablePlugins(MimaPlugin, NoPublishPlugin) 39 | .enablePlugins(MicrositesPlugin) 40 | .settings( 41 | commonSettings, 42 | releaseSettings, 43 | micrositeSettings, 44 | publish / skip := true, 45 | githubWorkflowArtifactUpload := false 46 | ) 47 | .dependsOn(coreJVM) 48 | 49 | lazy val coreJVM = core.jvm 50 | lazy val coreJS = core.js 51 | 52 | val catsV = "2.7.0" 53 | val catsEffectV = "2.5.4" 54 | val disciplineMunitV = "1.0.9" 55 | val munitCatsEffectV = "1.0.7" 56 | val kindProjectorV = "0.13.2" 57 | 58 | lazy val contributors = Seq( 59 | "ChristopherDavenport" -> "Christopher Davenport" 60 | ) 61 | 62 | // General Settings 63 | lazy val commonSettings = Seq( 64 | libraryDependencies ++= ( 65 | if (ScalaArtifacts.isScala3(scalaVersion.value)) Nil 66 | else 67 | Seq( 68 | compilerPlugin(("org.typelevel" % "kind-projector" % kindProjectorV).cross(CrossVersion.full)) 69 | ) 70 | ), 71 | libraryDependencies ++= Seq( 72 | "org.typelevel" %%% "cats-core" % catsV, 73 | "org.typelevel" %%% "cats-effect" % catsEffectV, 74 | "org.typelevel" %%% "discipline-munit" % disciplineMunitV % Test, 75 | "org.typelevel" %%% "munit-cats-effect-2" % munitCatsEffectV % Test, 76 | "org.typelevel" %%% "cats-laws" % catsV % Test 77 | ), 78 | testFrameworks += new TestFramework("munit.Framework") 79 | ) 80 | 81 | lazy val releaseSettings = { 82 | Seq( 83 | scmInfo := Some( 84 | ScmInfo( 85 | url("https://github.com/typelevel/unique"), 86 | "git@github.com:typelevel/unique.git" 87 | ) 88 | ), 89 | homepage := Some(url("https://github.com/typelevel/unique")), 90 | licenses := List("MIT" -> url("http://opensource.org/licenses/MIT")), 91 | pomIncludeRepository := { _ => false } 92 | ) 93 | } 94 | 95 | lazy val micrositeSettings = { 96 | import microsites._ 97 | Seq( 98 | micrositeName := "unique", 99 | micrositeDescription := "Functional Unique Values for Scala", 100 | micrositeAuthor := "Typelevel", 101 | micrositeGithubOwner := "typelevel", 102 | micrositeGithubRepo := "unique", 103 | micrositeBaseUrl := "/unique", 104 | micrositeDocumentationUrl := "https://www.javadoc.io/doc/org.typelevel/unique_2.13", 105 | micrositeFooterText := None, 106 | micrositeHighlightTheme := "atom-one-light", 107 | micrositePalette := Map( 108 | "brand-primary" -> "#3e5b95", 109 | "brand-secondary" -> "#294066", 110 | "brand-tertiary" -> "#2d5799", 111 | "gray-dark" -> "#49494B", 112 | "gray" -> "#7B7B7E", 113 | "gray-light" -> "#E5E5E6", 114 | "gray-lighter" -> "#F4F3F4", 115 | "white-color" -> "#FFFFFF" 116 | ), 117 | libraryDependencies += "com.47deg" %% "github4s" % "0.27.1", 118 | micrositePushSiteWith := GitHub4s, 119 | micrositeGithubToken := sys.env.get("GITHUB_TOKEN"), 120 | micrositeExtraMdFiles := Map( 121 | file("CHANGELOG.md") -> ExtraMdFileConfig("changelog.md", 122 | "page", 123 | Map("title" -> "changelog", 124 | "section" -> "changelog", 125 | "position" -> "100" 126 | ) 127 | ), 128 | file("CODE_OF_CONDUCT.md") -> ExtraMdFileConfig("code-of-conduct.md", 129 | "page", 130 | Map("title" -> "code of conduct", 131 | "section" -> "code of conduct", 132 | "position" -> "101" 133 | ) 134 | ), 135 | file("LICENSE") -> ExtraMdFileConfig("license.md", 136 | "page", 137 | Map("title" -> "license", "section" -> "license", "position" -> "102") 138 | ) 139 | ), 140 | mdocIn := (Compile / sourceDirectory).value / "mdoc" 141 | ) 142 | } 143 | 144 | val isScala213Cond = s"matrix.scala == '$Scala213'" 145 | 146 | ThisBuild / githubWorkflowBuildPreamble ++= Seq( 147 | WorkflowStep.Use(UseRef.Public("ruby", "setup-ruby", "v1"), 148 | params = Map("ruby-version" -> "2.7"), 149 | cond = Some(isScala213Cond) 150 | ), 151 | WorkflowStep.Run(List("gem install bundler"), cond = Some(isScala213Cond)), 152 | WorkflowStep.Run(List("bundle install --gemfile=docs/Gemfile"), cond = Some(isScala213Cond)) 153 | ) 154 | 155 | ThisBuild / githubWorkflowTargetBranches := List("*", "series/*") 156 | 157 | val JDK8 = JavaSpec.temurin("8") 158 | val JDK11 = JavaSpec.temurin("11") 159 | val JDK17 = JavaSpec.temurin("17") 160 | 161 | ThisBuild / githubWorkflowJavaVersions := Seq(JDK8, JDK11, JDK17) 162 | ThisBuild / githubWorkflowBuild := Seq( 163 | WorkflowStep 164 | .Sbt( 165 | List("fmtCheck"), 166 | name = Some("Check formatting") 167 | ), 168 | WorkflowStep.Sbt(List("mimaReportBinaryIssues"), name = Some("Check binary issues")), 169 | WorkflowStep.Sbt(List("Test/compile"), name = Some("Compile")), 170 | WorkflowStep.Sbt(List("test"), name = Some("Run tests")), 171 | WorkflowStep.Sbt(List("docs/makeMicrosite"), name = Some("Build the microsite"), cond = Some(isScala213Cond)) 172 | ) 173 | 174 | ThisBuild / githubWorkflowPublish := Seq( 175 | WorkflowStep.Sbt(List("release"), name = Some("Release")), 176 | WorkflowStep.Sbt(List("docs/publishMicrosite"), name = Some(s"Publish the microsite")) 177 | ) 178 | 179 | ThisBuild / versionIntroduced := Map( 180 | "2.12" -> "2.1.1", 181 | "2.13" -> "2.1.1", 182 | "3.0.0-M3" -> "2.1.1", 183 | "3.0.0-RC1" -> "2.1.1", 184 | "3.0.0-RC2" -> "2.1.3", 185 | "3.0.0-RC3" -> "2.1.4" 186 | ) 187 | 188 | // Scalafmt 189 | addCommandAlias("fmt", "; Compile / scalafmt; Test / scalafmt; scalafmtSbt") 190 | addCommandAlias("fmtCheck", "; Compile / scalafmtCheck; Test / scalafmtCheck; scalafmtSbtCheck") 191 | -------------------------------------------------------------------------------- /core/src/main/scala/org/typelevel/unique/Unique.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Christopher Davenport 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package org.typelevel.unique 23 | 24 | import cats.Hash 25 | import cats.effect.Sync 26 | 27 | final class Unique private extends Serializable { 28 | override def toString: String = s"Unique(${hashCode.toHexString})" 29 | } 30 | object Unique { 31 | def newUnique[F[_]: Sync]: F[Unique] = Sync[F].delay(new Unique) 32 | 33 | implicit val uniqueInstances: Hash[Unique] = 34 | Hash.fromUniversalHashCode[Unique] 35 | } 36 | -------------------------------------------------------------------------------- /core/src/test/scala/org/typelevel/unique/CompileTest.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Christopher Davenport 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package org.typelevel.unique 23 | 24 | import cats.implicits._ 25 | 26 | object CompileTest { 27 | 28 | def compare(a: Unique, b: Unique): Boolean = a === b 29 | } 30 | -------------------------------------------------------------------------------- /core/src/test/scala/org/typelevel/unique/UniqueSuite.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Christopher Davenport 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package org.typelevel.unique 23 | 24 | import cats._ 25 | import cats.effect._ 26 | import munit.CatsEffectSuite 27 | 28 | class UniqueSuite extends CatsEffectSuite { 29 | 30 | test("Equality - be equal when comparing the same value") { 31 | for { 32 | unique <- Unique.newUnique[IO] 33 | } yield assert(Eq[Unique].eqv(unique, unique)) 34 | } 35 | 36 | test("Non-Equality - Not be equal when comparing different values") { 37 | for { 38 | unique1 <- Unique.newUnique[IO] 39 | unique2 <- Unique.newUnique[IO] 40 | } yield assert(Eq[Unique].neqv(unique1, unique2)) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /core/src/test/scala/org/typelevel/unique/UniqueTests.scala: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2021 Christopher Davenport 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | * this software and associated documentation files (the "Software"), to deal in 6 | * the Software without restriction, including without limitation the rights to 7 | * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 8 | * the Software, and to permit persons to whom the Software is furnished to do so, 9 | * subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in all 12 | * copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 16 | * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 17 | * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 18 | * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 19 | * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 20 | */ 21 | 22 | package org.typelevel.unique 23 | 24 | import org.scalacheck._ 25 | import cats.effect.SyncIO 26 | import cats.kernel.laws.discipline.{EqTests, HashTests} 27 | import munit.DisciplineSuite 28 | 29 | class UniqueTests extends DisciplineSuite { 30 | 31 | implicit def functionArbitrary[B, A: Arbitrary]: Arbitrary[B => A] = Arbitrary { 32 | for { 33 | a <- Arbitrary.arbitrary[A] 34 | } yield { (_: B) => a } 35 | } 36 | 37 | implicit val uniqueArb: Arbitrary[Unique] = Arbitrary { 38 | Arbitrary.arbitrary[Unit].map(_ => Unique.newUnique[SyncIO].unsafeRunSync()) 39 | } 40 | 41 | checkAll("Unique", HashTests[Unique].hash) 42 | checkAll("Unique", EqTests[Unique].eqv) 43 | } 44 | -------------------------------------------------------------------------------- /docs/Gemfile: -------------------------------------------------------------------------------- 1 | source 'http://rubygems.org' 2 | 3 | gem "jekyll", ">= 3.2.1", "< 4.0.0" 4 | gem "jekyll-relative-links" 5 | gem "sass" 6 | -------------------------------------------------------------------------------- /docs/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: http://rubygems.org/ 3 | specs: 4 | addressable (2.8.0) 5 | public_suffix (>= 2.0.2, < 5.0) 6 | colorator (1.1.0) 7 | concurrent-ruby (1.1.5) 8 | em-websocket (0.5.1) 9 | eventmachine (>= 0.12.9) 10 | http_parser.rb (~> 0.6.0) 11 | eventmachine (1.2.7) 12 | ffi (1.11.3) 13 | forwardable-extended (2.6.0) 14 | http_parser.rb (0.6.0) 15 | i18n (0.9.5) 16 | concurrent-ruby (~> 1.0) 17 | jekyll (3.8.6) 18 | addressable (~> 2.4) 19 | colorator (~> 1.0) 20 | em-websocket (~> 0.5) 21 | i18n (~> 0.7) 22 | jekyll-sass-converter (~> 1.0) 23 | jekyll-watch (~> 2.0) 24 | kramdown (~> 1.14) 25 | liquid (~> 4.0) 26 | mercenary (~> 0.3.3) 27 | pathutil (~> 0.9) 28 | rouge (>= 1.7, < 4) 29 | safe_yaml (~> 1.0) 30 | jekyll-relative-links (0.6.1) 31 | jekyll (>= 3.3, < 5.0) 32 | jekyll-sass-converter (1.5.2) 33 | sass (~> 3.4) 34 | jekyll-watch (2.2.1) 35 | listen (~> 3.0) 36 | kramdown (1.17.0) 37 | liquid (4.0.3) 38 | listen (3.2.1) 39 | rb-fsevent (~> 0.10, >= 0.10.3) 40 | rb-inotify (~> 0.9, >= 0.9.10) 41 | mercenary (0.3.6) 42 | pathutil (0.16.2) 43 | forwardable-extended (~> 2.6) 44 | public_suffix (4.0.6) 45 | rb-fsevent (0.10.3) 46 | rb-inotify (0.10.0) 47 | ffi (~> 1.0) 48 | rouge (3.14.0) 49 | safe_yaml (1.0.5) 50 | sass (3.7.4) 51 | sass-listen (~> 4.0.0) 52 | sass-listen (4.0.0) 53 | rb-fsevent (~> 0.9, >= 0.9.4) 54 | rb-inotify (~> 0.9, >= 0.9.7) 55 | 56 | PLATFORMS 57 | ruby 58 | 59 | DEPENDENCIES 60 | jekyll (>= 3.2.1, < 4.0.0) 61 | jekyll-relative-links 62 | sass 63 | 64 | BUNDLED WITH 65 | 2.2.11 66 | -------------------------------------------------------------------------------- /docs/src/main/mdoc/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: home 3 | 4 | --- 5 | # unique [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.typelevel/unique_2.12/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.typelevel/unique_2.12) 6 | 7 | This is a shared library for creating and managing unique values in a referentially transparent way. 8 | 9 | ## Maintenance status 10 | 11 | This project is now end-of-life. Its functionality has been [adopted into Cats-Effect 3](https://typelevel.github.io/unique). We will consider releases for [security](https://github.com/typelevel/unique/security/policy) or other significant tales of woe, but routine maintenance has ceased. 12 | 13 | ## Synopsis 14 | 15 | Creation of a unique value must always happen within an effect. 16 | 17 | Let's start with some imports. 18 | 19 | ```scala mdoc:silent 20 | import org.typelevel.unique.Unique 21 | import cats.implicits._ 22 | import cats.effect._ 23 | ``` 24 | 25 | Then we can display that only values created by the same effect are equal. 26 | 27 | ```scala mdoc 28 | // Equal 29 | { 30 | for { 31 | unique <- Unique.newUnique[IO] 32 | } yield unique === unique 33 | }.unsafeRunSync() 34 | 35 | // Not equal 36 | { 37 | for { 38 | unique1 <- Unique.newUnique[IO] 39 | unique2 <- Unique.newUnique[IO] 40 | } yield unique1 === unique2 41 | }.unsafeRunSync() 42 | 43 | 44 | ``` 45 | -------------------------------------------------------------------------------- /project/build.properties: -------------------------------------------------------------------------------- 1 | sbt.version=1.6.2 2 | 3 | 4 | -------------------------------------------------------------------------------- /project/plugins.sbt: -------------------------------------------------------------------------------- 1 | addSbtPlugin("com.47deg" % "sbt-microsites" % "1.3.4") 2 | addSbtPlugin("com.codecommit" % "sbt-spiewak-sonatype" % "0.23.0") 3 | addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.2.0") 4 | addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.10.0") 5 | addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.6") 6 | --------------------------------------------------------------------------------