├── .github
└── workflows
│ ├── ci.yml
│ └── clean.yml
├── .gitignore
├── .idea
├── codeStyleSettings.xml
├── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
└── vcs.xml
├── .sbtopts
├── .scala-steward.conf
├── LICENSE
├── README.md
├── analyzer
└── src
│ ├── main
│ ├── resources
│ │ └── scalac-plugin.xml
│ └── scala
│ │ └── com
│ │ └── avsystem
│ │ └── commons
│ │ └── analyzer
│ │ ├── AnalyzerPlugin.scala
│ │ ├── AnalyzerRule.scala
│ │ ├── Any2StringAdd.scala
│ │ ├── BadSingletonComponent.scala
│ │ ├── BasePackage.scala
│ │ ├── CheckBincompat.scala
│ │ ├── CheckMacroPrivate.scala
│ │ ├── ConstantDeclarations.scala
│ │ ├── DiscardedMonixTask.scala
│ │ ├── ExplicitGenerics.scala
│ │ ├── FindUsages.scala
│ │ ├── ImplicitTypes.scala
│ │ ├── ImportJavaUtil.scala
│ │ ├── ShowAst.scala
│ │ ├── ThrowableObjects.scala
│ │ ├── ValueEnumExhaustiveMatch.scala
│ │ └── VarargsAtLeast.scala
│ └── test
│ └── scala
│ └── com
│ └── avsystem
│ └── commons
│ └── analyzer
│ ├── AnalyzerTest.scala
│ ├── Any2StringAddTest.scala
│ ├── BadSingletonComponentTest.scala
│ ├── BasePackageTest.scala
│ ├── CheckBincompatTest.scala
│ ├── CheckMacroPrivateTest.scala
│ ├── ConstantDeclarationsTest.scala
│ ├── DiscardedMonixTaskTest.scala
│ ├── ExplicitGenericsTest.scala
│ ├── FindUsagesTest.scala
│ ├── ImplicitTypesTest.scala
│ ├── ImportJavaUtilTest.scala
│ ├── TestUtils.scala
│ ├── ThrowableObjectsTest.scala
│ ├── ValueEnumExhaustiveMatchTest.scala
│ └── VarargsAtLeastTest.scala
├── benchmark
├── js
│ ├── README.md
│ ├── fullopt-2.13.html
│ └── src
│ │ └── main
│ │ └── scala
│ │ └── com
│ │ └── avsystem
│ │ └── commons
│ │ ├── Main.scala
│ │ └── ser
│ │ ├── IsoInstantBenchmarks.scala
│ │ └── JsonBenchmarks.scala
├── jvm
│ └── src
│ │ └── main
│ │ └── scala
│ │ └── com
│ │ └── avsystem
│ │ └── commons
│ │ ├── core
│ │ ├── OptBenchmarks.scala
│ │ └── generalBenchmarks.scala
│ │ ├── mongo
│ │ ├── BsonCodecBenchmark.scala
│ │ └── BsonInputOutputBenchmark.scala
│ │ └── ser
│ │ ├── GenCodecBenchmarks.scala
│ │ ├── JsonSerializationBenchmark.scala
│ │ └── StreamInputOutputBenchmark.scala
└── src
│ └── main
│ └── scala
│ └── com
│ └── avsystem
│ └── commons
│ └── ser
│ ├── CirceJsonInputOutput.scala
│ └── jsonExampleData.scala
├── build.sbt
├── core
├── js
│ └── src
│ │ ├── main
│ │ └── scala
│ │ │ └── com
│ │ │ └── avsystem
│ │ │ └── commons
│ │ │ ├── jiop
│ │ │ └── JavaInterop.scala
│ │ │ ├── jsiop
│ │ │ └── JsInterop.scala
│ │ │ ├── misc
│ │ │ ├── CrossUtils.scala
│ │ │ └── TimestampConversions.scala
│ │ │ └── serialization
│ │ │ ├── IsoInstant.scala
│ │ │ └── nativejs
│ │ │ ├── NativeFormatOptions.scala
│ │ │ ├── NativeJsonInput.scala
│ │ │ └── NativeJsonOutput.scala
│ │ └── test
│ │ └── scala
│ │ └── com
│ │ └── avsystem
│ │ └── commons
│ │ ├── serialization
│ │ └── nativejs
│ │ │ └── NativeJsonInputOutputTest.scala
│ │ └── testutil
│ │ └── IO.scala
├── jvm
│ └── src
│ │ ├── main
│ │ └── scala
│ │ │ └── com
│ │ │ └── avsystem
│ │ │ └── commons
│ │ │ ├── concurrent
│ │ │ ├── BlockingUtils.scala
│ │ │ └── ObservableBlockingIterator.scala
│ │ │ ├── jiop
│ │ │ ├── GuavaInterop.scala
│ │ │ ├── JFunctionUtils.scala
│ │ │ ├── JOptionalUtils.scala
│ │ │ ├── JStreamUtils.scala
│ │ │ ├── Java8CollectionUtils.scala
│ │ │ ├── JavaApi.scala
│ │ │ ├── JavaInterop.scala
│ │ │ ├── JavaTimeInterop.scala
│ │ │ ├── ScalaJDoubleStream.scala
│ │ │ ├── ScalaJIntStream.scala
│ │ │ ├── ScalaJLongStream.scala
│ │ │ └── ScalaJStream.scala
│ │ │ ├── jsiop
│ │ │ └── JsInterop.scala
│ │ │ ├── misc
│ │ │ ├── CrossUtils.scala
│ │ │ └── TimestampConversions.scala
│ │ │ └── serialization
│ │ │ └── IsoInstant.scala
│ │ └── test
│ │ └── scala
│ │ └── com
│ │ └── avsystem
│ │ └── commons
│ │ ├── concurrent
│ │ ├── JvmTaskExtensionsTest.scala
│ │ └── ObservableBlockingIteratorTest.scala
│ │ ├── di
│ │ ├── ComponentComposition.scala
│ │ ├── ComponentsExample.scala
│ │ ├── ComponentsTest.scala
│ │ └── MyApp.scala
│ │ ├── jiop
│ │ └── JavaInteropTest.scala
│ │ ├── macros
│ │ └── TypeClassDerivationTest.scala
│ │ ├── misc
│ │ └── TryCompanionOpsTest.scala
│ │ ├── serialization
│ │ ├── JCodecTestBase.scala
│ │ └── JGenCodecTest.scala
│ │ └── testutil
│ │ └── IO.scala
└── src
│ ├── main
│ └── scala
│ │ └── com
│ │ └── avsystem
│ │ └── commons
│ │ ├── CommonAliases.scala
│ │ ├── SharedExtensions.scala
│ │ ├── annotation
│ │ ├── AnnotationAggregate.scala
│ │ ├── NotInheritedFromSealedTypes.scala
│ │ ├── atLeast.scala
│ │ ├── bincompat.scala
│ │ ├── defaultsToName.scala
│ │ ├── explicitGenerics.scala
│ │ ├── macroPrivate.scala
│ │ ├── positioned.scala
│ │ └── showAst.scala
│ │ ├── collection
│ │ ├── CloseableIterator.scala
│ │ ├── CollectionAliases.scala
│ │ ├── MutableStack.scala
│ │ └── crossBuilders.scala
│ │ ├── concurrent
│ │ ├── DurationPostfixConverters.scala
│ │ ├── ObservableExtensions.scala
│ │ ├── RetryStrategy.scala
│ │ ├── TaskExtensions.scala
│ │ ├── executionContexts.scala
│ │ └── package.scala
│ │ ├── derivation
│ │ ├── AllowImplicitMacro.scala
│ │ ├── DeferredInstance.scala
│ │ └── Materialized.scala
│ │ ├── di
│ │ ├── Component.scala
│ │ └── Components.scala
│ │ ├── jiop
│ │ ├── CompatAsJavaScalaExtensions.scala
│ │ ├── JBasicUtils.scala
│ │ ├── JCollectionUtils.scala
│ │ └── JFactory.scala
│ │ ├── meta
│ │ ├── AdtMetadataCompanion.scala
│ │ ├── Fallback.scala
│ │ ├── MacroInstances.scala
│ │ ├── MetadataCompanion.scala
│ │ ├── OptionLike.scala
│ │ ├── metaAnnotations.scala
│ │ └── metadata.scala
│ │ ├── misc
│ │ ├── AnnotationOf.scala
│ │ ├── ApplierUnapplier.scala
│ │ ├── Bidirectional.scala
│ │ ├── BoxingUnboxing.scala
│ │ ├── Bytes.scala
│ │ ├── CaseMethods.scala
│ │ ├── CharSubSequence.scala
│ │ ├── Delegation.scala
│ │ ├── GraphUtils.scala
│ │ ├── Implicits.scala
│ │ ├── MiscAliases.scala
│ │ ├── NOpt.scala
│ │ ├── Opt.scala
│ │ ├── OptArg.scala
│ │ ├── OptRef.scala
│ │ ├── Sam.scala
│ │ ├── SamCompanion.scala
│ │ ├── ScalaDurationExtensions.scala
│ │ ├── SealedUtils.scala
│ │ ├── SelfInstance.scala
│ │ ├── SimpleClassName.scala
│ │ ├── SourceInfo.scala
│ │ ├── Timestamp.scala
│ │ ├── TypeString.scala
│ │ ├── TypedMap.scala
│ │ ├── ValueEnum.scala
│ │ ├── ValueOf.scala
│ │ └── package.scala
│ │ ├── package.scala
│ │ ├── rpc
│ │ ├── AsRawReal.scala
│ │ ├── MetadataAnnotation.scala
│ │ ├── RPCFramework.scala
│ │ ├── RawRpcCompanion.scala
│ │ ├── RawValueCompanion.scala
│ │ ├── RpcMetadataCompanion.scala
│ │ ├── RpcUtils.scala
│ │ ├── StandardRPCFramework.scala
│ │ └── rpcAnnotations.scala
│ │ ├── serialization
│ │ ├── Base64.scala
│ │ ├── DefaultCaseObjectInput.scala
│ │ ├── FieldValues.scala
│ │ ├── GenCodec.scala
│ │ ├── GenCodecStructure.scala
│ │ ├── GenKeyCodec.scala
│ │ ├── GenObjectCodec.scala
│ │ ├── GenRef.scala
│ │ ├── HasGenCodec.scala
│ │ ├── IgnoreTransientDefaultMarker.scala
│ │ ├── InputOutput.scala
│ │ ├── OptionalFieldValueCodec.scala
│ │ ├── PeekingObjectInput.scala
│ │ ├── SerializationName.scala
│ │ ├── SimpleValueInputOutput.scala
│ │ ├── StreamInputOutput.scala
│ │ ├── TransparentWrapperCompanion.scala
│ │ ├── TupleGenCodecs.scala
│ │ ├── cbor
│ │ │ ├── CborAdtMetadata.scala
│ │ │ ├── CborInput.scala
│ │ │ ├── CborKeyCodec.scala
│ │ │ ├── CborOptimizedCodecs.scala
│ │ │ ├── CborOutput.scala
│ │ │ ├── HFloat.scala
│ │ │ ├── RawCbor.scala
│ │ │ └── definitions.scala
│ │ ├── customMarkerWrappers.scala
│ │ ├── defaultCase.scala
│ │ ├── flatten.scala
│ │ ├── generated.scala
│ │ ├── json
│ │ │ ├── JsonOptions.scala
│ │ │ ├── JsonStringInput.scala
│ │ │ ├── JsonStringOutput.scala
│ │ │ ├── JsonType.scala
│ │ │ ├── RawJson.scala
│ │ │ └── WrappedJson.scala
│ │ ├── macroCodecs.scala
│ │ ├── name.scala
│ │ ├── optionalParam.scala
│ │ ├── outOfOrder.scala
│ │ ├── transientDefault.scala
│ │ ├── transparent.scala
│ │ ├── whenAbsent.scala
│ │ └── wrappers.scala
│ │ └── tuples
│ │ └── TupleDerivation.scala
│ └── test
│ ├── resources
│ ├── SimpleApi.txt
│ └── StringApi.txt
│ └── scala
│ └── com
│ └── avsystem
│ └── commons
│ ├── collection
│ └── MutableStackTest.scala
│ ├── concurrent
│ ├── ExponentialBackoffTest.scala
│ ├── ObservableExtensionsTest.scala
│ └── TaskExtensionsTest.scala
│ ├── macros
│ ├── ApplyUnapplyTest.scala
│ ├── JavaClassNameTest.scala
│ ├── KnownSubtypesTest.scala
│ ├── TreeForTypeTest.scala
│ └── TypeStringTest.scala
│ ├── meta
│ └── AdtTaggingTest.scala
│ ├── misc
│ ├── AdtMetadataTest.scala
│ ├── AnnotationOfTest.scala
│ ├── ApplierUnapplierTest.scala
│ ├── BidirectionalTest.scala
│ ├── BoxingUnboxingTest.scala
│ ├── CaseMethodsTest.scala
│ ├── DelegationTest.scala
│ ├── GraphUtilTest.scala
│ ├── ImplicitNotFoundTest.scala
│ ├── MacroInstancesTest.scala
│ ├── NOptTest.scala
│ ├── NamedEnumTest.scala
│ ├── OptArgTest.scala
│ ├── OptRefTest.scala
│ ├── OptTest.scala
│ ├── SamTest.scala
│ ├── SealedEnumTest.scala
│ ├── SharedExtensionsPropertyTest.scala
│ ├── SharedExtensionsTest.scala
│ ├── SourceInfoTest.scala
│ ├── ValueEnumTest.scala
│ └── ValueOfTest.scala
│ ├── rpc
│ ├── ApiReflectionTest.scala
│ ├── DummyRPC.scala
│ ├── GenericMetadataTest.scala
│ ├── MangleOverloadsTest.scala
│ ├── NewRawRpc.scala
│ ├── NewRpcMetadataTest.scala
│ ├── RPCMetadataTest.scala
│ ├── RPCTest.scala
│ ├── Tag.scala
│ └── TestRPC.scala
│ ├── serialization
│ ├── AbstractCodecTest.scala
│ ├── BuildablePojo.scala
│ ├── CodeSizeTester.scala
│ ├── CodecTestData.scala
│ ├── GenCodecErrorsTest.scala
│ ├── GenCodecRoundtripTest.scala
│ ├── GenRefTest.scala
│ ├── IgnoreTransientDefaultMarkerTest.scala
│ ├── IsoInstantTest.scala
│ ├── JavaCodecs.scala
│ ├── ObjectSizeTest.scala
│ ├── SimpleGenCodecTest.scala
│ ├── StreamGenCodecTest.scala
│ ├── StreamInputOutputTest.scala
│ ├── cbor
│ │ ├── CborChunkedTest.scala
│ │ ├── CborInputOutputTest.scala
│ │ ├── CborNonStringKeysTest.scala
│ │ └── HFloatTest.scala
│ └── json
│ │ ├── JsonGenCodecRoundtripTest.scala
│ │ ├── JsonStringInputOutputTest.scala
│ │ ├── SerializationTestUtils.scala
│ │ └── WrappedJsonTest.scala
│ └── testutil
│ ├── CompilationErrorAssertions.scala
│ └── FileBasedSuite.scala
├── docs
├── Analyzer.md
├── Annotations.md
├── CBOR.md
├── Components.md
├── GenCodec.md
├── GenCodecOld.md
├── RedisDriver.md
├── TypedMongo.md
└── images
│ └── REST.svg
├── hocon
└── src
│ ├── main
│ └── scala
│ │ └── com
│ │ └── avsystem
│ │ └── commons
│ │ └── hocon
│ │ ├── ConfigCompanion.scala
│ │ ├── HParser.scala
│ │ ├── HTokenType.scala
│ │ ├── HTree.scala
│ │ ├── HoconInput.scala
│ │ ├── HoconOutput.scala
│ │ ├── HoconTypeMarker.scala
│ │ ├── SizeInBytes.scala
│ │ └── SourceFile.scala
│ └── test
│ ├── resources
│ ├── lexer
│ │ └── hocon.txt
│ └── parser
│ │ └── hocon.txt
│ └── scala
│ └── com
│ └── avsystem
│ └── commons
│ └── hocon
│ ├── HLexerTest.scala
│ ├── HParserTest.scala
│ ├── HoconGenCodecRoundtripTest.scala
│ └── HoconInputTest.scala
├── jetty
└── src
│ ├── main
│ └── scala
│ │ └── com
│ │ └── avsystem
│ │ └── commons
│ │ └── jetty
│ │ └── rpc
│ │ ├── HttpException.scala
│ │ └── JettyRPCFramework.scala
│ └── test
│ └── scala
│ └── com
│ └── avsystem
│ └── commons
│ └── jetty
│ └── rpc
│ └── JettyRPCFrameworkTest.scala
├── macros
└── src
│ └── main
│ └── scala
│ └── com
│ └── avsystem
│ └── commons
│ └── macros
│ ├── CompatMacroCommons.scala
│ ├── MacroCommons.scala
│ ├── RecursiveImplicitMarker.scala
│ ├── TestMacros.scala
│ ├── TypeClassDerivation.scala
│ ├── UniversalMacros.scala
│ ├── di
│ └── ComponentMacros.scala
│ ├── meta
│ ├── AdtMetadataMacros.scala
│ ├── MacroMetadatas.scala
│ └── MacroSymbols.scala
│ ├── misc
│ ├── BidirectionalMacro.scala
│ ├── DelegationMacros.scala
│ ├── LazyLoggingMacros.scala
│ ├── MiscMacros.scala
│ ├── Res.scala
│ ├── SamMacros.scala
│ └── SealedMacros.scala
│ ├── rpc
│ ├── RPCFrameworkMacros.scala
│ ├── RpcMacros.scala
│ ├── RpcMappings.scala
│ ├── RpcMetadatas.scala
│ └── RpcSymbols.scala
│ └── serialization
│ ├── BsonRefMacros.scala
│ ├── CodecMacroCommons.scala
│ ├── GenCodecMacros.scala
│ ├── GenKeyCodecMacros.scala
│ ├── GenRefMacros.scala
│ └── MongoMacros.scala
├── mongo
├── js
│ └── src
│ │ └── main
│ │ └── scala
│ │ └── com
│ │ └── avsystem
│ │ └── commons
│ │ └── mongo
│ │ └── typed
│ │ └── MongoEntityCompanion.scala
├── jvm
│ └── src
│ │ ├── main
│ │ └── scala
│ │ │ └── com
│ │ │ └── avsystem
│ │ │ └── commons
│ │ │ └── mongo
│ │ │ ├── BsonCodec.scala
│ │ │ ├── BsonGenCodecs.scala
│ │ │ ├── BsonInputOutput.scala
│ │ │ ├── BsonReaderInput.scala
│ │ │ ├── BsonRef.scala
│ │ │ ├── BsonValueInput.scala
│ │ │ ├── BsonValueOutput.scala
│ │ │ ├── BsonValueUtils.scala
│ │ │ ├── BsonWriterOutput.scala
│ │ │ ├── Decimal128Utils.scala
│ │ │ ├── Doc.scala
│ │ │ ├── DocKey.scala
│ │ │ ├── DocumentCodec.scala
│ │ │ ├── DuplicateKey.scala
│ │ │ ├── Filter.scala
│ │ │ ├── GenCodecBasedBsonCodec.scala
│ │ │ ├── KeyEscaper.scala
│ │ │ ├── MongoCodec.scala
│ │ │ ├── Sort.scala
│ │ │ ├── Update.scala
│ │ │ ├── core
│ │ │ ├── GenCodecProvider.scala
│ │ │ ├── GenCodecRegistry.scala
│ │ │ └── ops
│ │ │ │ ├── BaseFiltering.scala
│ │ │ │ ├── BaseIterableFiltering.scala
│ │ │ │ ├── BaseIterableUpdating.scala
│ │ │ │ ├── BaseSorting.scala
│ │ │ │ ├── BaseUpdating.scala
│ │ │ │ ├── BsonFiltering.scala
│ │ │ │ ├── BsonRefFiltering.scala
│ │ │ │ ├── BsonRefIterableFiltering.scala
│ │ │ │ ├── BsonRefIterableUpdating.scala
│ │ │ │ ├── BsonRefKeyElementHandling.scala
│ │ │ │ ├── BsonRefKeyHandling.scala
│ │ │ │ ├── BsonRefKeyValueHandling.scala
│ │ │ │ ├── BsonRefSorting.scala
│ │ │ │ ├── BsonRefUpdating.scala
│ │ │ │ ├── BsonUpdating.scala
│ │ │ │ ├── DocKeyFiltering.scala
│ │ │ │ ├── DocKeyKeyHandling.scala
│ │ │ │ ├── DocKeyKeyValueHandling.scala
│ │ │ │ ├── DocKeySorting.scala
│ │ │ │ ├── Filtering.scala
│ │ │ │ ├── KeyElementHandling.scala
│ │ │ │ ├── KeyGetter.scala
│ │ │ │ ├── KeyHandling.scala
│ │ │ │ ├── KeyValueHandling.scala
│ │ │ │ ├── Sorting.scala
│ │ │ │ └── Updating.scala
│ │ │ ├── reactive
│ │ │ ├── GenCodecCollection.scala
│ │ │ └── ReactiveMongoExtensions.scala
│ │ │ ├── scala
│ │ │ ├── GenCodecCollection.scala
│ │ │ └── MongoScalaObservableExtensions.scala
│ │ │ ├── sync
│ │ │ ├── GenCodecCollection.scala
│ │ │ └── MongoOps.scala
│ │ │ ├── text
│ │ │ └── TextSearchLanguage.scala
│ │ │ └── typed
│ │ │ ├── Bson.scala
│ │ │ ├── DataTypeDsl.scala
│ │ │ ├── EntityIdMode.scala
│ │ │ ├── FilterDocBuilder.scala
│ │ │ ├── MongoEntityCompanion.scala
│ │ │ ├── MongoFilter.scala
│ │ │ ├── MongoFormat.scala
│ │ │ ├── MongoIndex.scala
│ │ │ ├── MongoOrder.scala
│ │ │ ├── MongoPolyDataCompanion.scala
│ │ │ ├── MongoProjection.scala
│ │ │ ├── MongoQueryOperator.scala
│ │ │ ├── MongoRef.scala
│ │ │ ├── MongoTypedKey.scala
│ │ │ ├── MongoUpdate.scala
│ │ │ ├── MongoUpdateOperator.scala
│ │ │ ├── MongoWrite.scala
│ │ │ ├── ObjectIdWrapperCompanion.scala
│ │ │ ├── ProjectionZippers.scala
│ │ │ ├── QueryOperatorsDsl.scala
│ │ │ ├── TypedClientSession.scala
│ │ │ ├── TypedMongoClient.scala
│ │ │ ├── TypedMongoCollection.scala
│ │ │ ├── TypedMongoDatabase.scala
│ │ │ ├── TypedMongoUtils.scala
│ │ │ └── UpdateOperatorsDsl.scala
│ │ └── test
│ │ ├── scala-2.13
│ │ └── com
│ │ │ └── avsystem
│ │ │ └── commons
│ │ │ └── mongo
│ │ │ └── typed
│ │ │ ├── MongoPolyDataTest.scala
│ │ │ └── PolyDataWithCustomImplicits.scala
│ │ └── scala
│ │ └── com
│ │ └── avsystem
│ │ └── commons
│ │ └── mongo
│ │ ├── BigDecimalEncodingTest.scala
│ │ ├── BsonInputOutputTest.scala
│ │ ├── BsonRefTest.scala
│ │ ├── BsonValueCodecsTest.scala
│ │ ├── Decimal128UtilsTest.scala
│ │ ├── DocTest.scala
│ │ ├── KeyEscaperTest.scala
│ │ ├── SomethingPlain.scala
│ │ ├── core
│ │ └── ops
│ │ │ ├── BsonEquality.scala
│ │ │ ├── BsonRefIterableUpdatingCompilationTest.scala
│ │ │ ├── FilteringTest.scala
│ │ │ ├── SomeEntity.scala
│ │ │ └── SortingTest.scala
│ │ └── typed
│ │ ├── MongoFilterTest.scala
│ │ ├── MongoIndexTest.scala
│ │ ├── MongoOrderTest.scala
│ │ ├── MongoProjectionTest.scala
│ │ ├── MongoRefTest.scala
│ │ ├── MongoUpdateTest.scala
│ │ ├── TypedMongoCollectionTest.scala
│ │ └── testEntities.scala
└── src
│ └── main
│ └── scala
│ └── com
│ └── avsystem
│ └── commons
│ └── mongo
│ ├── mongoId.scala
│ └── typed
│ └── MongoEntity.scala
├── project
├── Commons.scala
├── build.properties
└── plugins.sbt
├── redis
├── preconfiguredCluster
│ ├── 9000
│ │ └── nodes.conf
│ ├── 9001
│ │ └── nodes.conf
│ ├── 9002
│ │ └── nodes.conf
│ ├── 9003
│ │ └── nodes.conf
│ ├── 9004
│ │ └── nodes.conf
│ └── 9005
│ │ └── nodes.conf
├── preconfiguredMasterSlave
│ ├── 8000
│ │ └── redis.log
│ ├── 8001
│ │ └── redis.log
│ ├── 28000
│ │ └── sentinel.conf
│ ├── 28001
│ │ └── sentinel.conf
│ └── 28002
│ │ └── sentinel.conf
├── src
│ ├── main
│ │ ├── resources
│ │ │ ├── reference.conf
│ │ │ └── slotkeys.txt
│ │ └── scala
│ │ │ └── com
│ │ │ └── avsystem
│ │ │ └── commons
│ │ │ └── redis
│ │ │ ├── ApiSubset.scala
│ │ │ ├── FlatMapper.scala
│ │ │ ├── Hash.scala
│ │ │ ├── NodeAddress.scala
│ │ │ ├── RedisApi.scala
│ │ │ ├── RedisBatch.scala
│ │ │ ├── RedisClusterClient.scala
│ │ │ ├── RedisCommand.scala
│ │ │ ├── RedisConnectionClient.scala
│ │ │ ├── RedisDataCodec.scala
│ │ │ ├── RedisExecutor.scala
│ │ │ ├── RedisMasterSlaveClient.scala
│ │ │ ├── RedisNodeClient.scala
│ │ │ ├── RedisRecordCodec.scala
│ │ │ ├── Sequencer.scala
│ │ │ ├── Transaction.scala
│ │ │ ├── TupleSequencers.scala
│ │ │ ├── actor
│ │ │ ├── ClusterMonitoringActor.scala
│ │ │ ├── ConnectionPoolActor.scala
│ │ │ ├── RedisConnectionActor.scala
│ │ │ ├── RedisOperationActor.scala
│ │ │ └── SentinelsMonitoringActor.scala
│ │ │ ├── commands
│ │ │ ├── RedisInfo.scala
│ │ │ ├── ReplyDecoders.scala
│ │ │ ├── cluster.scala
│ │ │ ├── common.scala
│ │ │ ├── connection.scala
│ │ │ ├── geo.scala
│ │ │ ├── hashes.scala
│ │ │ ├── hyperLogLog.scala
│ │ │ ├── keys.scala
│ │ │ ├── lists.scala
│ │ │ ├── pubsub.scala
│ │ │ ├── scripting.scala
│ │ │ ├── sentinel.scala
│ │ │ ├── server.scala
│ │ │ ├── sets.scala
│ │ │ ├── sortedSets.scala
│ │ │ ├── streams.scala
│ │ │ ├── strings.scala
│ │ │ └── transactions.scala
│ │ │ ├── config
│ │ │ ├── ExecutionConfig.scala
│ │ │ └── config.scala
│ │ │ ├── exception
│ │ │ └── RedisException.scala
│ │ │ ├── monitoring
│ │ │ ├── ClusterStateObserver.scala
│ │ │ ├── ConnectionStateObserver.scala
│ │ │ └── SentinelStateObserver.scala
│ │ │ ├── protocol
│ │ │ └── messages.scala
│ │ │ ├── raw.scala
│ │ │ └── util
│ │ │ ├── ActorLazyLogging.scala
│ │ │ ├── DelayedFuture.scala
│ │ │ ├── FoldingBuilder.scala
│ │ │ ├── HeadIterable.scala
│ │ │ ├── SingletonSeq.scala
│ │ │ └── SizedArraySeqBuilder.scala
│ └── test
│ │ ├── resources
│ │ └── application.conf
│ │ └── scala
│ │ └── com
│ │ └── avsystem
│ │ └── commons
│ │ └── redis
│ │ ├── AddScaladocs.scala
│ │ ├── ApiTypecheckingTest.scala
│ │ ├── ClusterUtils.scala
│ │ ├── CommandsSuite.scala
│ │ ├── CommunicationLogging.scala
│ │ ├── RedisClusterClientTest.scala
│ │ ├── RedisConnectionClientTest.scala
│ │ ├── RedisDataGenCodecRoundtripTest.scala
│ │ ├── RedisMasterSlaveClientTest.scala
│ │ ├── RedisNodeClientTest.scala
│ │ ├── RedisProcessUtils.scala
│ │ ├── SeparateThreadExecutionContext.scala
│ │ ├── TestDebugListener.scala
│ │ ├── TransactionTest.scala
│ │ ├── UsesActorSystem.scala
│ │ ├── UsesClusterServers.scala
│ │ ├── UsesFreshCluster.scala
│ │ ├── UsesMasterSlaveServers.scala
│ │ ├── UsesPreconfiguredCluster.scala
│ │ ├── UsesPreconfiguredMasterSlave.scala
│ │ ├── UsesRedisClusterClient.scala
│ │ ├── UsesRedisConnectionClient.scala
│ │ ├── UsesRedisMasterSlaveClient.scala
│ │ ├── UsesRedisNodeClient.scala
│ │ ├── UsesRedisServer.scala
│ │ ├── UsesSslContext.scala
│ │ ├── commands
│ │ ├── ClusterApiTest.scala
│ │ ├── ConnectionApiSuite.scala
│ │ ├── GeoApiSuite.scala
│ │ ├── HashesApiSuite.scala
│ │ ├── HyperLogLogApiSuite.scala
│ │ ├── KeysApiSuite.scala
│ │ ├── ListsApiSuite.scala
│ │ ├── NodeOnlyServerApiSuite.scala
│ │ ├── ScriptingApiSuite.scala
│ │ ├── ServerApiSuite.scala
│ │ ├── SetsApiSuite.scala
│ │ ├── SortedSetsApiSuite.scala
│ │ ├── StreamsApiSuite.scala
│ │ ├── StringsApiSuite.scala
│ │ └── commandSuites.scala
│ │ ├── examples
│ │ ├── ApiCustomizationExample.scala
│ │ ├── ClusterClientExample.scala
│ │ ├── ConnectionClientExample.scala
│ │ ├── ConnectionSetupExample.scala
│ │ ├── MasterSlaveClientExample.scala
│ │ ├── MultiExecExample.scala
│ │ ├── NodeClientExample.scala
│ │ ├── PipeliningExample.scala
│ │ ├── ScriptingExample.scala
│ │ └── TransactionExample.scala
│ │ └── protocol
│ │ ├── CommonsScalacheck.scala
│ │ ├── RedisMsgScalacheck.scala
│ │ └── RedisMsgTest.scala
└── tls
│ ├── ca.crt
│ ├── ca.key
│ ├── gen-test-certs.sh
│ ├── redis.crt
│ ├── redis.key
│ └── redis.p12
└── spring
└── src
├── main
└── scala
│ └── com
│ └── avsystem
│ └── commons
│ └── spring
│ ├── AttrNames.scala
│ ├── HoconBeanDefinitionReader.scala
│ ├── HoconType.scala
│ └── ScalaDefaultValuesInjector.scala
└── test
├── resources
├── bean1.conf
├── bean2.conf
├── beans.conf
├── beans.xml
├── conditionalInclude.conf
├── conditionalsDisabled.conf
├── conditionalsEnabled.conf
├── conditionalsNested.conf
└── testBean.conf
└── scala
└── com
└── avsystem
└── commons
└── spring
├── HoconBeanDefinitionReaderTest.scala
├── Playground.scala
└── XmlPlayground.scala
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/*
2 | !.idea/vcs.xml
3 | !.idea/codeStyleSettings.xml
4 | !.idea/codeStyles/
5 | *.iml
6 | *.iws
7 | *.ipr
8 |
9 | target
10 | out
11 | .bloop
12 | .bsp
13 |
14 | *.sjsir
15 | *.class
16 | *.diff
17 | *.orig
18 | *~
19 | *.scss.cache
20 | *.scssc
21 | *.log
22 | *.rdb
23 | *.aof
24 | *.pid
25 |
26 | local.*
27 | cluster/
28 | masterSlave/
29 |
--------------------------------------------------------------------------------
/.idea/codeStyleSettings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.sbtopts:
--------------------------------------------------------------------------------
1 | -J-Xmx2g
2 |
--------------------------------------------------------------------------------
/.scala-steward.conf:
--------------------------------------------------------------------------------
1 | pullRequests.grouping = [
2 | {
3 | name = "jetty",
4 | title = "Update Jetty dependencies",
5 | filter = [{ group = "org.eclipse.jetty" }, { group = "org.eclipse.jetty*" }]
6 | }
7 | ]
8 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 AVSystem
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/analyzer/src/main/resources/scalac-plugin.xml:
--------------------------------------------------------------------------------
1 |
2 | avsAnalyzer
3 | com.avsystem.commons.analyzer.AnalyzerPlugin
4 |
5 |
--------------------------------------------------------------------------------
/analyzer/src/main/scala/com/avsystem/commons/analyzer/Any2StringAdd.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package analyzer
3 |
4 | import scala.tools.nsc.Global
5 |
6 | class Any2StringAdd(g: Global) extends AnalyzerRule(g, "any2stringadd", Level.Off) {
7 |
8 | import global._
9 |
10 | private lazy val any2stringaddSym =
11 | typeOf[Predef.type].member(TermName("any2stringadd")).alternatives.find(_.isMethod).get
12 |
13 | def analyze(unit: CompilationUnit): Unit = {
14 | unit.body.foreach(analyzeTree {
15 | case t if t.symbol == any2stringaddSym =>
16 | report(t.pos, "concatenating arbitrary values with strings is disabled, " +
17 | "use explicit toString or string interpolation")
18 | })
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/analyzer/src/main/scala/com/avsystem/commons/analyzer/BasePackage.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package analyzer
3 |
4 | import scala.annotation.tailrec
5 | import scala.tools.nsc.Global
6 |
7 | class BasePackage(g: Global) extends AnalyzerRule(g, "basePackage") {
8 |
9 | import global._
10 |
11 | object SkipImports {
12 | @tailrec def unapply(stats: List[Tree]): Some[List[Tree]] = stats match {
13 | case Import(_, _) :: tail => unapply(tail)
14 | case stats => Some(stats)
15 | }
16 | }
17 |
18 | def analyze(unit: CompilationUnit): Unit = if (argument != null) {
19 | val requiredBasePackage = argument
20 |
21 | @tailrec def validate(tree: Tree): Unit = tree match {
22 | case PackageDef(pid, _) if pid.symbol.hasPackageFlag && pid.symbol.fullName == requiredBasePackage =>
23 | case PackageDef(_, SkipImports(List(stat))) => validate(stat)
24 | case t => report(t.pos, s"`$requiredBasePackage` must be one of the base packages in this file")
25 | }
26 |
27 | validate(unit.body)
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/analyzer/src/main/scala/com/avsystem/commons/analyzer/CheckBincompat.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package analyzer
3 |
4 | import scala.tools.nsc.Global
5 |
6 | class CheckBincompat(g: Global) extends AnalyzerRule(g, "bincompat") {
7 |
8 | import global._
9 |
10 | private lazy val bincompatAnnotType = classType("com.avsystem.commons.annotation.bincompat")
11 |
12 | def analyze(unit: CompilationUnit): Unit =
13 | unit.body.foreach(analyzeTree {
14 | case tree@(_: Ident | _: Select | _: New) if tree.symbol != null &&
15 | tree.symbol.annotations.exists(_.tree.tpe <:< bincompatAnnotType) =>
16 | report(tree.pos, "Symbols annotated as @bincompat exist only for binary compatibility " +
17 | "and should not be used directly")
18 | })
19 | }
20 |
--------------------------------------------------------------------------------
/analyzer/src/main/scala/com/avsystem/commons/analyzer/CheckMacroPrivate.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package analyzer
3 |
4 | import scala.tools.nsc.Global
5 |
6 | class CheckMacroPrivate(g: Global) extends AnalyzerRule(g, "macroPrivate") {
7 |
8 | import global._
9 |
10 | lazy val macroPrivateAnnotTpe = classType("com.avsystem.commons.annotation.macroPrivate")
11 |
12 | def analyze(unit: CompilationUnit) = if (macroPrivateAnnotTpe != NoType) {
13 | def analyzeTree(tree: Tree): Unit = analyzer.macroExpandee(tree) match {
14 | case `tree` | EmptyTree =>
15 | tree match {
16 | case _: Ident | _: Select | _: SelectFromTypeTree | _: New
17 | if tree.symbol != null && tree.pos != NoPosition =>
18 |
19 | val sym = tree.symbol
20 | val macroPrivate = (sym :: sym.overrides).iterator
21 | .flatMap(_.annotations).exists(_.tree.tpe <:< macroPrivateAnnotTpe)
22 | if (macroPrivate) {
23 | report(tree.pos, s"$sym can only be used in macro-generated code")
24 | }
25 | case _ =>
26 | }
27 | tree.children.foreach(analyzeTree)
28 | case prevTree =>
29 | analyzeTree(prevTree)
30 | }
31 | analyzeTree(unit.body)
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/analyzer/src/main/scala/com/avsystem/commons/analyzer/ConstantDeclarations.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package analyzer
3 |
4 | import scala.tools.nsc.Global
5 |
6 | class ConstantDeclarations(g: Global) extends AnalyzerRule(g, "constantDeclarations", Level.Off) {
7 |
8 | import global._
9 |
10 | def analyze(unit: CompilationUnit): Unit = unit.body.foreach {
11 | case t@ValDef(_, name, tpt, rhs)
12 | if t.symbol.hasGetter && t.symbol.owner.isEffectivelyFinal =>
13 |
14 | val getter = t.symbol.getterIn(t.symbol.owner)
15 | if (getter.isPublic && getter.isStable && getter.overrides.isEmpty) {
16 | val constantValue = rhs.tpe match {
17 | case ConstantType(_) => true
18 | case _ => false
19 | }
20 |
21 | def doReport(msg: String): Unit =
22 | report(t.pos, msg, site = t.symbol)
23 |
24 | val firstChar = name.toString.charAt(0)
25 | if (constantValue && (firstChar.isLower || !getter.isFinal)) {
26 | doReport("a literal-valued constant should be declared as a `final val` with an UpperCamelCase name")
27 | }
28 | if (!constantValue && firstChar.isUpper && !getter.isFinal) {
29 | doReport("a constant with UpperCamelCase name should be declared as a `final val`")
30 | }
31 | if (getter.isFinal && constantValue && !(tpt.tpe =:= rhs.tpe)) {
32 | doReport("a constant with a literal value should not have an explicit type annotation")
33 | }
34 | }
35 | case _ =>
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/analyzer/src/main/scala/com/avsystem/commons/analyzer/ExplicitGenerics.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package analyzer
3 |
4 | import scala.tools.nsc.Global
5 |
6 | class ExplicitGenerics(g: Global) extends AnalyzerRule(g, "explicitGenerics") {
7 |
8 | import global._
9 |
10 | lazy val explicitGenericsAnnotTpe = classType("com.avsystem.commons.annotation.explicitGenerics")
11 |
12 | def analyze(unit: CompilationUnit) = if (explicitGenericsAnnotTpe != NoType) {
13 | def requiresExplicitGenerics(sym: Symbol): Boolean =
14 | sym != NoSymbol && (sym :: sym.overrides).flatMap(_.annotations).exists(_.tree.tpe <:< explicitGenericsAnnotTpe)
15 |
16 | def analyzeTree(tree: Tree): Unit = analyzer.macroExpandee(tree) match {
17 | case `tree` | EmptyTree =>
18 | tree match {
19 | case t@TypeApply(pre, args) if requiresExplicitGenerics(pre.symbol) =>
20 | val inferredTypeParams = args.forall {
21 | case tt: TypeTree => tt.original == null || tt.original == EmptyTree
22 | case _ => false
23 | }
24 | if (inferredTypeParams) {
25 | report(t.pos, s"${pre.symbol} requires that its type arguments are explicit (not inferred)")
26 | }
27 | case _ =>
28 | }
29 | tree.children.foreach(analyzeTree)
30 | case prevTree =>
31 | analyzeTree(prevTree)
32 | }
33 | analyzeTree(unit.body)
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/analyzer/src/main/scala/com/avsystem/commons/analyzer/FindUsages.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package analyzer
3 |
4 | import scala.tools.nsc.Global
5 |
6 | class FindUsages(g: Global) extends AnalyzerRule(g, "findUsages") {
7 |
8 | import global._
9 |
10 | lazy val rejectedSymbols: Set[String] =
11 | if (argument == null) Set.empty else argument.split(";").toSet
12 |
13 | override def analyze(unit: CompilationUnit): Unit = if (rejectedSymbols.nonEmpty) {
14 | unit.body.foreach { tree =>
15 | if (tree.symbol != null && rejectedSymbols.contains(tree.symbol.fullName)) {
16 | report(tree.pos, s"found usage of ${tree.symbol.fullName}")
17 | }
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/analyzer/src/main/scala/com/avsystem/commons/analyzer/ImplicitTypes.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package analyzer
3 |
4 | import scala.tools.nsc.Global
5 |
6 | class ImplicitTypes(g: Global) extends AnalyzerRule(g, "implicitTypes") {
7 |
8 | import global._
9 |
10 | def analyze(unit: CompilationUnit): Unit = unit.body.foreach {
11 | case t@ValOrDefDef(mods, _, tpt@TypeTree(), _) if tpt.original == null && mods.isImplicit && !mods.isSynthetic =>
12 | report(t.pos, s"Implicit definitions must have type annotated explicitly")
13 | case _ =>
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/analyzer/src/main/scala/com/avsystem/commons/analyzer/ImportJavaUtil.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package analyzer
3 |
4 | import scala.tools.nsc.Global
5 |
6 | class ImportJavaUtil(g: Global) extends AnalyzerRule(g, "importJavaUtil") {
7 |
8 | import global._
9 |
10 | def analyze(unit: CompilationUnit): Unit = {
11 | unit.body.foreach(analyzeTree {
12 | case tree@q"import java.util" =>
13 | report(tree.pos, "Don't import java.util: either import with rename (e.g. import java.{util => ju}) " +
14 | "or use type aliases from JavaInterop (e.g. JList, JSet, etc)")
15 | })
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/analyzer/src/main/scala/com/avsystem/commons/analyzer/ShowAst.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package analyzer
3 |
4 | import scala.tools.nsc.Global
5 |
6 | class ShowAst(g: Global) extends AnalyzerRule(g, "showAst", Level.Error) {
7 |
8 | import global._
9 |
10 | lazy val showAstAnnotType: Type = classType("com.avsystem.commons.annotation.showAst")
11 |
12 | def analyze(unit: CompilationUnit) = if (showAstAnnotType != NoType) {
13 | def analyzeTree(tree: Tree): Unit = analyzer.macroExpandee(tree) match {
14 | case `tree` | EmptyTree =>
15 | tree match {
16 | case Annotated(annot, arg) if annot.tpe <:< showAstAnnotType =>
17 | report(arg.pos, showCode(arg))
18 | case Typed(expr, tpt) if tpt.tpe.annotations.exists(_.tpe <:< showAstAnnotType) =>
19 | report(expr.pos, showCode(expr))
20 | case _: MemberDef if tree.symbol.annotations.exists(_.tpe <:< showAstAnnotType) =>
21 | report(tree.pos, showCode(tree))
22 | case _ =>
23 | }
24 | tree.children.foreach(analyzeTree)
25 | case prevTree =>
26 | analyzeTree(prevTree)
27 | }
28 | analyzeTree(unit.body)
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/analyzer/src/main/scala/com/avsystem/commons/analyzer/ThrowableObjects.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package analyzer
3 |
4 | import scala.tools.nsc.Global
5 |
6 | class ThrowableObjects(g: Global) extends AnalyzerRule(g, "throwableObjects", Level.Warn) {
7 |
8 | import global._
9 |
10 | private lazy val throwableTpe = typeOf[Throwable]
11 | private lazy val throwableSym = throwableTpe.dealias.typeSymbol
12 |
13 | def analyze(unit: CompilationUnit): Unit = unit.body.foreach {
14 | case md: ModuleDef =>
15 | val tpe = md.symbol.typeSignature
16 | def fillInStackTraceSym: Symbol =
17 | tpe.member(TermName("fillInStackTrace")).alternatives.find(_.paramLists == List(Nil)).get
18 |
19 | if (tpe <:< throwableTpe && fillInStackTraceSym.owner == throwableSym) {
20 | report(md.pos, "objects should never extend Throwable unless they have no stack trace")
21 | }
22 | case _ =>
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/analyzer/src/main/scala/com/avsystem/commons/analyzer/VarargsAtLeast.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package analyzer
3 |
4 | import scala.tools.nsc.Global
5 |
6 | class VarargsAtLeast(g: Global) extends AnalyzerRule(g, "varargsAtLeast") {
7 |
8 | import global._
9 |
10 | lazy val atLeastAnnotTpe = classType("com.avsystem.commons.annotation.atLeast")
11 |
12 | def analyze(unit: CompilationUnit): Unit = if (atLeastAnnotTpe != NoType) {
13 | def isVarargParam(tree: Tree) = tree match {
14 | case Typed(_, Ident(typeNames.WILDCARD_STAR)) => true
15 | case _ => false
16 | }
17 |
18 | unit.body.foreach(analyzeTree {
19 | case t@Apply(fun, args)
20 | if fun.tpe != null && fun.tpe.params.lastOption.map(_.tpe.typeSymbol).contains(definitions.RepeatedParamClass) &&
21 | !args.lastOption.exists(isVarargParam) =>
22 |
23 | val required =
24 | fun.tpe.params.last.annotations.find(_.tree.tpe <:< atLeastAnnotTpe).map(_.tree.children.tail).collect {
25 | case List(Literal(Constant(n: Int))) => n
26 | }.getOrElse(0)
27 |
28 | val actual = args.size - fun.tpe.params.size + 1
29 |
30 | if (actual < required) {
31 | report(t.pos,
32 | s"This method requires at least $required arguments for its repeated parameter, $actual passed.")
33 | }
34 | })
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/analyzer/src/test/scala/com/avsystem/commons/analyzer/AnalyzerTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package analyzer
3 |
4 | import org.scalactic.source.Position
5 | import org.scalatest.Assertions
6 |
7 | import scala.reflect.internal.util.BatchSourceFile
8 | import scala.tools.nsc.plugins.Plugin
9 | import scala.tools.nsc.{Global, Settings}
10 |
11 | trait AnalyzerTest { this: Assertions =>
12 | val settings = new Settings
13 | settings.usejavacp.value = true
14 | settings.Yrangepos.value = true
15 | settings.pluginOptions.value ++= List("AVSystemAnalyzer:+_")
16 |
17 | val compiler: Global = new Global(settings) { global =>
18 | override protected def loadRoughPluginsList(): List[Plugin] =
19 | new AnalyzerPlugin(global) :: super.loadRoughPluginsList()
20 | }
21 |
22 | def compile(source: String): Unit = {
23 | compiler.reporter.reset()
24 | val run = new compiler.Run
25 | run.compileSources(List(new BatchSourceFile("test.scala", source)))
26 | }
27 |
28 | def assertErrors(source: String)(implicit pos: Position): Unit = {
29 | compile(source)
30 | assert(compiler.reporter.hasErrors)
31 | }
32 |
33 | def assertErrors(errors: Int, source: String)(implicit pos: Position): Unit = {
34 | compile(source)
35 | assert(compiler.reporter.errorCount == errors)
36 | }
37 |
38 | def assertNoErrors(source: String)(implicit pos: Position): Unit = {
39 | compile(source)
40 | assert(!compiler.reporter.hasErrors)
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/analyzer/src/test/scala/com/avsystem/commons/analyzer/Any2StringAddTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package analyzer
3 |
4 | import org.scalatest.funsuite.AnyFunSuite
5 |
6 | class Any2StringAddTest extends AnyFunSuite with AnalyzerTest {
7 | test("any2stringadd should be rejected") {
8 | assertErrors(
9 | """
10 | |object whatever {
11 | | whatever + "fag"
12 | |}
13 | """.stripMargin
14 | )
15 | }
16 |
17 | test("toString should not be rejected") {
18 | assertNoErrors(
19 | """
20 | |object whatever {
21 | | whatever.toString + "fag"
22 | |}
23 | """.stripMargin
24 | )
25 | }
26 |
27 | test("string interpolation should not be rejected") {
28 | assertNoErrors(
29 | """
30 | |object whatever {
31 | | s"${whatever}fag"
32 | |}
33 | """.stripMargin
34 | )
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/analyzer/src/test/scala/com/avsystem/commons/analyzer/BadSingletonComponentTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package analyzer
3 |
4 | import org.scalatest.funsuite.AnyFunSuite
5 |
6 | class BadSingletonComponentTest extends AnyFunSuite with AnalyzerTest {
7 | test("general") {
8 | assertErrors(5,
9 | """
10 | |import com.avsystem.commons.di._
11 | |
12 | |object test extends Components {
13 | | singleton(123)
14 | | val notDef = singleton(123)
15 | | def hasParams(param: Int) = singleton(param)
16 | | def hasTypeParams[T]: Component[T] = singleton(???)
17 | | def outerMethod: Component[Int] = {
18 | | def innerMethod = singleton(123)
19 | | innerMethod
20 | | }
21 | |
22 | | def good: Component[Int] = singleton(123)
23 | | def alsoGood: Component[Int] = { singleton(123) }
24 | | def goodAsWell: Component[Int] = singleton(123).dependsOn(good)
25 | |}
26 | """.stripMargin
27 | )
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/analyzer/src/test/scala/com/avsystem/commons/analyzer/CheckBincompatTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package analyzer
3 |
4 | import org.scalatest.funsuite.AnyFunSuite
5 |
6 | class CheckBincompatTest extends AnyFunSuite with AnalyzerTest {
7 | test("definitions of @bincompat annotated symbols should not be rejected") {
8 | assertNoErrors(
9 | """
10 | |import com.avsystem.commons.annotation.bincompat
11 | |
12 | |@bincompat class klass
13 | |
14 | |@bincompat object objekt {
15 | | @bincompat def method: Int = 42
16 | |}
17 | """.stripMargin
18 | )
19 | }
20 |
21 | test("usage of @bincompat annotated symbols should be rejected") {
22 | assertErrors(3,
23 | """
24 | |import com.avsystem.commons.annotation.bincompat
25 | |
26 | |@bincompat class klass
27 | |
28 | |@bincompat object objekt
29 | |
30 | |object outer {
31 | | @bincompat def method: Int = 42
32 | |}
33 | |
34 | |object test {
35 | | println(objekt)
36 | | println(new klass)
37 | | println(outer.method)
38 | |}
39 | """.stripMargin
40 | )
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/analyzer/src/test/scala/com/avsystem/commons/analyzer/DiscardedMonixTaskTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package analyzer
3 |
4 | import org.scalatest.funsuite.AnyFunSuite
5 |
6 | class DiscardedMonixTaskTest extends AnyFunSuite with AnalyzerTest {
7 | test("simple") {
8 | assertErrors(10,
9 | """
10 | |import monix.eval.Task
11 | |
12 | |object whatever {
13 | | def task: Task[String] = ???
14 | |
15 | | // errors from these
16 | | task
17 | |
18 | | { println(""); task }
19 | |
20 | | if(true) task else task
21 | |
22 | | try task catch { case _: Exception => task } finally task
23 | |
24 | | Seq(1,2,3).foreach(_ => task)
25 | |
26 | | while(true) task
27 | |
28 | | do task while(true)
29 | |
30 | | // no errors from these
31 | | Seq(1,2,3).map(_ => task)
32 | | val tsk = task
33 | |}
34 | """.stripMargin
35 | )
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/analyzer/src/test/scala/com/avsystem/commons/analyzer/ExplicitGenericsTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package analyzer
3 |
4 | import org.scalatest.funsuite.AnyFunSuite
5 |
6 | class ExplicitGenericsTest extends AnyFunSuite with AnalyzerTest {
7 | test("inferred generic should be rejected") {
8 | assertErrors(
9 | """
10 | |import com.avsystem.commons.analyzer.TestUtils
11 | |
12 | |object whatever {
13 | | val x = TestUtils.genericMethod(123)
14 | |}
15 | """.stripMargin
16 | )
17 | }
18 |
19 | test("inferred generic in macro should be rejected") {
20 | assertErrors(
21 | """
22 | |import com.avsystem.commons.analyzer.TestUtils
23 | |
24 | |object whatever {
25 | | val x = TestUtils.genericMacro(123)
26 | |}
27 | """.stripMargin
28 | )
29 | }
30 |
31 |
32 | test("explicit generic should not be rejected") {
33 | assertNoErrors(
34 | """
35 | |import com.avsystem.commons.analyzer.TestUtils
36 | |
37 | |object whatever {
38 | | val x = TestUtils.genericMethod[Int](123)
39 | |}
40 | """.stripMargin
41 | )
42 | }
43 |
44 | test("explicit generic in macro should not be rejected") {
45 | assertNoErrors(
46 | """
47 | |import com.avsystem.commons.analyzer.TestUtils
48 | |
49 | |object whatever {
50 | | val x = TestUtils.genericMacro[Int](123)
51 | |}
52 | """.stripMargin
53 | )
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/analyzer/src/test/scala/com/avsystem/commons/analyzer/FindUsagesTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package analyzer
3 |
4 | import org.scalatest.funsuite.AnyFunSuite
5 |
6 | class FindUsagesTest extends AnyFunSuite with AnalyzerTest {
7 | settings.pluginOptions.value ++= List("AVSystemAnalyzer:+findUsages:java.lang.String")
8 |
9 | test("java.lang.String usages should be found") {
10 | assertErrors(2,
11 | """
12 | |object whatever {
13 | | val x: String = String.valueOf(123)
14 | |}
15 | """.stripMargin
16 | )
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/analyzer/src/test/scala/com/avsystem/commons/analyzer/ImplicitTypesTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package analyzer
3 |
4 | import org.scalatest.funsuite.AnyFunSuite
5 |
6 | class ImplicitTypesTest extends AnyFunSuite with AnalyzerTest {
7 | test("implicit definitions without explicit types should be rejected") {
8 | assertErrors(2,
9 | """
10 | |object whatever {
11 | | implicit val x = 5
12 | | implicit val y: Int = 5
13 | | implicit def conv(x: Int) = x.toString
14 | | implicit def conv2(x: Int): String = x.toString
15 | | implicit object objekt
16 | | implicit class wtf(x: Int)
17 | |}
18 | """.stripMargin)
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/analyzer/src/test/scala/com/avsystem/commons/analyzer/ImportJavaUtilTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package analyzer
3 |
4 | import org.scalatest.funsuite.AnyFunSuite
5 |
6 | class ImportJavaUtilTest extends AnyFunSuite with AnalyzerTest {
7 | test("import java.util should be rejected") {
8 | assertErrors(
9 | """
10 | |import java.util
11 | |
12 | |object whatever
13 | """.stripMargin
14 | )
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/analyzer/src/test/scala/com/avsystem/commons/analyzer/TestUtils.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package analyzer
3 |
4 | import com.avsystem.commons.annotation.{atLeast, explicitGenerics, macroPrivate}
5 |
6 | import scala.reflect.macros.blackbox
7 |
8 | object TestUtils {
9 | def need3Params(@atLeast(3) args: Any*) = ()
10 |
11 | @macroPrivate
12 | def macroPrivateMethod = 42
13 |
14 | def invokeMacroPrivateMethod: Int = macro invokeMacroPrivateMethodImpl
15 |
16 | def invokeMacroPrivateMethodImpl(c: blackbox.Context): c.Tree = {
17 | import c.universe._
18 | q"${c.prefix}.macroPrivateMethod"
19 | }
20 |
21 | object Extractor {
22 | @macroPrivate def unapply(any: Any): Option[Any] = None
23 | }
24 |
25 | def genericMacroImpl[T](c: blackbox.Context)(arg: c.Tree): c.Tree = arg
26 |
27 | @explicitGenerics
28 | def genericMethod[T](arg: T): T = arg
29 | @explicitGenerics
30 | def genericMacro[T](arg: T): T = macro genericMacroImpl[T]
31 | }
32 |
--------------------------------------------------------------------------------
/analyzer/src/test/scala/com/avsystem/commons/analyzer/ThrowableObjectsTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package analyzer
3 |
4 | import org.scalatest.funsuite.AnyFunSuite
5 |
6 | class ThrowableObjectsTest extends AnyFunSuite with AnalyzerTest {
7 | test("throwable objects with stack trace should be rejected") {
8 | assertErrors(1,
9 | """
10 | |object throwableObject extends Throwable
11 | |object noStackTraceThrowableObject extends Throwable with scala.util.control.NoStackTrace
12 | """.stripMargin)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/analyzer/src/test/scala/com/avsystem/commons/analyzer/VarargsAtLeastTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package analyzer
3 |
4 | import org.scalatest.funsuite.AnyFunSuite
5 |
6 | class VarargsAtLeastTest extends AnyFunSuite with AnalyzerTest {
7 | test("too few varargs parameters should be rejected") {
8 | assertErrors(
9 | """
10 | |import com.avsystem.commons.analyzer.TestUtils
11 | |
12 | |object whatever {
13 | | TestUtils.need3Params(1, 2)
14 | |}
15 | """.stripMargin
16 | )
17 | }
18 |
19 | test("enough varargs parameters should not be rejected") {
20 | assertNoErrors(
21 | """
22 | |import com.avsystem.commons.analyzer.TestUtils
23 | |
24 | |object whatever {
25 | | TestUtils.need3Params(1, 2, 3)
26 | | TestUtils.need3Params(1, 2, 3, 4)
27 | |}
28 | """.stripMargin
29 | )
30 | }
31 |
32 | test("collection passed as varargs parameter should not be rejected") {
33 | assertNoErrors(
34 | """
35 | |import com.avsystem.commons.analyzer.TestUtils
36 | |
37 | |object whatever {
38 | | TestUtils.need3Params(List(1,2): _*)
39 | |}
40 | """.stripMargin
41 | )
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/benchmark/js/README.md:
--------------------------------------------------------------------------------
1 | How to run benchmark:
2 | - compile `sbt commons-benchmark-js/fullOptJS`
3 | - open `fullopt-2.13.html` file in a browser
4 | - select test suite and run
5 |
--------------------------------------------------------------------------------
/benchmark/js/fullopt-2.13.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | scalajs-benchmarks
5 |
6 |
7 | Loading...
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/benchmark/js/src/main/scala/com/avsystem/commons/Main.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 |
3 | import com.avsystem.commons.ser.{IsoInstantBenchmarks, JsonBenchmarks}
4 | import japgolly.scalajs.benchmark.gui.BenchmarkGUI
5 | import org.scalajs.dom._
6 |
7 | object Main {
8 | def main(args: Array[String]): Unit = {
9 | val body = document.getElementById("body")
10 | BenchmarkGUI.renderMenu(body)(
11 | IsoInstantBenchmarks.suite,
12 | JsonBenchmarks.suite
13 | )
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/benchmark/js/src/main/scala/com/avsystem/commons/ser/IsoInstantBenchmarks.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package ser
3 |
4 | import com.avsystem.commons.serialization.GenCodec.ReadFailure
5 | import japgolly.scalajs.benchmark.gui.GuiSuite
6 | import japgolly.scalajs.benchmark.{Benchmark, Suite}
7 |
8 | import scala.scalajs.js
9 | import scala.scalajs.js.RegExp
10 |
11 | object IsoInstantBenchmarks {
12 | private val regex: RegExp =
13 | js.RegExp("""^(\+|-)?[0-9]+-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]{3})?Z$""")
14 |
15 | def parse(string: String, validate: Boolean): Long = {
16 | def fail = throw new ReadFailure(s"invalid ISO instant: $string")
17 | if (!validate || regex.test(string)) {
18 | val parsed = js.Date.parse(string)
19 | if (parsed.isNaN) fail
20 | else parsed.toLong
21 | } else fail
22 | }
23 |
24 | val suite = GuiSuite(
25 | Suite("IsoInstant parsing")(
26 | Benchmark("with regex validation") {
27 | parse("2013-11-27T12:55:32.234Z", validate = true)
28 | },
29 | Benchmark("without regex validation") {
30 | parse("2013-11-27T12:55:32.234Z", validate = false)
31 | }
32 | )
33 | )
34 | }
35 |
--------------------------------------------------------------------------------
/build.sbt:
--------------------------------------------------------------------------------
1 | lazy val root = Commons.root
2 |
--------------------------------------------------------------------------------
/core/js/src/main/scala/com/avsystem/commons/jiop/JavaInterop.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package jiop
3 |
4 | trait JavaInterop extends AnyRef
5 | with JBasicUtils
6 | with JCollectionUtils
7 | with CompatAsJavaScalaExtensions
8 |
9 | object JavaInterop extends JavaInterop
10 |
--------------------------------------------------------------------------------
/core/js/src/main/scala/com/avsystem/commons/jsiop/JsInterop.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package jsiop
3 |
4 | import com.avsystem.commons.jsiop.JsInterop.{JsOptOps, UndefOrOps}
5 | import com.avsystem.commons.misc.TimestampConversions
6 |
7 | import scala.scalajs.js
8 | import scala.scalajs.js.UndefOr
9 |
10 | trait JsInterop {
11 | implicit def jsDateTimestampConversions(jsDate: js.Date): TimestampConversions =
12 | new TimestampConversions(jsDate.getTime().toLong)
13 |
14 | implicit def undefOrOps[A](undefOr: UndefOr[A]): UndefOrOps[A] =
15 | new UndefOrOps(undefOr)
16 |
17 | implicit def jsOptOps[A](opt: Opt[A]): JsOptOps[A] =
18 | new JsOptOps(opt.orNull[Any].asInstanceOf[A])
19 | }
20 | object JsInterop extends JsInterop {
21 | class UndefOrOps[A](private val value: UndefOr[A]) extends AnyVal {
22 | def toOpt: Opt[A] = if (value.isDefined) Opt(value.get) else Opt.Empty
23 | }
24 |
25 | class JsOptOps[A](private val raw: A) extends AnyVal {
26 | private def value = Opt(raw)
27 |
28 | def orUndefined: UndefOr[A] = if (value.isDefined) js.defined(value.get) else js.undefined
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/core/js/src/main/scala/com/avsystem/commons/misc/CrossUtils.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package misc
3 |
4 | import scala.scalajs.js
5 |
6 | object CrossUtils {
7 | type NativeArray[A] = js.Array[A]
8 | type NativeDict[A] = js.Dictionary[A]
9 |
10 | def newNativeArray[A](size: Int): NativeArray[A] = {
11 | val res = new js.Array[A](size)
12 | var idx = 0
13 | while (idx < size) {
14 | res(idx) = null.asInstanceOf[A]
15 | idx += 1
16 | }
17 | res
18 | }
19 |
20 | def newNativeDict[A]: NativeDict[A] = js.Dictionary.empty[A]
21 |
22 | def wrappedArray[A](elems: A*): MIndexedSeq[A] = js.Array(elems: _*)
23 | def arrayBuffer[A]: MIndexedSeq[A] with MBuffer[A] = js.Array[A]()
24 | def dictionary[A](keyValues: (String, A)*): MMap[String, A] = js.Dictionary(keyValues: _*)
25 | }
26 |
--------------------------------------------------------------------------------
/core/js/src/main/scala/com/avsystem/commons/misc/TimestampConversions.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons.misc
2 |
3 | import scala.scalajs.js
4 | import com.avsystem.commons.JDate
5 |
6 | final class TimestampConversions(private val millis: Long) extends AnyVal {
7 | def toTimestamp: Timestamp = Timestamp(millis)
8 | def toJsDate: js.Date = new js.Date(millis.toDouble)
9 | def toJDate: JDate = new JDate(millis)
10 | }
11 |
--------------------------------------------------------------------------------
/core/js/src/main/scala/com/avsystem/commons/serialization/IsoInstant.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package serialization
3 |
4 | import com.avsystem.commons.serialization.GenCodec.ReadFailure
5 |
6 | import scala.scalajs.js
7 | import scala.scalajs.js.RegExp
8 |
9 | object IsoInstant {
10 | private val regex: RegExp =
11 | js.RegExp("""^(\+|-)?[0-9]+-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\.[0-9]{3})?Z$""")
12 |
13 | def format(millis: Long): String =
14 | new js.Date(millis.toDouble).toISOString()
15 |
16 | def parse(string: String): Long = {
17 | def fail = throw new ReadFailure(s"invalid ISO instant: $string")
18 | if (regex.test(string)) {
19 | val parsed = js.Date.parse(string)
20 | if (parsed.isNaN) fail
21 | else parsed.toLong
22 | } else fail
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/core/js/src/test/scala/com/avsystem/commons/testutil/IO.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package testutil
3 |
4 | import scala.scalajs.js
5 | import scala.scalajs.js.annotation.{JSBracketAccess, JSGlobal, JSImport}
6 |
7 | @js.native
8 | @JSImport("fs", JSImport.Namespace)
9 | object NodeFS extends js.Object {
10 | def readFileSync(path: String, encoding: String = js.native): String = js.native
11 | def writeFileSync(path: String, data: String, encoding: String = js.native): Unit = js.native
12 | }
13 |
14 | @js.native
15 | @JSImport("path", JSImport.Namespace)
16 | object NodePath extends js.Object {
17 | val sep: String = js.native
18 | }
19 |
20 | @js.native
21 | @JSGlobal("process.env")
22 | object NodeEnv extends js.Object {
23 | @JSBracketAccess
24 | def get(name: String): String = js.native
25 | }
26 |
27 | object IO {
28 | private final val ResourcesPath = NodeEnv.get("RESOURCES_DIR")
29 |
30 | private def nativePath(path: String): String =
31 | ResourcesPath + path.replace("/", NodePath.sep)
32 |
33 | def readTestResource(path: String): String =
34 | NodeFS.readFileSync(nativePath(path), "UTF-8")
35 |
36 | def writeTestResource(path: String, data: String): Unit =
37 | NodeFS.writeFileSync(nativePath(path), data, "UTF-8")
38 | }
39 |
--------------------------------------------------------------------------------
/core/jvm/src/main/scala/com/avsystem/commons/jiop/JavaApi.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package jiop
3 |
4 | /**
5 | * Marks Scala method which exists only for Java compatibility / interoperability
6 | */
7 | class JavaApi extends StaticAnnotation
8 |
--------------------------------------------------------------------------------
/core/jvm/src/main/scala/com/avsystem/commons/jiop/JavaInterop.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package jiop
3 |
4 | trait JavaInterop extends AnyRef
5 | with JBasicUtils
6 | with JCollectionUtils
7 | with CompatAsJavaScalaExtensions
8 | with Java8CollectionUtils
9 | with JFunctionUtils
10 | with JStreamUtils
11 | with JOptionalUtils
12 | with JavaTimeInterop
13 |
14 | object JavaInterop extends JavaInterop
15 |
--------------------------------------------------------------------------------
/core/jvm/src/main/scala/com/avsystem/commons/jiop/JavaTimeInterop.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package jiop
3 |
4 | import java.time.Instant
5 |
6 | import com.avsystem.commons.jiop.JavaTimeInterop.InstantOps
7 | import com.avsystem.commons.misc.Timestamp
8 |
9 | trait JavaTimeInterop {
10 | implicit def instantOps(instant: Instant): InstantOps = new InstantOps(instant)
11 | }
12 | object JavaTimeInterop {
13 | class InstantOps(private val instant: Instant) extends AnyVal {
14 | def truncateToTimestamp: Timestamp = Timestamp(instant.toEpochMilli)
15 | def truncateToJDate: JDate = new JDate(instant.toEpochMilli)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/core/jvm/src/main/scala/com/avsystem/commons/jsiop/JsInterop.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package jsiop
3 |
4 | trait JsInterop
5 | object JsInterop extends JsInterop
6 |
--------------------------------------------------------------------------------
/core/jvm/src/main/scala/com/avsystem/commons/misc/CrossUtils.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package misc
3 |
4 | object CrossUtils {
5 | type NativeArray[A] = Array[A]
6 | type NativeDict[A] = MMap[String, A]
7 |
8 | def newNativeArray[A: ClassTag](size: Int): NativeArray[A] = new Array[A](size)
9 | def newNativeDict[A]: NativeDict[A] = new MHashMap[String, A]
10 | def unsetArrayValue: Any = null
11 |
12 | def wrappedArray[A: ClassTag](elems: A*): MIndexedSeq[A] = Array(elems: _*)
13 | def arrayBuffer[A]: MIndexedSeq[A] with MBuffer[A] = new MArrayBuffer[A]
14 | def dictionary[A](keyValues: (String, A)*): MMap[String, A] = MHashMap[String, A](keyValues: _*)
15 | }
16 |
--------------------------------------------------------------------------------
/core/jvm/src/main/scala/com/avsystem/commons/misc/TimestampConversions.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons.misc
2 |
3 | import java.time.Instant
4 |
5 | import com.avsystem.commons.JDate
6 |
7 | final class TimestampConversions(private val millis: Long) extends AnyVal {
8 | def toTimestamp: Timestamp = Timestamp(millis)
9 | def toInstant: Instant = Instant.ofEpochMilli(millis)
10 | def toJDate: JDate = new JDate(millis)
11 | }
12 |
--------------------------------------------------------------------------------
/core/jvm/src/main/scala/com/avsystem/commons/serialization/IsoInstant.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package serialization
3 |
4 | import java.time.Instant
5 | import java.time.format.DateTimeParseException
6 |
7 | import com.avsystem.commons.serialization.GenCodec.ReadFailure
8 |
9 | object IsoInstant {
10 | def format(millis: Long): String = {
11 | val res = Instant.ofEpochMilli(millis).toString
12 | // add trailing .000Z if omitted to align with JS implementation
13 | if (res.charAt(res.length - 5) == '.') res
14 | else res.substring(0, res.length - 1) + ".000Z"
15 | }
16 |
17 | def parse(string: String): Long =
18 | try Instant.parse(string).toEpochMilli catch {
19 | case _: DateTimeParseException => throw new ReadFailure(s"invalid ISO instant: $string")
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/core/jvm/src/test/scala/com/avsystem/commons/concurrent/JvmTaskExtensionsTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package concurrent
3 |
4 | import monix.eval.Task
5 | import monix.execution.Scheduler
6 | import org.scalatest.concurrent.ScalaFutures
7 | import org.scalatest.funsuite.AnyFunSuite
8 | import org.scalatest.matchers.should.Matchers
9 | import org.scalatestplus.scalacheck.ScalaCheckDrivenPropertyChecks
10 |
11 | import scala.concurrent.TimeoutException
12 | import scala.concurrent.duration._
13 |
14 | class JvmTaskExtensionsTest extends AnyFunSuite with Matchers with ScalaCheckDrivenPropertyChecks with ScalaFutures {
15 |
16 | import com.avsystem.commons.concurrent.TaskExtensions._
17 |
18 | private implicit val scheduler: Scheduler = Scheduler.global
19 |
20 | // This test does not work in SJS runtime (but the method itself does)
21 | test("lazyTimeout") {
22 | val result = Task.never.lazyTimeout(50.millis, "Lazy timeout").runToFuture.failed.futureValue
23 | result shouldBe a[TimeoutException]
24 | result.getMessage shouldBe "Lazy timeout"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/core/jvm/src/test/scala/com/avsystem/commons/di/ComponentComposition.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package di
3 |
4 | import scala.concurrent.Await
5 | import scala.concurrent.duration.Duration
6 |
7 | abstract class BaseComponent(implicit info: ComponentInfo) {
8 | println(s"$info init")
9 | }
10 |
11 | class SubDao(implicit info: ComponentInfo) extends BaseComponent
12 | class SubService(dao: SubDao)(implicit info: ComponentInfo) extends BaseComponent
13 |
14 | class SubSystem extends Components {
15 | override protected def componentNamePrefix: String = "sub."
16 |
17 | private val dao: Component[SubDao] =
18 | component(new SubDao)
19 |
20 | val service: Component[SubService] =
21 | component(new SubService(dao.ref))
22 | }
23 |
24 | class Service(subService: SubService)(implicit info: ComponentInfo) extends BaseComponent
25 |
26 | class System(subSystem: SubSystem) extends Components {
27 | val service: Component[Service] =
28 | component(new Service(subSystem.service.ref))
29 | }
30 |
31 | object ComponentComposition {
32 | def main(args: Array[String]): Unit = {
33 | val subSystem = new SubSystem
34 | val system = new System(subSystem)
35 |
36 | import ExecutionContext.Implicits.global
37 | Await.result(system.service.init, Duration.Inf)
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/core/jvm/src/test/scala/com/avsystem/commons/di/ComponentsTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package di
3 |
4 | import org.scalatest.funsuite.AnyFunSuite
5 |
6 | import scala.concurrent.Await
7 | import scala.concurrent.duration.Duration
8 |
9 | class ComponentsTest extends AnyFunSuite {
10 | object cycle extends Components {
11 | val a: Component[String] = component(b.ref.toUpperCase)
12 | val b: Component[String] = component(c.ref.toLowerCase)
13 | val c: Component[String] = component(a.ref.trim)
14 | }
15 |
16 | test("cycle detection test") {
17 | import ExecutionContext.Implicits.global
18 | assertThrows[DependencyCycleException](
19 | Await.result(cycle.a.init, Duration.Inf)
20 | )
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/core/jvm/src/test/scala/com/avsystem/commons/serialization/JCodecTestBase.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package serialization
3 |
4 | import CodecTestData._
5 |
6 | trait JCodecTestBase extends AbstractCodecTest {
7 | val jTreeMap = stringMap(new JTreeMap[String, Int])
8 | }
9 |
--------------------------------------------------------------------------------
/core/jvm/src/test/scala/com/avsystem/commons/serialization/JGenCodecTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package serialization
3 |
4 | import java.lang.annotation.RetentionPolicy
5 |
6 | import scala.collection.immutable.ListMap
7 |
8 | class JGenCodecTest extends JCodecTestBase with SimpleIOCodecTest {
9 | test("java collection test (TreeMap)") {
10 | testWrite[JSortedMap[String, Int]](jTreeMap, ListMap("1" -> 1, "2" -> 2, "3" -> 3))
11 | testWrite[JNavigableMap[String, Int]](jTreeMap, ListMap("1" -> 1, "2" -> 2, "3" -> 3))
12 | testWrite[JTreeMap[String, Int]](jTreeMap, ListMap("1" -> 1, "2" -> 2, "3" -> 3))
13 | }
14 |
15 | test("java enum test") {
16 | testWrite(RetentionPolicy.RUNTIME, "RUNTIME")
17 | testWrite(RetentionPolicy.SOURCE, "SOURCE")
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/core/jvm/src/test/scala/com/avsystem/commons/testutil/IO.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package testutil
3 |
4 | import java.io.{BufferedReader, File, FileWriter, InputStreamReader}
5 |
6 | import scala.annotation.tailrec
7 |
8 | object IO {
9 | def readTestResource(path: String): String = {
10 | val reader = new BufferedReader(new InputStreamReader(getClass.getResourceAsStream(path)))
11 | val buf = new Array[Char](2048)
12 | @tailrec def loop(sb: JStringBuilder): String = {
13 | reader.read(buf) match {
14 | case -1 => sb.toString
15 | case count => loop(sb.append(buf, 0, count))
16 | }
17 | }
18 | try loop(new JStringBuilder) finally reader.close()
19 | }
20 |
21 | def writeTestResource(path: String, data: String): Unit = {
22 | // assuming working dir used by intellij
23 | val writer = new FileWriter(s"src/test/resources$path".replace("/", File.separator))
24 | try writer.write(data) finally writer.close()
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/CommonAliases.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 |
3 | trait CommonAliases {
4 | type Try[+T] = scala.util.Try[T]
5 | final val Try = scala.util.Try
6 | type Success[+T] = scala.util.Success[T]
7 | final val Success = scala.util.Success
8 | type Failure[+T] = scala.util.Failure[T]
9 | final val Failure = scala.util.Failure
10 |
11 | type Future[+T] = scala.concurrent.Future[T]
12 | final val Future = scala.concurrent.Future
13 | type Promise[T] = scala.concurrent.Promise[T]
14 | final val Promise = scala.concurrent.Promise
15 | type ExecutionContext = scala.concurrent.ExecutionContext
16 | final val ExecutionContext = scala.concurrent.ExecutionContext
17 |
18 | final val NonFatal = scala.util.control.NonFatal
19 |
20 | type ClassTag[T] = scala.reflect.ClassTag[T]
21 | final val ClassTag = scala.reflect.ClassTag
22 | final def classTag[T: ClassTag]: ClassTag[T] = scala.reflect.classTag[T]
23 |
24 | type Annotation = scala.annotation.Annotation
25 | type StaticAnnotation = scala.annotation.StaticAnnotation
26 | }
27 | object CommonAliases extends CommonAliases
28 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/annotation/NotInheritedFromSealedTypes.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package annotation
3 |
4 | /**
5 | * Marker trait for annotations which don't want to be inherited by subtypes
6 | * of a sealed trait or class that has this annotation applied. Intended for annotations that should apply
7 | * only to the sealed trait itself.
8 | */
9 | trait NotInheritedFromSealedTypes extends StaticAnnotation
10 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/annotation/atLeast.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package annotation
3 |
4 | /**
5 | * When applied on varargs parameter, indicates that at least some number of parameters is required.
6 | * This is later checked by the static analyzer.
7 | *
8 | * WARNING: implementation of method which takes a varargs parameter may NOT assume that given number of
9 | * arguments will always be passed, because it's still possible to pass a `Seq` where
10 | * varargs parameter is required using the `: _*` ascription, e.g.
11 | * {{{
12 | * varargsMethod(List(): _*)
13 | * }}}
14 | * and that is not checked by the static analyzer.
15 | */
16 | class atLeast(n: Int) extends StaticAnnotation
17 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/annotation/bincompat.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package annotation
3 |
4 | /**
5 | * Marks symbols which exist only for binary compatibility with previous versions.
6 | * These symbols should never be used directly. This is checked by `commons-analyzer` plugin.
7 | * Additionally, it's recommended to make these symbols package private so that they cannot be used
8 | * directly from Scala code but remain public in bytecode.
9 | */
10 | class bincompat extends Annotation
11 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/annotation/defaultsToName.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package annotation
3 |
4 | /**
5 | * Meta annotation that may be used on `String` constructor parameter of an annotation. This constructor parameter
6 | * must take a default `null` value. [[defaultsToName]] makes annotation processing macro engines insert the name
7 | * of annotated symbol instead of `null`.
8 | *
9 | * @example
10 | *
11 | * {{{
12 | * class SomeMethodAnnotation(@defaultsToName val name: String = null)
13 | *
14 | * @SomeMethodAnnotation def someMethod: String
15 | * }}}
16 | *
17 | * Now, when some macro engine has to inspect `SomeMethodAnnotation` of `someMethod`, it will automatically insert
18 | * the string "someMethod" as the argument of `SomeMethodAnnotation`.
19 | */
20 | class defaultsToName extends StaticAnnotation
21 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/annotation/explicitGenerics.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package annotation
3 |
4 | /**
5 | * When applied on generic method, requires that all the type parameters are given explicitly
6 | * (cannot be inferred by the compiler). This is meant primarily for methods whose generics cannot be
7 | * inferred from method arguments. Requiring that the programmer specifies them explicitly is a protection
8 | * against the compiler inferring `Nothing` or `Null`.
9 | * {{{
10 | * @explicitGenerics
11 | * def readJson[T: GenCodec](json: String): T = ...
12 | *
13 | * // raise error, because otherwise we have a hidden bug - the compiler infers `Nothing` in place of `T`
14 | * val x: MyType = readJson("{}")
15 | * // ok
16 | * val x = readJson[MyType]("{}")
17 | * }}}
18 | */
19 | class explicitGenerics extends StaticAnnotation
20 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/annotation/macroPrivate.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package annotation
3 |
4 | /**
5 | * Symbols annotated with this annotation can only be used in macro-generated code.
6 | */
7 | class macroPrivate extends StaticAnnotation
8 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/annotation/positioned.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package annotation
3 |
4 | /**
5 | * Annotate a symbol (i.e. class, method, parameter, etc.) with `@positioned(positioned.here)` to retain source
6 | * position information for that symbol to be available in macro implementations which inspect that symbol.
7 | * This is necessary e.g. for determining declaration order of subtypes of sealed hierarchies in macro implementations.
8 | * This annotation is only needed when macro is invoked in a different source file than the source file of inspected
9 | * symbol. If macro is invoked in the same file, source position is always available.
10 | */
11 | class positioned(val point: Int) extends StaticAnnotation
12 | object positioned {
13 | def here: Int = macro macros.misc.MiscMacros.posPoint
14 | }
15 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/annotation/showAst.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package annotation
3 |
4 | /**
5 | * When applied on a definition (`class`, `object`, `def`, `val`, etc.) or expression, will cause the
6 | * AVSystem static analyzer to print compilation error with AST of annotated code fragment.
7 | * This is useful primarily for debugging macro expansions.
8 | */
9 | class showAst extends StaticAnnotation
10 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/collection/MutableStack.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package collection
3 |
4 | final class MutableStack[T] {
5 | private[this] var ssize: Int = 0
6 | private[this] var stack = List.empty[T]
7 |
8 | def push(elem: T): Unit = {
9 | stack ::= elem
10 | ssize += 1
11 | }
12 |
13 | def pushAll(elems: IterableOnce[T]): Unit =
14 | elems.iterator.foreach(push)
15 |
16 | def pop(): T = stack match {
17 | case head :: tail =>
18 | stack = tail
19 | ssize -= 1
20 | head
21 | case Nil => throw new NoSuchElementException("pop on empty stack")
22 | }
23 |
24 | def popOpt(): Opt[T] = stack match {
25 | case head :: tail =>
26 | stack = tail
27 | ssize -= 1
28 | Opt.some(head)
29 | case Nil => Opt.Empty
30 | }
31 |
32 | def popOption(): Option[T] = stack match {
33 | case head :: tail =>
34 | stack = tail
35 | ssize -= 1
36 | Some(head)
37 | case Nil => None
38 | }
39 |
40 | def top: T = stack match {
41 | case head :: _ => head
42 | case _ => throw new NoSuchElementException("top on empty stack")
43 | }
44 |
45 | def topOpt: Opt[T] = stack match {
46 | case head :: _ => Opt.some(head)
47 | case _ => Opt.Empty
48 | }
49 |
50 | def topOption: Option[T] =
51 | stack.headOption
52 |
53 | def asList: List[T] =
54 | stack
55 |
56 | def size: Int =
57 | ssize
58 |
59 | def isEmpty: Boolean =
60 | stack.isEmpty
61 | }
62 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/collection/crossBuilders.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package collection
3 |
4 | import scala.collection.Factory
5 |
6 | trait CrossBuilder[-Elem, +To] extends MBuilder[Elem, To]
7 | trait CrossFactory[-A, +C] extends Factory[A, C]
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/concurrent/DurationPostfixConverters.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package concurrent
3 |
4 | import scala.concurrent.duration._
5 |
6 | trait DurationPostfixConverters {
7 | implicit def durationInt(int: Int): DurationInt = new DurationInt(int)
8 | implicit def durationLong(long: Long): DurationLong = new DurationLong(long)
9 | implicit def durationDouble(double: Double): DurationDouble = new DurationDouble(double)
10 | }
11 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/derivation/AllowImplicitMacro.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package derivation
3 |
4 | /**
5 | * Marker type used internally by automatic type class derivation macros.
6 | * Used to inform the compiler and macro engine that automatic derivation of particular type class is
7 | * allowed in some context.
8 | */
9 | sealed trait AllowImplicitMacro[T]
10 | object AllowImplicitMacro {
11 | def apply[T]: AllowImplicitMacro[T] = null
12 | }
13 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/derivation/DeferredInstance.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package derivation
3 |
4 | trait DeferredInstance[T] { this: T =>
5 | var underlying: T = _
6 | }
7 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/derivation/Materialized.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package derivation
3 |
4 | /**
5 | * Marker trait for macro-materialized typeclass instances. Also may serve to prioritize macro-generated instance
6 | * over some other implicit.
7 | */
8 | trait Materialized
9 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/jiop/CompatAsJavaScalaExtensions.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package jiop
3 |
4 | import scala.collection.convert.{AsJavaExtensions, AsScalaExtensions}
5 |
6 | trait CompatAsJavaScalaExtensions extends AsJavaExtensions with AsScalaExtensions
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/jiop/JBasicUtils.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package jiop
3 |
4 | import java.util.Comparator
5 | import java.util.concurrent.Callable
6 | import java.{lang => jl, math => jm, util => ju}
7 |
8 | import com.avsystem.commons.misc.{Sam, TimestampConversions}
9 |
10 | trait JBasicUtils {
11 | def jRunnable(code: => Any) = Sam[Runnable](code)
12 | def jCallable[T](expr: => T) = Sam[Callable[T]](expr)
13 | def jComparator[T](cmp: (T, T) => Int) = Sam[Comparator[T]](cmp)
14 |
15 | implicit def jDateTimestampConversions(date: JDate): TimestampConversions =
16 | new TimestampConversions(date.getTime)
17 |
18 | type JByte = jl.Byte
19 | type JShort = jl.Short
20 | type JInteger = jl.Integer
21 | type JLong = jl.Long
22 | type JFloat = jl.Float
23 | type JDouble = jl.Double
24 | type JBoolean = jl.Boolean
25 | type JCharacter = jl.Character
26 | type JBigInteger = jm.BigInteger
27 | type JBigDecimal = jm.BigDecimal
28 | type JDate = ju.Date
29 | type JNumber = jl.Number
30 | type JVoid = jl.Void
31 | type JEnum[E <: jl.Enum[E]] = jl.Enum[E]
32 | type JStringBuilder = jl.StringBuilder
33 | }
34 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/meta/Fallback.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package meta
3 |
4 | /**
5 | * Wrap your implicit instance of `AsReal`, `AsRaw`, `AsRawReal` or RPC metadata (with companion that extends
6 | * `RpcMetadataCompanion`) into Fallback` in order to lower its implicit priority.
7 | * Useful when some implicit must be imported but we don't want it to get higher priority that imports normally
8 | * have over implicit scope (e.g. implicits from companion objects).
9 | *
10 | * NOTE: `Fallback` does not work for *all* typeclasses, only RPC-related ones (`AsReal`, `AsRaw`, etc).
11 | * You can make it work with your own typeclass, but you must define appropriate forwarder in its companion, e.g.
12 | * {{{
13 | * trait FallbackAwareTC[T] { ... }
14 | * object FallbackAwareTC {
15 | * implicit def fromFallback[T](implicit f: Fallback[FallbackAwareTC[T]]): FallbackAwareTC[T] = f.value
16 | * }
17 | * }}}
18 | **/
19 | case class Fallback[+T](value: T) extends AnyVal
20 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/misc/Bidirectional.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package misc
3 |
4 | /** Creates reversed partial function. */
5 | object Bidirectional {
6 | def apply[A, B](pf: PartialFunction[A, B]): (PartialFunction[A, B], PartialFunction[B, A]) = macro com.avsystem.commons.macros.misc.BidirectionalMacro.impl[A, B]
7 | }
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/misc/Bytes.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package misc
3 |
4 | import java.nio.charset.StandardCharsets
5 |
6 | import com.avsystem.commons.serialization.Base64
7 |
8 | /**
9 | * General purpose wrapper over byte array which adds `equals`, `hashCode` and `toString` which
10 | * work on array elements instead of object identity.
11 | */
12 | final case class Bytes(bytes: Array[Byte]) {
13 | def hex: String = bytes.iterator.map(b => f"${b & 0xFF}%02X").mkString
14 |
15 | def base64: String = base64()
16 | def base64(withoutPadding: Boolean = false, urlSafe: Boolean = false): String =
17 | Base64.encode(bytes, withoutPadding, urlSafe)
18 |
19 | override def hashCode(): Int = java.util.Arrays.hashCode(bytes)
20 | override def equals(obj: Any): Boolean = obj match {
21 | case Bytes(obytes) => java.util.Arrays.equals(bytes, obytes)
22 | case _ => false
23 | }
24 | override def toString: String = hex
25 | }
26 | object Bytes {
27 | def apply(str: String): Bytes = Bytes(str.getBytes(StandardCharsets.UTF_8))
28 | def fromHex(hex: String): Bytes = Bytes(hex.grouped(2).map(Integer.parseInt(_, 16).toByte).toArray)
29 | def fromBase64(base64: String, urlSafe: Boolean = false): Bytes = Bytes(Base64.decode(base64, urlSafe))
30 | }
31 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/misc/CharSubSequence.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package misc
3 |
4 | final class CharSubSequence private(
5 | private val seq: CharSequence,
6 | private val start: Int,
7 | private val end: Int
8 | ) extends CharSequence {
9 | def length: Int = end - start
10 |
11 | def charAt(index: Int): Char =
12 | if (index >= 0 && index < length) seq.charAt(start + index)
13 | else throw new IndexOutOfBoundsException(index.toString)
14 |
15 | def subSequence(subStart: Int, subEnd: Int): CharSequence =
16 | if (subStart < 0 || subEnd > length || subStart > subEnd) throw new IllegalArgumentException
17 | else new CharSubSequence(seq, start + subStart, start + subEnd)
18 |
19 | override def toString: String = {
20 | val res = new JStringBuilder
21 | res.append(seq, start, end)
22 | res.toString
23 | }
24 | }
25 | object CharSubSequence {
26 | /**
27 | * Creates a subsequence of a `CharSequence` which is guaranteed to be a wrapper and never a copy
28 | * (unlike standard `subSequence` method).
29 | */
30 | def apply(seq: CharSequence, start: Int, end: Int): CharSequence =
31 | if (start < 0 || end > seq.length || start > end) throw new IllegalArgumentException
32 | else if (start == 0 && end == seq.length) seq
33 | else new CharSubSequence(seq, start, end)
34 | }
35 |
36 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/misc/Delegation.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package misc
3 |
4 | /**
5 | * A typeclass which witnesses that type `A` can be wrapped into trait or abstract class `B`
6 | */
7 | trait Delegation[A, B] {
8 | def delegate(a: A): B
9 | }
10 |
11 | object Delegation {
12 | implicit def materializeDelegation[A, B]: Delegation[A, B] =
13 | macro com.avsystem.commons.macros.misc.DelegationMacros.materializeDelegation[A, B]
14 |
15 | /**
16 | * Provides following syntax:
17 | *
18 | * Delegation[TargetType](value)
19 | *
20 | */
21 | def apply[B] = new CurriedDelegation[B]
22 |
23 | class CurriedDelegation[B] {
24 | def apply[A](source: A): B = macro com.avsystem.commons.macros.misc.DelegationMacros.delegate[A, B]
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/misc/MiscAliases.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package misc
3 |
4 | trait MiscAliases {
5 | type Opt[+A] = com.avsystem.commons.misc.Opt[A]
6 | final val Opt = com.avsystem.commons.misc.Opt
7 | type OptArg[+A] = com.avsystem.commons.misc.OptArg[A]
8 | final val OptArg = com.avsystem.commons.misc.OptArg
9 | type NOpt[+A] = com.avsystem.commons.misc.NOpt[A]
10 | final val NOpt = com.avsystem.commons.misc.NOpt
11 | type OptRef[+A >: Null] = com.avsystem.commons.misc.OptRef[A]
12 | final val OptRef = com.avsystem.commons.misc.OptRef
13 | }
14 | object MiscAliases extends MiscAliases
15 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/misc/Sam.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package misc
3 |
4 | object Sam {
5 | /**
6 | * Implements a single abstract method trait/class `T` using passed function or expression as implementation
7 | * of the sole abstract method. The argument passed may be either a function that must match the signature
8 | * of the abstract method or - in case the method does not take any arguments - an expression which will be returned
9 | * in the implementation of abstract method (as if the expression was passed as by-name parameter).
10 | */
11 | def apply[T](fun: => Any): T = macro com.avsystem.commons.macros.misc.SamMacros.createSam[T]
12 | }
13 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/misc/SamCompanion.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package misc
3 |
4 | import com.avsystem.commons.misc.SamCompanion.ValidSam
5 |
6 | abstract class SamCompanion[T, F](implicit vs: ValidSam[T, F]) {
7 | def apply(fun: F): T = macro com.avsystem.commons.macros.misc.SamMacros.toSam[T, F]
8 | }
9 |
10 | object SamCompanion {
11 | sealed trait ValidSam[T, F]
12 | object ValidSam {
13 | implicit def isValidSam[T, F]: ValidSam[T, F] = macro com.avsystem.commons.macros.misc.SamMacros.validateSam[T, F]
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/misc/ScalaDurationExtensions.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package misc
3 |
4 | import scala.concurrent.duration.{DoubleMult, DurationDouble, DurationInt, DurationLong, IntMult, LongMult}
5 |
6 | /**
7 | * Gathers all extensions from [[scala.concurrent.duration]] into one trait that can be mixed in with package object.
8 | */
9 | trait ScalaDurationExtensions {
10 | implicit def durationIntOps(n: Int): DurationInt = new DurationInt(n)
11 | implicit def durationLongOps(n: Long): DurationLong = new DurationLong(n)
12 | implicit def durationDoubleOps(d: Double): DurationDouble = new DurationDouble(d)
13 | implicit def durationIntMulOps(i: Int): IntMult = new IntMult(i)
14 | implicit def durationLongMulOps(i: Long): LongMult = new LongMult(i)
15 | implicit def durationDoubleMulOps(d: Double): DoubleMult = new DoubleMult(d)
16 | }
17 | object ScalaDurationExtensions extends ScalaDurationExtensions
18 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/misc/SelfInstance.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package misc
3 |
4 | case class SelfInstance[C[_]](instance: C[_])
5 | object SelfInstance {
6 | implicit def materialize[C[_]]: SelfInstance[C] = macro macros.misc.MiscMacros.selfInstance[C[_]]
7 | }
8 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/misc/SimpleClassName.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package misc
3 |
4 | case class SimpleClassName[T](name: String) extends AnyVal
5 | object SimpleClassName {
6 | def of[T](implicit scn: SimpleClassName[T]): String = scn.name
7 |
8 | implicit def materialize[T]: SimpleClassName[T] = macro macros.misc.MiscMacros.simpleClassName[T]
9 | }
10 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/misc/SourceInfo.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package misc
3 |
4 | /**
5 | * Macro-materialized implicit value that provides information about callsite source file position.
6 | * It can be used in runtime for logging and debugging purposes.
7 | * Similar to Scalactic's `Position`, but contains more information.
8 | */
9 | case class SourceInfo(
10 | filePath: String,
11 | fileName: String,
12 | offset: Int,
13 | line: Int,
14 | column: Int,
15 | lineContent: String,
16 | enclosingSymbols: List[String]
17 | ) {
18 | override def equals(obj: Any): Boolean = obj match {
19 | case otherInfo: SourceInfo => filePath ==
20 | otherInfo.filePath && offset == otherInfo.offset
21 | case _ => false
22 | }
23 |
24 | override def hashCode: Int =
25 | (filePath, offset).hashCode
26 | }
27 |
28 | object SourceInfo {
29 | def apply()(implicit si: SourceInfo): SourceInfo = si
30 |
31 | implicit def here: SourceInfo = macro macros.misc.MiscMacros.sourceInfo
32 | }
33 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/misc/ValueOf.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package misc
3 |
4 | import com.avsystem.commons.macros.misc.MiscMacros
5 |
6 | import scala.annotation.implicitNotFound
7 |
8 | /**
9 | * Macro materialized typeclass which captures the single value of a singleton type.
10 | */
11 | @implicitNotFound("Cannot derive value of ${T} - is not a singleton type")
12 | class ValueOf[T](val value: T) extends AnyVal
13 | object ValueOf {
14 | def apply[T](implicit vof: ValueOf[T]): T = vof.value
15 |
16 | implicit def mkValueOf[T]: ValueOf[T] = macro MiscMacros.mkValueOf[T]
17 | }
18 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/misc/package.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 |
3 | package object misc {
4 | type OptBase[+A] = IterableOnce[A]
5 | }
6 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/package.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem
2 |
3 | import com.avsystem.commons.collection.CollectionAliases
4 | import com.avsystem.commons.jiop.JavaInterop
5 | import com.avsystem.commons.jsiop.JsInterop
6 | import com.avsystem.commons.misc.MiscAliases
7 |
8 | package object commons
9 | extends SharedExtensions with CommonAliases with MiscAliases with CollectionAliases with JavaInterop with JsInterop
10 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/rpc/MetadataAnnotation.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package rpc
3 |
4 | /**
5 | * Annotations that extend this trait will be retained for runtime in `RPCMetadata` typeclass instances
6 | */
7 | trait MetadataAnnotation extends StaticAnnotation
8 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/rpc/RawRpcCompanion.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package rpc
3 |
4 | import com.avsystem.commons.macros.rpc.RpcMacros
5 |
6 | /**
7 | * Base trait for companion objects of raw RPC traits.
8 | */
9 | trait RawRpcCompanion[Raw] {
10 | type AsRawRpc[Real] = AsRaw[Raw, Real]
11 | type AsRealRpc[Real] = AsReal[Raw, Real]
12 | type AsRawRealRpc[Real] = AsRawReal[Raw, Real]
13 |
14 | def asRealRpc[Real](implicit asReal: AsRealRpc[Real]): AsRealRpc[Real] = asReal
15 | def asRawRpc[Real](implicit asRaw: AsRawRpc[Real]): AsRawRpc[Real] = asRaw
16 | def asRawRealRpc[Real](implicit asRawReal: AsRawRealRpc[Real]): AsRawRealRpc[Real] = asRawReal
17 |
18 | def asReal[Real](raw: Raw)(implicit asRealRpc: AsRealRpc[Real]): Real = asRealRpc.asReal(raw)
19 | def asRaw[Real](real: Real)(implicit asRawRpc: AsRawRpc[Real]): Raw = asRawRpc.asRaw(real)
20 |
21 | def materializeAsRaw[Real]: AsRawRpc[Real] = macro RpcMacros.rpcAsRaw[Raw, Real]
22 | def materializeAsReal[Real]: AsRealRpc[Real] = macro RpcMacros.rpcAsReal[Raw, Real]
23 | def materializeAsRawReal[Real]: AsRawRealRpc[Real] = macro RpcMacros.rpcAsRawReal[Raw, Real]
24 |
25 | /**
26 | * Like [[materializeAsRaw]] but for arbitrary real type instead of RPC trait.
27 | * Scans all public methods of the real type (instead of abstract methods for RPC trait).
28 | */
29 | def materializeApiAsRaw[Real]: AsRawRpc[Real] = macro RpcMacros.apiAsRaw[Raw, Real]
30 | }
31 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/rpc/RawValueCompanion.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package rpc
3 |
4 | abstract class RawValueCompanion[Raw] {
5 | type AsRaw[Real] = rpc.AsRaw[Raw, Real]
6 | type AsReal[Real] = rpc.AsReal[Raw, Real]
7 | type AsRawReal[Real] = rpc.AsRawReal[Raw, Real]
8 | }
9 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/rpc/RpcMetadataCompanion.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package rpc
3 |
4 | import com.avsystem.commons.macros.rpc.RpcMacros
5 | import com.avsystem.commons.meta.MetadataCompanion
6 |
7 | /**
8 | * Base trait for companion objects of RPC metadata classes.
9 | *
10 | * RPC metadata class is a generic class which captures information about some RPC trait's API (its abstract methods).
11 | * The `materialize` macro is responsible for doing this compile-time reflection. It is steered by various
12 | * meta-annotations present in the definition of the metadata class, e.g. [[rpcMethodMetadata]].
13 | *
14 | * @tparam M metadata class type constructor
15 | */
16 | trait RpcMetadataCompanion[M[_]] extends MetadataCompanion[M] {
17 | def materialize[Real]: M[Real] = macro RpcMacros.rpcMetadata[Real]
18 | }
19 |
20 | /**
21 | * Like [[RpcMetadataCompanion]] but reflects over the entire public API of a particular Scala type
22 | * (unlike RPC traits which only have their abstract methods captured).
23 | */
24 | trait ApiMetadataCompanion[M[_]] extends MetadataCompanion[M] {
25 | def materialize[Real]: M[Real] = macro macros.rpc.RpcMacros.apiMetadata[Real]
26 | }
27 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/serialization/Base64.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package serialization
3 |
4 | import java.util.{Base64 => JBase64}
5 |
6 | object Base64 {
7 | def encode(bytes: Array[Byte], withoutPadding: Boolean = false, urlSafe: Boolean = false): String = {
8 | val encoder = (if (urlSafe) JBase64.getUrlEncoder else JBase64.getEncoder) |>
9 | (e => if (withoutPadding) e.withoutPadding else e)
10 | encoder.encodeToString(bytes)
11 | }
12 |
13 | def decode(base64: String, urlSafe: Boolean = false): Array[Byte] = {
14 | val decoder = if (urlSafe) JBase64.getUrlDecoder else JBase64.getDecoder
15 | decoder.decode(base64)
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/serialization/DefaultCaseObjectInput.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package serialization
3 |
4 | import com.avsystem.commons.serialization.GenCodec.ReadFailure
5 |
6 | final class DefaultCaseObjectInput(firstField: FieldInput, actualInput: ObjectInput, caseFieldName: String)
7 | extends ObjectInput {
8 |
9 | override def knownSize: Int = actualInput.knownSize
10 |
11 | private[this] var atFirstField = true
12 |
13 | def hasNext: Boolean = atFirstField || actualInput.hasNext
14 | def nextField(): FieldInput =
15 | if (atFirstField) {
16 | atFirstField = false
17 | firstField
18 | } else {
19 | val field = actualInput.nextField()
20 | if (field.fieldName == caseFieldName) {
21 | throw new ReadFailure(s"$caseFieldName field found too far into the object")
22 | }
23 | field
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/serialization/IgnoreTransientDefaultMarker.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package serialization
3 |
4 | /**
5 | * Instructs [[GenCodec]] to ignore the [[transientDefault]] annotation when serializing a case class.
6 | * This ensures that even if a field's value is the same as its default, it will be included in the serialized
7 | * representation. Deserialization behavior remains unchanged. If a field is missing from the input, the default
8 | * value will be used as usual.
9 | *
10 | * This marker can be helpful when using the same model class in multiple contexts with different serialization
11 | * formats that have conflicting requirements for handling default values.
12 | *
13 | * @see [[CustomMarkersOutputWrapper]] for an easy way to add markers to existing [[Output]] implementations
14 | */
15 | object IgnoreTransientDefaultMarker extends CustomEventMarker[Unit]
16 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/serialization/OptionalFieldValueCodec.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package serialization
3 |
4 | import com.avsystem.commons.meta.OptionLike
5 |
6 | final class OptionalFieldValueCodec[O, V](optionLike: OptionLike.Aux[O, V], valueCodec: GenCodec[V]) extends GenCodec[O] {
7 | def read(input: Input): O =
8 | if (optionLike.ignoreNulls && input.readNull()) optionLike.none
9 | else optionLike.some(valueCodec.read(input))
10 |
11 | def write(output: Output, value: O): Unit =
12 | optionLike.fold(value, output.writeNull())(valueCodec.write(output, _))
13 | }
14 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/serialization/PeekingObjectInput.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package serialization
3 |
4 | /**
5 | * Wrapper over [[ObjectInput]] that lets you peek next field name without advancing the input.
6 | */
7 | final class PeekingObjectInput(original: ObjectInput) extends ObjectInput {
8 | private[this] var peekedField: FieldInput = _
9 |
10 | override def knownSize: Int = original.knownSize
11 |
12 | def peekNextFieldName: Opt[String] = peekedField match {
13 | case null if original.hasNext =>
14 | peekedField = original.nextField()
15 | peekedField.fieldName.opt
16 | case null => Opt.Empty
17 | case fi => fi.fieldName.opt
18 | }
19 |
20 | def nextField(): FieldInput =
21 | peekedField match {
22 | case null => original.nextField()
23 | case fi =>
24 | peekedField = null
25 | fi
26 | }
27 |
28 | def hasNext: Boolean =
29 | peekedField != null || original.hasNext
30 |
31 | override def peekField(name: String): Opt[FieldInput] =
32 | original.peekField(name)
33 | }
34 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/serialization/SerializationName.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package serialization
3 |
4 | import com.avsystem.commons.misc.{AnnotationOf, SimpleClassName}
5 |
6 | case class SerializationName[T](name: String) extends AnyVal
7 | object SerializationName extends SerializationNameLowPrio {
8 | def of[T](implicit sn: SerializationName[T]): String = sn.name
9 |
10 | implicit def fromNameAnnot[T](implicit nameAnnot: AnnotationOf[name, T]): SerializationName[T] =
11 | SerializationName(nameAnnot.annot.name)
12 | }
13 | trait SerializationNameLowPrio { this: SerializationName.type =>
14 | implicit def fromSimpleClassName[T: SimpleClassName]: SerializationName[T] =
15 | SerializationName(SimpleClassName.of[T])
16 | }
17 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/serialization/defaultCase.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package serialization
3 |
4 | /**
5 | * When materializing a `GenCodec` for sealed hierarchy with `@flatten` annotation, you can use this
6 | * annotation on one of case classes or objects to mark it as the default one. If during deserialization the
7 | * codec is unable to find the `_case` field and determine the case class/object to deserialize, it will try to
8 | * deserialize the data to the class/object marked with this annotation.
9 | *
10 | * This is useful for retaining backwards compatibility with serialized format when refactoring code and replacing
11 | * a simple case class with a sealed hierarchy.
12 | *
13 | * @param transient if `true`, the codec will also not emit the `_case` field during writing of default
14 | * case class/object (analogous to [[transientDefault]]).
15 | */
16 | class defaultCase(val transient: Boolean) extends StaticAnnotation {
17 | def this() = this(transient = false)
18 | }
19 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/serialization/flatten.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package serialization
3 |
4 | /**
5 | * Changes the serialization format used by `GenCodec`s automatically derived for sealed hierarchies.
6 | * The format is changed from "nested" to "flat".
7 | *
8 | * {{{
9 | * @flatten sealed trait Value
10 | * case class Numeric(int: Int) extends Value
11 | * case class Textual(string: String) extends Value
12 | * object Value {
13 | * implicit val codec: GenCodec[Value] = GenCodec.materialize[Value]
14 | * }
15 | * }}}
16 | *
17 | * Without [[flatten]] annotation, the "nested" format is used, e.g. when `Numeric(42)` would be encoded to JSON
18 | * as:
19 | *
20 | * {{{
21 | * {"Numeric": {"int": 42}}
22 | * }}}
23 | *
24 | * but when [[flatten]] annotation is applied on sealed trait/class, then it changes to:
25 | *
26 | * {{{
27 | * {"_case": "Numeric", "int": 42}
28 | * }}}
29 | *
30 | * The "_case" field name can be customized with annotation parameter
31 | */
32 | class flatten(val caseFieldName: String) extends StaticAnnotation {
33 | def this() = this("_case")
34 | }
35 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/serialization/generated.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package serialization
3 |
4 | /**
5 | * May be used on members of objects, case classes or any types having companion object with case class
6 | * like `apply` and `unapply`/`unapplySeq` methods in order to add additional, generated fields to data serialized by
7 | * auto-materialized `GenCodec`s.
8 | *
9 | * {{{
10 | * case class User(id: Long, login: String) {
11 | * @generated def upperLogin: String = login.toUpperCase
12 | * }
13 | * object User {
14 | * implicit val codec: GenCodec[User] = GenCodec.materialize[User]
15 | * }
16 | * }}}
17 | *
18 | * This annotation may be applied on `val`s, `var`s and `def`s. When applied on a `def`, it must be either parameterless
19 | * (no parameter lists or empty parameter list) or accept only implicit parameters, provided that all the implicit values
20 | * are available in the scope where `GenCodec` is materialized (implicit values will be "baked in" the codec).
21 | *
22 | * NOTE: `@generated` annotation may be defined on any level of inheritance hierarchy - it will be inherited
23 | * from implemented and overridden members.
24 | */
25 | class generated extends StaticAnnotation
26 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/serialization/json/JsonType.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package serialization.json
3 |
4 | import com.avsystem.commons.misc.{AbstractValueEnum, AbstractValueEnumCompanion, EnumCtx}
5 | import com.avsystem.commons.serialization.InputMetadata
6 |
7 | final class JsonType(implicit enumCtx: EnumCtx) extends AbstractValueEnum
8 | object JsonType extends AbstractValueEnumCompanion[JsonType] with InputMetadata[JsonType] {
9 | final val list, `object`, number, string, boolean, `null`: Value = new JsonType
10 | }
11 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/serialization/json/RawJson.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package serialization.json
3 |
4 | import com.avsystem.commons.serialization.TypeMarker
5 |
6 | object RawJson extends TypeMarker[String]
7 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/serialization/json/WrappedJson.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package serialization.json
3 |
4 | import com.avsystem.commons.misc.CaseMethods
5 | import com.avsystem.commons.serialization.GenCodec
6 |
7 | /**
8 | * Wrapper for raw JSON string
9 | *
10 | * It will be serialized as JSON value when used with [[com.avsystem.commons.serialization.Output]] supporting
11 | * [[RawJson]] marker.
12 | */
13 | final case class WrappedJson(value: String) extends AnyVal with CaseMethods
14 | object WrappedJson {
15 | implicit val codec: GenCodec[WrappedJson] = GenCodec.create(
16 | in => WrappedJson(in.readCustom(RawJson).getOrElse(in.readSimple().readString())),
17 | (out, v) => if (!out.writeCustom(RawJson, v.value)) out.writeSimple().writeString(v.value),
18 | )
19 | }
20 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/serialization/name.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package serialization
3 |
4 | import com.avsystem.commons.annotation.NotInheritedFromSealedTypes
5 |
6 | /**
7 | * Can be used on case class fields and classes in sealed hierarchy to instruct automatically derived `GenCodec`
8 | * to use particular name instead of just using parameter or class name.
9 | *
10 | * For example:
11 | * {{{
12 | * sealed trait Base
13 | * @name("STH")
14 | * case class Something(@name("dbname") paramname: Int) extends Base
15 | * object Base {
16 | * implicit codec = GenCodec.auto[Base]
17 | * }
18 | * }}}
19 | *
20 | * `GenCodec.write[Base](someOutput, Something(42))` would write an object
21 | * `{"STH": {"dbname": 42}}` instead of `{"Something": {"paramname": 42}}`.
22 | *
23 | * NOTE: `@name` annotation may be defined on any level of inheritance hierarchy.
24 | * For instance, if a case class field overrides a method of some base trait, the `@name` annotation may
25 | * be used on that method and will affect the case class field.
26 | */
27 | class name(val name: String) extends StaticAnnotation with NotInheritedFromSealedTypes
28 |
29 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/serialization/transientDefault.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package serialization
3 |
4 | /**
5 | * If some case class field has default value or [[whenAbsent]] annotation, you can use [[transientDefault]]
6 | * on this field to instruct an automatically derived `GenCodec` to not persist the value of that field if
7 | * it's equal to the default value.
8 | *
9 | * For example:
10 | * {{{
11 | * case class Something(str: String, @transientDefault int: Int = 42)
12 | * object Something {
13 | * implicit val codec = GenCodec.auto[Something]
14 | * }
15 | * }}}
16 | *
17 | * `GenCodec.write(someOutput, Something("lol", 10))` would yield object `{"str": "lol", "int": 10}` but
18 | * `GenCodec.write(someOutput, Something("lol", 42))` would yield object `{"str": "lol"}` because the value of `int`
19 | * is the same as the default value.
20 | *
21 | * NOTE: [[transientDefault]] also works for method parameters in RPC framework.
22 | */
23 | class transientDefault extends StaticAnnotation
24 |
--------------------------------------------------------------------------------
/core/src/main/scala/com/avsystem/commons/serialization/transparent.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package serialization
3 |
4 | /**
5 | * Can be used on case classes with exactly one field to instruct automatically generated `GenCodec` that the
6 | * class is a "transparent wrapper" and should be serialized to the same representation as the value of its sole
7 | * field.
8 | *
9 | * Whenever possible, it's better to use [[TransparentWrapperCompanion]] rather than this annotation.
10 | * [[TransparentWrapperCompanion]] will give you more typeclass instances for free (e.g. `GenKeyCodec` in addition to
11 | * just `GenCodec`) while this annotation requires special macro support from every typeclass.
12 | */
13 | class transparent extends StaticAnnotation
14 |
--------------------------------------------------------------------------------
/core/src/test/resources/SimpleApi.txt:
--------------------------------------------------------------------------------
1 | com.avsystem.commons.rpc.SimpleApi {
2 | def noParamLists: Int
3 | def noParams(): String
4 | def multiParamLists(int: Int)(str: String)(): Double
5 | def takesImplicits(int: Int)(implicit ord: Ordering[Int], moar: DummyImplicit): String
6 | }
--------------------------------------------------------------------------------
/core/src/test/scala/com/avsystem/commons/collection/MutableStackTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package collection
3 |
4 | import org.scalatest.funsuite.AnyFunSuite
5 |
6 | class MutableStackTest extends AnyFunSuite {
7 | test("push") {
8 | val stack = new MutableStack[String]
9 | stack.push("lol1")
10 | stack.push("lol2")
11 | stack.push("lol3")
12 | assert(stack.asList == List("lol3", "lol2", "lol1"))
13 | }
14 |
15 | test("pushAll") {
16 | val stack = new MutableStack[String]
17 | stack.pushAll(List("lol1", "lol2", "lol3"))
18 | assert(stack.asList == List("lol3", "lol2", "lol1"))
19 | }
20 |
21 | test("pop") {
22 | val stack = new MutableStack[String]
23 | assertThrows[NoSuchElementException](stack.pop())
24 | assert(stack.popOpt().isEmpty)
25 | assert(stack.popOption().isEmpty)
26 | stack.push("lol")
27 | assert(stack.pop() == "lol")
28 | assert(stack.isEmpty)
29 | stack.push("lol")
30 | assert(stack.popOpt() == Opt("lol"))
31 | assert(stack.isEmpty)
32 | stack.push("lol")
33 | assert(stack.popOption().contains("lol"))
34 | assert(stack.isEmpty)
35 | }
36 |
37 | test("top") {
38 | val stack = new MutableStack[String]
39 | assertThrows[NoSuchElementException](stack.top)
40 | assert(stack.topOpt.isEmpty)
41 | assert(stack.topOption.isEmpty)
42 | stack.push("lol")
43 | assert(stack.top == "lol")
44 | assert(stack.topOpt.contains("lol"))
45 | assert(stack.topOption.contains("lol"))
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/core/src/test/scala/com/avsystem/commons/macros/ApplyUnapplyTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package macros
3 |
4 | trait ApplierUnapplier[T, F] {
5 | def apply(f: F): T
6 | def unapply(t: T): F
7 | }
8 |
9 | object ApplyUnapplyTest {
10 | case class Empty()
11 | case class Single(int: Int)
12 | case class Multiple(int: Int, str: String)
13 | case class Gadt[T](t: T, list: List[T], cos: String)
14 | case class Generic[T](value: String)
15 |
16 | trait Custom[T]
17 | object Custom {
18 | def apply[T](t: T): Custom[T] = null
19 | def unapply[T](whatever: Custom[T]): Option[T] = None
20 | }
21 |
22 | def applierUnapplier[T, F]: ApplierUnapplier[T, F] = macro TestMacros.applierUnapplier[T, F]
23 |
24 | applierUnapplier[Empty, Unit]
25 | applierUnapplier[Single, Int]
26 | applierUnapplier[Multiple, (Int, String)]
27 | applierUnapplier[Gadt[Int], (Int, List[Int], String)]
28 | applierUnapplier[Custom[String], String]
29 | applierUnapplier[Generic[String], String]
30 | }
31 |
--------------------------------------------------------------------------------
/core/src/test/scala/com/avsystem/commons/misc/AnnotationOfTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package misc
3 |
4 | import com.avsystem.commons.annotation.AnnotationAggregate
5 | import org.scalatest.funsuite.AnyFunSuite
6 |
7 | case class genann[T](value: T) extends StaticAnnotation
8 | case class genagg[T](value: T) extends AnnotationAggregate {
9 | @genann(value)
10 | final def aggregated: List[StaticAnnotation] = reifyAggregated
11 | }
12 |
13 | @genagg(42)
14 | class Subject
15 |
16 | abstract class SelfAnnots(implicit val annots: SelfAnnotations[genann[_]])
17 | @genagg(42) @genann("fuu") class Klass extends SelfAnnots
18 | @genagg(42) @genann("fuu") object Objekt extends SelfAnnots
19 |
20 | class AnnotationOfTest extends AnyFunSuite {
21 | test("aggregate with generic") {
22 | assert(AnnotationOf.materialize[genann[Int], Subject].annot.value == 42)
23 | }
24 |
25 | test("self annotations") {
26 | assert(new Klass().annots.annots == List(genann(42), genann("fuu")))
27 | assert(Objekt.annots.annots == List(genann(42), genann("fuu")))
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/core/src/test/scala/com/avsystem/commons/misc/BoxingUnboxingTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons.misc
2 |
3 | import com.avsystem.commons.JInteger
4 |
5 | class BoxingUnboxingTest {
6 | val jint: JInteger = Opt(42).boxedOrNull
7 | }
8 |
--------------------------------------------------------------------------------
/core/src/test/scala/com/avsystem/commons/misc/CaseMethodsTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package misc
3 |
4 | import org.scalatest.funsuite.AnyFunSuite
5 |
6 | case class Whatever(str: String, int: Int) extends AbstractCase
7 |
8 | class CaseMethodsTest extends AnyFunSuite {
9 | val first = Whatever("lol", 42)
10 | val second = Whatever("lol", 42)
11 | val other = Whatever("lol", 41)
12 |
13 | test("equality") {
14 | assert(first canEqual second)
15 | assert(first == second)
16 | assert(first equals second)
17 | }
18 |
19 | test("inequality") {
20 | assert(first != other)
21 | assert(!(first equals other))
22 | }
23 |
24 | test("hashCode") {
25 | assert(first.hashCode == second.hashCode)
26 | assert(first.## == second.##)
27 | }
28 |
29 | test("toString") {
30 | assert(first.toString == "Whatever(lol,42)")
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/core/src/test/scala/com/avsystem/commons/misc/DelegationTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package misc
3 |
4 | import org.scalatest.funsuite.AnyFunSuite
5 |
6 | class DelegationTest extends AnyFunSuite {
7 | trait Destination[T] {
8 | val te: T
9 | def simple(omg: Int): String
10 | def meth[C[+X] >: Null <: Iterable[X]](map: Map[T, C[String]]): C[(T, String)]
11 | def multi(a: String)(b: String): String
12 | def vararg(values: String*): String
13 | }
14 |
15 | class Source {
16 | val te: Double = 3.14
17 | def simple(omg: Int): String = omg.toString
18 | def meth[C[+X] >: Null <: Iterable[X]](map: Map[Double, C[String]]): C[(Double, String)] = null
19 | def multi(a: String)(b: String): String = a + b
20 | def vararg(values: String*): String = values.mkString("")
21 | }
22 |
23 | test("simple test") {
24 | val source = new Source
25 | val destination = Delegation[Destination[Double]](source)
26 |
27 | assert(source.te == destination.te)
28 | assert(source.simple(42) == destination.simple(42))
29 | assert(source.meth[List](Map.empty) == destination.meth[List](Map.empty))
30 | assert(source.multi("4")("2") == destination.multi("4")("2"))
31 | assert(source.vararg("4", "2") == destination.vararg("4", "2"))
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/core/src/test/scala/com/avsystem/commons/misc/GraphUtilTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package misc
3 |
4 | import org.scalatest.funsuite.AnyFunSuite
5 |
6 | class GraphUtilTest extends AnyFunSuite {
7 | def edges(node: Int): List[Int] = node match {
8 | case 1 => List(2)
9 | case 2 => List(3, 4)
10 | case 3 => List(5, 6)
11 | case 4 => List(5)
12 | case 5 => List()
13 | case 6 => List(7)
14 | case 7 => List(2)
15 | }
16 |
17 | test("dfs") {
18 | val log = new StringBuilder
19 |
20 | GraphUtils.dfs(1 to 7)(
21 | edges,
22 | onEnter = (n, _) => log.append(s"onEnter $n\n"),
23 | onExit = (n, _) => log.append(s"onExit $n\n"),
24 | onRevisit = (n, _) => log.append(s"onRevisit $n\n"),
25 | onCycle = (n, _) => log.append(s"onCycle $n\n"),
26 | )
27 |
28 | val exp =
29 |
30 |
31 | assert(log.result() ==
32 | """onEnter 1
33 | |onEnter 2
34 | |onEnter 3
35 | |onEnter 5
36 | |onExit 5
37 | |onEnter 6
38 | |onEnter 7
39 | |onCycle 2
40 | |onExit 7
41 | |onExit 6
42 | |onExit 3
43 | |onEnter 4
44 | |onRevisit 5
45 | |onExit 4
46 | |onExit 2
47 | |onExit 1
48 | |""".stripMargin)
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/core/src/test/scala/com/avsystem/commons/misc/ImplicitNotFoundTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package misc
3 |
4 | import com.avsystem.commons.testutil.CompilationErrorAssertions
5 | import org.scalatest.funsuite.AnyFunSuite
6 |
7 | import scala.annotation.implicitNotFound
8 |
9 | sealed trait Stuff
10 | object Stuff {
11 | @implicitNotFound("no stuff available")
12 | implicit def inf: ImplicitNotFound[Stuff] = ImplicitNotFound()
13 | }
14 |
15 | sealed trait OtherStuff
16 | object OtherStuff {
17 | @implicitNotFound("no other stuff available because: #{forStuff}")
18 | implicit def inf(implicit forStuff: ImplicitNotFound[Stuff]): ImplicitNotFound[OtherStuff] = ImplicitNotFound()
19 | }
20 |
21 | class ImplicitNotFoundTest extends AnyFunSuite with CompilationErrorAssertions {
22 | test("simple") {
23 | assert(typeErrorFor("Implicits.infer[Stuff]") == "no stuff available")
24 | }
25 |
26 | test("with dependencies") {
27 | assert(typeErrorFor("Implicits.infer[OtherStuff]") == "no other stuff available because: no stuff available")
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/core/src/test/scala/com/avsystem/commons/misc/OptArgTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons.misc
2 |
3 | import com.avsystem.commons.SharedExtensions._
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.matchers.should.Matchers
6 |
7 | class OptArgTest extends AnyFunSuite with Matchers {
8 | test("nonempty") {
9 | val opt = OptArg(23)
10 | opt match {
11 | case OptArg(num) => assert(num == 23)
12 | }
13 | }
14 |
15 | test("empty") {
16 | val str: String = null
17 | val opt = OptArg(str)
18 | opt match {
19 | case OptArg.Empty =>
20 | }
21 | }
22 |
23 | test("null some") {
24 | intercept[NullPointerException](OptArg.some[String](null))
25 | }
26 |
27 | def takeMaybeString(str: OptArg[String] = OptArg.Empty): Opt[String] = str.toOpt
28 |
29 | test("argument passing") {
30 | takeMaybeString() shouldEqual Opt.Empty
31 | takeMaybeString("stringzor") shouldEqual "stringzor".opt
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/core/src/test/scala/com/avsystem/commons/misc/OptRefTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons.misc
2 |
3 | import com.avsystem.commons.JInteger
4 | import org.scalatest.funsuite.AnyFunSuite
5 |
6 | class OptRefTest extends AnyFunSuite {
7 | test("nonempty test") {
8 | val opt = OptRef("lol")
9 | opt match {
10 | case OptRef(str) => assert(str == "lol")
11 | }
12 | }
13 |
14 | test("empty test") {
15 | val str: String = null
16 | val opt = OptRef(str)
17 | opt match {
18 | case OptRef.Empty =>
19 | }
20 | }
21 |
22 | test("null some test") {
23 | intercept[NullPointerException](OptRef.some[String](null))
24 | }
25 |
26 | test("unboxing matching test") {
27 | val opt = OptRef[JInteger](42)
28 | opt match {
29 | case OptRef.Boxed(num) => assert(num == 42)
30 | }
31 | }
32 |
33 | test("zip") {
34 | assert(OptRef[JInteger](3).zip(OptRef[JInteger](2)) == OptRef((3, 2)))
35 | assert(OptRef.Empty.zip(OptRef[JInteger](2)) == OptRef.Empty)
36 | assert(OptRef[JInteger](3).zip(OptRef.Empty) == OptRef.Empty)
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/core/src/test/scala/com/avsystem/commons/misc/SealedEnumTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package misc
3 |
4 | import org.scalatest.funsuite.AnyFunSuite
5 |
6 | class SealedEnumTest extends AnyFunSuite {
7 | sealed abstract class SomeEnum(implicit val sourceInfo: SourceInfo) extends OrderedEnum
8 | object SomeEnum extends SealedEnumCompanion[SomeEnum] {
9 | case object First extends SomeEnum
10 | case object Second extends SomeEnum
11 | case object Third extends SomeEnum
12 | case object Fourth extends SomeEnum
13 |
14 | val values: List[SomeEnum] = caseObjects
15 | val classTags: List[ClassTag[_ <: SomeEnum]] = SealedUtils.instancesFor[ClassTag, SomeEnum]
16 | }
17 |
18 | test("case objects listing") {
19 | import SomeEnum._
20 | assert(values == List(First, Second, Third, Fourth))
21 | }
22 |
23 | test("typeclass instance listing") {
24 | import SomeEnum._
25 | assert(classTags.map(_.runtimeClass) == List(First.getClass, Second.getClass, Third.getClass, Fourth.getClass))
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/core/src/test/scala/com/avsystem/commons/misc/SourceInfoTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package misc
3 |
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatest.matchers.should.Matchers
6 |
7 | class SourceInfoTest extends AnyFunSuite with Matchers {
8 | val srcInfo = SourceInfo.here
9 |
10 | test("simple") {
11 | srcInfo should matchPattern {
12 | case SourceInfo(_, "SourceInfoTest.scala", 216, 8, 28,
13 | " val srcInfo = SourceInfo.here",
14 | List("srcInfo", "SourceInfoTest", "misc", "commons", "avsystem", "com")) =>
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/core/src/test/scala/com/avsystem/commons/misc/ValueOfTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package misc
3 |
4 | import org.scalatest.funsuite.AnyFunSuite
5 |
6 | object Obj {
7 | val x: String = "fuu"
8 |
9 | class Inner {
10 | val x: String = "fuu"
11 |
12 | def valueOfX: x.type = ValueOf[x.type]
13 | def valueOfThis: this.type = ValueOf[this.type]
14 | }
15 | }
16 |
17 | class ValueOfTest extends AnyFunSuite {
18 | test("object") {
19 | assert(ValueOf[Obj.type] == Obj)
20 | }
21 |
22 | test("static val") {
23 | assert(ValueOf[Obj.x.type] == Obj.x)
24 | }
25 |
26 | test("inner val of local") {
27 | val i = new Obj.Inner
28 | assert(ValueOf[i.x.type] == i.x)
29 | assert(i.valueOfX == i.x)
30 | }
31 |
32 | test("this") {
33 | val i = new Obj.Inner
34 | assert(i.valueOfThis == i)
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/core/src/test/scala/com/avsystem/commons/rpc/MangleOverloadsTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package rpc
3 |
4 | import com.avsystem.commons.meta.multi
5 | import org.scalatest.funsuite.AnyFunSuite
6 |
7 | trait MangleOverloadsRaw {
8 | @multi @mangleOverloads def select(@methodName name: String, @multi args: List[Int]): String
9 | }
10 | object MangleOverloadsRaw extends RawRpcCompanion[MangleOverloadsRaw]
11 |
12 | trait Overloads {
13 | def one: String
14 | def one(i: Int): String
15 | def two: String
16 | def two(i: Int): String
17 | def two(i: Int, j: Int): String
18 | }
19 | object Overloads {
20 | implicit val asRawReal: AsRawReal[MangleOverloadsRaw, Overloads] = MangleOverloadsRaw.materializeAsRawReal
21 | }
22 |
23 | class MangleOverloadsTest extends AnyFunSuite {
24 | test("overload mangling") {
25 | val raw = new MangleOverloadsRaw {
26 | def select(name: String, args: List[Int]): String = args.mkString(name + "(", ",", ")")
27 | }
28 | val real = MangleOverloadsRaw.asReal[Overloads](raw)
29 |
30 | assert(real.one == "one()")
31 | assert(real.one(42) == "one_1(42)")
32 | assert(real.two == "two()")
33 | assert(real.two(42) == "two_1(42)")
34 | assert(real.two(42, 13) == "two_2(42,13)")
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/core/src/test/scala/com/avsystem/commons/rpc/Tag.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package rpc
3 |
4 | import com.avsystem.commons.misc.{NamedEnum, NamedEnumCompanion}
5 | import com.avsystem.commons.serialization.GenCodec
6 |
7 | sealed abstract class Tag[T](implicit val codec: GenCodec[T]) extends NamedEnum with Product {
8 | def name: String = productPrefix
9 | }
10 | object Tag extends NamedEnumCompanion[Tag[_]] {
11 | def apply[T](implicit tag: Tag[T]): Tag[T] = tag
12 |
13 | implicit case object String extends Tag[String]
14 | implicit case object Int extends Tag[Int]
15 |
16 | val values: ISeq[Tag[_]] = caseObjects
17 |
18 | implicit def tagCodec[T]: GenCodec[Tag[T]] =
19 | codec.asInstanceOf[GenCodec[Tag[T]]]
20 | }
21 |
--------------------------------------------------------------------------------
/core/src/test/scala/com/avsystem/commons/serialization/AbstractCodecTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package serialization
3 |
4 | import org.scalactic.source.Position
5 | import org.scalatest.funsuite.AnyFunSuite
6 |
7 | trait AbstractCodecTest extends AnyFunSuite {
8 | def assertSameTypeValue[T](v1: T, v2: T)(implicit pos: Position): Unit = {
9 | assert(v1 == v2)
10 | assert(v1 == null || v1.getClass == v2.getClass)
11 | }
12 |
13 | type Raw
14 |
15 | def writeToOutput(write: Output => Unit): Raw
16 | def createInput(raw: Raw): Input
17 |
18 | def testWrite[T: GenCodec](value: T, expectedRepr: Raw)(implicit pos: Position): Unit = {
19 | val repr = writeToOutput(GenCodec.write[T](_, value))
20 | assert(repr == expectedRepr)
21 | }
22 |
23 | def testRead[T: GenCodec](repr: Raw, expected: T)(implicit pos: Position): Unit = {
24 | assert(expected == GenCodec.read[T](createInput(repr)))
25 | }
26 |
27 | def testRoundtrip[T: GenCodec](value: T)(implicit pos: Position): Unit = {
28 | val written: Raw = writeToOutput(GenCodec.write[T](_, value))
29 | val readBack = GenCodec.read[T](createInput(written))
30 | assertSameTypeValue(value, readBack)
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/core/src/test/scala/com/avsystem/commons/serialization/GenCodecErrorsTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package serialization
3 |
4 | import com.avsystem.commons.serialization.GenCodec.{ReadFailure, WriteFailure}
5 | import org.scalatest.funsuite.AnyFunSuite
6 |
7 | import scala.collection.immutable.ListMap
8 |
9 | case class Inner(int: Int)
10 | object Inner extends HasGenCodec[Inner]
11 |
12 | class Unwritable
13 | object Unwritable {
14 | implicit val codec: GenCodec[Unwritable] = GenCodec.create(
15 | _ => throw new ReadFailure("cannot"),
16 | (_, _) => throw new WriteFailure("cannot")
17 | )
18 | }
19 |
20 | sealed trait Base
21 | case class Outer(inner: Inner) extends Base
22 | case class Other(unwritable: Unwritable) extends Base
23 | object Base extends HasGenCodec[Base]
24 |
25 | class GenCodecErrorsTest extends AnyFunSuite {
26 | def causeChain(t: Throwable): List[Throwable] =
27 | if (t == null) Nil
28 | else t :: causeChain(t.getCause)
29 |
30 | test("deep reading failure test") {
31 | val failure = intercept[ReadFailure] {
32 | SimpleValueInput.read[Base](ListMap("Outer" -> ListMap("inner" -> ListMap("int" -> "NOT INT"))))
33 | }
34 | assert(causeChain(failure).size == 4)
35 | }
36 |
37 | test("deep writing failure test") {
38 | val failure = intercept[WriteFailure] {
39 | SimpleValueOutput.write[Base](Other(new Unwritable))
40 | }
41 | assert(causeChain(failure).size == 3)
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/core/src/test/scala/com/avsystem/commons/serialization/IsoInstantTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package serialization
3 |
4 | import com.avsystem.commons.serialization.GenCodec.ReadFailure
5 | import org.scalacheck.Gen
6 | import org.scalatest.funsuite.AnyFunSuite
7 | import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks
8 |
9 | class IsoInstantTest extends AnyFunSuite with ScalaCheckPropertyChecks {
10 | test("basic parsing") {
11 | assert(IsoInstant.parse("1970-01-01T00:00:00Z") == 0)
12 | assert(IsoInstant.parse("1970-01-01T00:00:00.000Z") == 0)
13 | intercept[ReadFailure](IsoInstant.parse("1970-01-01T00:00:00"))
14 | intercept[ReadFailure](IsoInstant.parse("1970-01-01"))
15 | intercept[ReadFailure](IsoInstant.parse("1970-13-01T00:00:00Z"))
16 | intercept[ReadFailure](IsoInstant.parse("1970-01-32T00:00:00Z"))
17 | intercept[ReadFailure](IsoInstant.parse("1970-01-01T25:00:00Z"))
18 | intercept[ReadFailure](IsoInstant.parse("1970-01-01T00:61:00Z"))
19 | intercept[ReadFailure](IsoInstant.parse("1970-01-01T00:00:61Z"))
20 | }
21 |
22 | test("basic formatting") {
23 | assert(IsoInstant.format(0) == "1970-01-01T00:00:00.000Z")
24 | assert(IsoInstant.format(1) == "1970-01-01T00:00:00.001Z")
25 | }
26 |
27 | test("roundtrip") {
28 | val genTstamp = Gen.choose[Long](-(1L << 50), 1L << 50)
29 | forAll(genTstamp) { (l: Long) =>
30 | assert(IsoInstant.parse(IsoInstant.format(l)) == l)
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/core/src/test/scala/com/avsystem/commons/serialization/JavaCodecs.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package serialization
3 |
4 | object JavaCodecs {
5 | implicit val buildablePojoCodec: GenCodec[BuildablePojo] =
6 | GenCodec.fromJavaBuilder(BuildablePojo.builder())(_.build())
7 | }
8 |
--------------------------------------------------------------------------------
/core/src/test/scala/com/avsystem/commons/serialization/StreamGenCodecTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package serialization
3 |
4 | import java.io.{ByteArrayInputStream, ByteArrayOutputStream, DataInputStream, DataOutputStream}
5 |
6 | class StreamGenCodecTest extends GenCodecRoundtripTest {
7 | type Raw = Array[Byte]
8 |
9 | def writeToOutput(write: Output => Unit): Array[Byte] = {
10 | val baos = new ByteArrayOutputStream
11 | write(new StreamOutput(new DataOutputStream(baos)))
12 | baos.toByteArray
13 | }
14 |
15 | def createInput(raw: Array[Byte]): Input =
16 | new StreamInput(new DataInputStream(new ByteArrayInputStream(raw)))
17 | }
18 |
--------------------------------------------------------------------------------
/core/src/test/scala/com/avsystem/commons/serialization/cbor/CborNonStringKeysTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package serialization.cbor
3 |
4 | import com.avsystem.commons.misc.Bytes
5 | import com.avsystem.commons.serialization.SizePolicy
6 | import org.scalatest.funsuite.AnyFunSuite
7 |
8 | import java.io.{ByteArrayOutputStream, DataOutputStream}
9 |
10 | class CborNonStringKeysTest extends AnyFunSuite {
11 | test("writing and reading CBOR map with non-string keys") {
12 | val baos = new ByteArrayOutputStream
13 | val output = new CborOutput(new DataOutputStream(baos), CborKeyCodec.Default, SizePolicy.Optional)
14 |
15 | val objout = output.writeObject()
16 | objout.declareSize(2)
17 | objout.writeKey().writeInt(42)
18 | objout.writeValue().writeString("42")
19 | objout.writeKey().writeBoolean(true)
20 | objout.writeValue().writeDouble(3.14)
21 | objout.finish()
22 |
23 | val bytes = baos.toByteArray
24 | assert(Bytes(bytes).toString == "A2182A623432F5FB40091EB851EB851F")
25 |
26 | val input = new CborInput(new CborReader(RawCbor(bytes)), CborKeyCodec.Default)
27 | val objin = input.readObject()
28 | assert(objin.nextKey().readInt() == 42)
29 | assert(objin.nextValue().readString() == "42")
30 | assert(objin.nextKey().readBoolean() == true)
31 | assert(objin.nextValue().readDouble() == 3.14)
32 | assert(!objin.hasNext)
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/core/src/test/scala/com/avsystem/commons/serialization/json/JsonGenCodecRoundtripTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package serialization.json
3 |
4 | import com.avsystem.commons.serialization.{GenCodecRoundtripTest, Input, Output}
5 |
6 | class JsonGenCodecRoundtripTest extends GenCodecRoundtripTest {
7 | type Raw = String
8 |
9 | def writeToOutput(write: Output => Unit): String = {
10 | val sb = new JStringBuilder
11 | write(new JsonStringOutput(sb))
12 | sb.toString
13 | }
14 |
15 | def createInput(raw: String): Input =
16 | new JsonStringInput(new JsonReader(raw))
17 | }
18 |
--------------------------------------------------------------------------------
/core/src/test/scala/com/avsystem/commons/serialization/json/WrappedJsonTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package serialization.json
3 |
4 | import com.avsystem.commons.serialization.{SimpleValueInput, SimpleValueOutput}
5 | import org.scalatest.funsuite.AnyFunSuite
6 | import org.scalatest.matchers.should.Matchers
7 |
8 | class WrappedJsonTest extends AnyFunSuite with Matchers {
9 |
10 | private val testJson = """{"a": 123, "b": 3.14}"""
11 |
12 | test("WrappedJson with JSON input/output") {
13 | assert(JsonStringOutput.write(WrappedJson(testJson)) == testJson)
14 | assert(JsonStringInput.read[WrappedJson](testJson) == WrappedJson(testJson))
15 | }
16 |
17 | // SimpleValueInput/Output does not support RawJson marker
18 | test("WrappedJson with plain input/output") {
19 | assert(SimpleValueOutput.write(WrappedJson(testJson)) == testJson)
20 | assert(SimpleValueInput.read[WrappedJson](testJson) == WrappedJson(testJson))
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/core/src/test/scala/com/avsystem/commons/testutil/CompilationErrorAssertions.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package testutil
3 |
4 | import com.avsystem.commons.macros.TestMacros
5 | import org.scalatest.Assertions
6 |
7 | trait CompilationErrorAssertions extends Assertions {
8 | def typeErrorFor(code: String): String = macro TestMacros.typeErrorImpl
9 | }
10 |
11 |
--------------------------------------------------------------------------------
/core/src/test/scala/com/avsystem/commons/testutil/FileBasedSuite.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package testutil
3 |
4 | import org.scalactic.source.Position
5 | import org.scalatest.funsuite.AnyFunSuite
6 |
7 | abstract class FileBasedSuite(testdir: String) extends AnyFunSuite {
8 | def updateTestFiles: Boolean =
9 | System.getProperty("updateTestFiles").opt.map(_.toBoolean).contains(true)
10 |
11 | def separator: String = "-----\n"
12 |
13 | def testFile(file: String)(process: String => String)(implicit position: Position): Unit = {
14 | val path = s"$testdir/$file"
15 | val contents = IO.readTestResource(path)
16 | val (input, expectedOutput) = contents.indexOf(separator) match {
17 | case -1 => (contents, "")
18 | case idx => (contents.take(idx), contents.drop(idx + separator.length))
19 | }
20 | val output = process(input)
21 | if (updateTestFiles && output != expectedOutput) {
22 | IO.writeTestResource(path, input + separator + output)
23 | }
24 | assert(output == expectedOutput)
25 | }
26 |
27 | def assertContents(actual: String, expectedFile: String): Unit = {
28 | val filePath = s"$testdir/$expectedFile"
29 | val expected = IO.readTestResource(filePath)
30 | if (updateTestFiles) {
31 | IO.writeTestResource(filePath, actual)
32 | }
33 | assert(actual == expected)
34 | }
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/hocon/src/main/scala/com/avsystem/commons/hocon/SizeInBytes.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package hocon
3 |
4 | /**
5 | * To read the size in bytes represented in [[https://github.com/lightbend/config/blob/master/HOCON.md#size-in-bytes-format HOCON format]],
6 | * use this type together with [[com.avsystem.commons.hocon.SizeInBytesMarker]] when deserializing data from HOCON.
7 | *
8 | * @see [[com.avsystem.commons.hocon.HoconGenCodecs.SizeInBytesCodec]]
9 | */
10 | final case class SizeInBytes(bytes: Long)
11 | object SizeInBytes {
12 | final val Zero = SizeInBytes(0)
13 | final val `1KiB` = SizeInBytes(1024L)
14 | final val `1MiB` = SizeInBytes(1024 * 1024L)
15 | }
16 |
--------------------------------------------------------------------------------
/hocon/src/test/scala/com/avsystem/commons/hocon/HLexerTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package hocon
3 |
4 | import com.avsystem.commons.testutil.FileBasedSuite
5 |
6 | class HLexerTest extends FileBasedSuite("/lexer") {
7 | test("simple") {
8 | testFile("hocon.txt") { contents =>
9 | val source = SourceFile(contents, "hocon.txt")
10 | val lexer = new HLexer(source)
11 | val tokens = lexer.tokenize()
12 | tokens.iterator.map { t =>
13 | s"<${t.pos.startLine + 1}:${t.pos.startColumn + 1}:$t:${t.pos.endLine + 1}:${t.pos.endColumn + 1}>"
14 | }.mkString("\n")
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/hocon/src/test/scala/com/avsystem/commons/hocon/HParserTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package hocon
3 |
4 | import com.avsystem.commons.testutil.FileBasedSuite
5 |
6 | class HParserTest extends FileBasedSuite("/parser") {
7 | test("simple") {
8 | testFile("hocon.txt") { contents =>
9 | val source = SourceFile(contents, "hocon.txt")
10 | val parser = new HParser(new HLexer(source).tokenize())
11 | HTree.repr(parser.parseSource())
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/hocon/src/test/scala/com/avsystem/commons/hocon/HoconGenCodecRoundtripTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package hocon
3 |
4 | import com.avsystem.commons.hocon.HoconInputTest.CustomCodecsClass
5 | import com.avsystem.commons.serialization.{GenCodecRoundtripTest, Input, Output}
6 | import com.typesafe.config.{ConfigFactory, ConfigValue}
7 |
8 | import java.time.{Duration, Period}
9 | import scala.concurrent.duration.*
10 |
11 | class HoconGenCodecRoundtripTest extends GenCodecRoundtripTest {
12 | type Raw = ConfigValue
13 |
14 | def writeToOutput(write: Output => Unit): ConfigValue = {
15 | var result: ConfigValue = null
16 | write(new HoconOutput(result = _))
17 | result
18 | }
19 |
20 | def createInput(raw: ConfigValue): Input =
21 | new HoconInput(raw)
22 |
23 | test("custom codes class") {
24 | val value = CustomCodecsClass(
25 | duration = 1.minute,
26 | jDuration = Duration.ofMinutes(5),
27 | fileSize = SizeInBytes.`1KiB`,
28 | embeddedConfig = ConfigFactory.parseMap(JMap("something" -> "abc")),
29 | period = Period.ofWeeks(2),
30 | clazz = classOf[HoconGenCodecRoundtripTest],
31 | clazzMap = Map(classOf[HoconGenCodecRoundtripTest] -> "abc"),
32 | )
33 | testRoundtrip(value)
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/jetty/src/main/scala/com/avsystem/commons/jetty/rpc/HttpException.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package jetty.rpc
3 |
4 | import java.io.IOException
5 |
6 | final class HttpException(val status: Int, val reason: String) extends IOException(s"HttpException($status): $reason")
7 |
--------------------------------------------------------------------------------
/macros/src/main/scala/com/avsystem/commons/macros/CompatMacroCommons.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package macros
3 |
4 | trait CompatMacroCommons { this: MacroCommons =>
5 | type NamedArgTree = c.universe.NamedArg
6 | final val NamedArgTree = c.universe.NamedArg
7 | }
8 |
--------------------------------------------------------------------------------
/macros/src/main/scala/com/avsystem/commons/macros/RecursiveImplicitMarker.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package macros
3 |
4 | import scala.annotation.compileTimeOnly
5 |
6 | object RecursiveImplicitMarker {
7 | @compileTimeOnly("this can only be used by derivation macros")
8 | implicit def mark[T]: T = throw new NotImplementedError
9 | }
10 |
--------------------------------------------------------------------------------
/macros/src/main/scala/com/avsystem/commons/macros/misc/LazyLoggingMacros.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package macros.misc
3 |
4 | import com.avsystem.commons.macros.MacroCommons
5 |
6 | import scala.reflect.macros.blackbox
7 |
8 | /**
9 | * Author: ghik
10 | * Created: 20/11/15.
11 | */
12 | class LazyLoggingMacros(val c: blackbox.Context) extends MacroCommons {
13 |
14 | import c.universe._
15 |
16 | val DelegationCls = tq"$MiscPkg.Delegation"
17 |
18 | def warningImpl(msg: Tree) =
19 | q"""
20 | if(${c.prefix}.rawLog.isWarningEnabled) {
21 | ${c.prefix}.rawLog.warning($msg)
22 | }
23 | """
24 |
25 | def infoImpl(msg: Tree) =
26 | q"""
27 | if(${c.prefix}.rawLog.isInfoEnabled) {
28 | ${c.prefix}.rawLog.info($msg)
29 | }
30 | """
31 |
32 | def debugImpl(msg: Tree) =
33 | q"""
34 | if(${c.prefix}.rawLog.isDebugEnabled) {
35 | ${c.prefix}.rawLog.debug($msg)
36 | }
37 | """
38 | }
39 |
--------------------------------------------------------------------------------
/macros/src/main/scala/com/avsystem/commons/macros/misc/SealedMacros.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package macros.misc
3 |
4 | import com.avsystem.commons.macros.AbstractMacroCommons
5 |
6 | import scala.reflect.macros.blackbox
7 |
8 | class SealedMacros(ctx: blackbox.Context) extends AbstractMacroCommons(ctx) {
9 |
10 | import c.universe._
11 |
12 | final lazy val OrderedEnumType: Type = staticType(tq"$MiscPkg.OrderedEnum")
13 |
14 | def caseObjectsFor[T: WeakTypeTag]: Tree = instrument {
15 | val tpe = weakTypeOf[T]
16 | knownSubtypes(tpe).map { subtypes =>
17 | val objects = subtypes.map(subTpe => singleValueFor(subTpe)
18 | .getOrElse(abort(s"All possible values of a SealedEnum must be objects but $subTpe is not")))
19 | val result = q"$ListObj(..$objects)"
20 | if (tpe <:< OrderedEnumType) q"$result.sorted" else result
21 | }.getOrElse(abort(s"$tpe is not a sealed trait or class"))
22 | }
23 |
24 | def instancesFor[TC: WeakTypeTag, T: WeakTypeTag]: Tree = instrument {
25 | val tpe = weakTypeOf[T]
26 | def instanceTpe(forTpe: Type): Type = weakTypeOf[TC] match {
27 | case TypeRef(pre, sym, Nil) => internal.typeRef(pre, sym, List(forTpe))
28 | case _ => abort(s"expected type constructor")
29 | }
30 | val subtypes = knownSubtypes(tpe).getOrElse(abort(s"$tpe is not a sealed hierarchy root"))
31 | q"${subtypes.map(st => q"""$ImplicitsObj.infer[${instanceTpe(st)}]("")""")}"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/macros/src/main/scala/com/avsystem/commons/macros/serialization/BsonRefMacros.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package macros.serialization
3 |
4 | import scala.reflect.macros.blackbox
5 |
6 | class BsonRefMacros(ctx: blackbox.Context) extends CodecMacroCommons(ctx) {
7 |
8 | import c.universe._
9 |
10 | final def MongoPkg: Tree = q"$CommonsPkg.mongo"
11 |
12 | def bsonRef[S: WeakTypeTag, T: WeakTypeTag](fun: Tree): Tree = {
13 | val sType = weakTypeOf[S]
14 | val tType = weakTypeOf[T]
15 | q"$MongoPkg.BsonRef($SerializationPkg.GenRef.create[$sType].ref[$tType]($fun))"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/mongo/js/src/main/scala/com/avsystem/commons/mongo/typed/MongoEntityCompanion.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.typed
3 |
4 | import com.avsystem.commons.meta.MacroInstances
5 | import com.avsystem.commons.serialization.{GenObjectCodec, HasGenObjectCodec}
6 |
7 | /**
8 | * Stub version of JVM's `MongoDataCompanion`. Makes it possible to cross-compile MongoDB entities for Scala.js.
9 | * Of course, they cannot be used _as_ MongoDB entities in Scala.js - they are simply seen as ADTs with
10 | * a `GenObjectCodec` instance.
11 | */
12 | abstract class MongoDataCompanion[T](
13 | implicit instances: MacroInstances[Unit, () => GenObjectCodec[T]]
14 | ) extends HasGenObjectCodec[T]
15 |
16 | /**
17 | * Stub version of JVM's `MongoEntityCompanion`. Makes it possible to cross-compile MongoDB entities for Scala.js.
18 | * Of course, they cannot be used _as_ MongoDB entities in Scala.js - they are simply seen as ADTs with
19 | * a `GenObjectCodec` instance.
20 | */
21 | abstract class MongoEntityCompanion[T <: BaseMongoEntity](
22 | implicit instances: MacroInstances[Unit, () => GenObjectCodec[T]]
23 | ) extends MongoDataCompanion[T]
24 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/BsonValueUtils.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo
3 |
4 | import org.bson.codecs.{BsonValueCodec, DecoderContext, EncoderContext}
5 | import org.bson.{BsonReader, BsonValue, BsonWriter}
6 |
7 | object BsonValueUtils {
8 | private val bsonValueCodec = new BsonValueCodec
9 | private val encoderContext = EncoderContext.builder.build
10 | private val decoderContext = DecoderContext.builder.build
11 |
12 | def encode(bw: BsonWriter, bv: BsonValue): Unit =
13 | bsonValueCodec.encode(bw, bv, encoderContext)
14 |
15 | def decode(br: BsonReader): BsonValue = {
16 | if (br.getCurrentBsonType eq null) {
17 | br.readBsonType()
18 | }
19 | bsonValueCodec.decode(br, decoderContext)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/Doc.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo
3 |
4 | import org.bson.{BsonDocument, BsonValue}
5 |
6 | /**
7 | * @author MKej
8 | */
9 | class Doc(private val doc: BsonDocument) extends AnyVal {
10 | def get[A, BSON <: BsonValue](key: DocKey[A, BSON]): Option[A] =
11 | Option(doc.get(key.key).asInstanceOf[BSON]).map(key.codec.fromBson)
12 |
13 | def getOpt[A, BSON <: BsonValue](key: DocKey[A, BSON]): Opt[A] =
14 | Opt(doc.get(key.key).asInstanceOf[BSON]).map(key.codec.fromBson)
15 |
16 | def require[A](key: DocKey[A, _ <: BsonValue]): A = getOpt(key).get
17 |
18 | def put[A](key: DocKey[A, _ <: BsonValue], value: A): Doc = {
19 | doc.put(key.key, key.codec.toBson(value))
20 | this
21 | }
22 |
23 | def putOpt[A](key: DocKey[A, _ <: BsonValue], optValue: Option[A]): Doc = optValue.fold(this)(put(key, _))
24 |
25 | def putOpt[A](key: DocKey[A, _ <: BsonValue], optValue: Opt[A]): Doc = optValue.fold(this)(put(key, _))
26 |
27 | def toBson: BsonDocument = doc
28 | }
29 |
30 | object Doc {
31 | def apply(): Doc = new Doc(new BsonDocument())
32 | def apply[A](key: DocKey[A, _ <: BsonValue], value: A): Doc = apply().put(key, value)
33 | }
34 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/DocKey.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo
3 |
4 | import com.avsystem.commons.mongo.core.ops.{DocKeyFiltering, DocKeySorting}
5 | import org.bson.{BsonDocument, BsonValue}
6 |
7 | /**
8 | * @author MKej
9 | */
10 | case class DocKey[A, BSON <: BsonValue](key: String, codec: BsonCodec[A, BSON]) {
11 | def ++[B, BBSON <: BsonValue](other: DocKey[B, BBSON])(implicit ev: BSON <:< BsonDocument): DocKey[B, BBSON] =
12 | new DocKey(key + "." + other.key, other.codec)
13 | }
14 | object DocKey {
15 | implicit def docKeySorting[T](docKey: DocKey[T, _ <: BsonValue]): DocKeySorting[T] = new DocKeySorting(docKey)
16 | implicit def docKeyFiltering[T](docKey: DocKey[T, _ <: BsonValue]): DocKeyFiltering[T] = new DocKeyFiltering(docKey)
17 | }
18 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/DocumentCodec.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo
3 |
4 | import org.bson.BsonDocument
5 |
6 | /**
7 | * @author MKej
8 | */
9 | trait DocumentCodec[T] {
10 | def toDocument(t: T): Doc
11 | def fromDocument(doc: Doc): T
12 |
13 | lazy val bsonCodec: BsonCodec[T, BsonDocument] = BsonCodec.doc.map(fromDocument, toDocument)
14 | }
15 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/DuplicateKey.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo
3 |
4 | import com.mongodb.{DuplicateKeyException, ErrorCategory, MongoException}
5 |
6 | /**
7 | * @author MKej
8 | */
9 | object DuplicateKey {
10 | def unapply(t: Throwable) = t match {
11 | case e: DuplicateKeyException => Some(e)
12 | case e: MongoException if ErrorCategory.fromErrorCode(e.getCode) == ErrorCategory.DUPLICATE_KEY =>
13 | Some(e)
14 | case _ => None
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/GenCodecBasedBsonCodec.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo
3 |
4 | import com.avsystem.commons.serialization.GenCodec
5 | import org.bson.codecs.{Codec, DecoderContext, EncoderContext}
6 | import org.bson.{BsonReader, BsonWriter}
7 |
8 | class GenCodecBasedBsonCodec[T](legacyOptionEncoding: Boolean)(
9 | implicit ct: ClassTag[T], genCodec: GenCodec[T]
10 | ) extends Codec[T] {
11 | override def getEncoderClass: Class[T] = ct.runtimeClass.asInstanceOf[Class[T]]
12 |
13 | override def decode(reader: BsonReader, decoderContext: DecoderContext): T =
14 | genCodec.read(new BsonReaderInput(reader, legacyOptionEncoding))
15 |
16 | override def encode(writer: BsonWriter, value: T, encoderContext: EncoderContext): Unit =
17 | genCodec.write(new BsonWriterOutput(writer, legacyOptionEncoding), value)
18 | }
19 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/MongoCodec.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo
3 |
4 | import org.bson.codecs.configuration.CodecRegistry
5 | import org.bson.codecs.{Codec, DecoderContext, EncoderContext}
6 | import org.bson.{BsonReader, BsonValue, BsonWriter}
7 |
8 | /**
9 | * @author MKej
10 | */
11 | class MongoCodec[A, BSON <: BsonValue](bsonCodec: BsonCodec[A, BSON], registry: CodecRegistry)
12 | (implicit cta: ClassTag[A], ctbson: ClassTag[BSON])
13 | extends Codec[A] {
14 |
15 | val aClass = cta.runtimeClass.asInstanceOf[Class[A]]
16 | val bsonClass = ctbson.runtimeClass.asInstanceOf[Class[BSON]]
17 |
18 | def decode(reader: BsonReader, decoderContext: DecoderContext) =
19 | bsonCodec.fromBson(registry.get(bsonClass).decode(reader, decoderContext))
20 |
21 | def encode(writer: BsonWriter, value: A, encoderContext: EncoderContext) =
22 | registry.get(bsonClass).encode(writer, bsonCodec.toBson(value), encoderContext)
23 |
24 | def getEncoderClass = aClass
25 | }
26 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/Sort.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo
3 |
4 | import com.mongodb.client.model.{Sorts => S}
5 | import org.bson.conversions.Bson
6 |
7 | /**
8 | * @author MKej
9 | */
10 | object Sort {
11 | def ascending(keys: DocKey[_, _]*): Bson = S.ascending(keys.map(_.key).asJava)
12 | def descending(keys: DocKey[_, _]*): Bson = S.descending(keys.map(_.key).asJava)
13 | }
14 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/Update.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo
3 |
4 | import com.mongodb.client.model.{Updates => U}
5 | import org.bson.conversions.Bson
6 |
7 | /**
8 | * @author MKej
9 | */
10 | object Update {
11 | def combine(updates: Bson*): Bson = U.combine(updates.asJava)
12 |
13 | def set[A](key: DocKey[A, _], value: A): Bson = U.set(key.key, key.codec.toBson(value))
14 | def unset(key: DocKey[_, _]): Bson = U.unset(key.key)
15 |
16 | def max[A](key: DocKey[A, _], value: A): Bson = U.max(key.key, key.codec.toBson(value))
17 | }
18 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/core/GenCodecProvider.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.core
3 |
4 | import com.avsystem.commons.mongo.GenCodecBasedBsonCodec
5 | import com.avsystem.commons.serialization.GenCodec
6 | import org.bson.codecs.Codec
7 | import org.bson.codecs.configuration.{CodecProvider, CodecRegistry}
8 |
9 | class GenCodecProvider[T: GenCodec](legacyOptionEncoding: Boolean)(implicit ct: ClassTag[T]) extends CodecProvider {
10 | private val mongoCodec = new GenCodecBasedBsonCodec[T](legacyOptionEncoding)
11 | private val runtimeClass = ct.runtimeClass
12 |
13 | override def get[X](clazz: Class[X], registry: CodecRegistry): Codec[X] = {
14 | if (runtimeClass.isAssignableFrom(clazz)) mongoCodec.asInstanceOf[Codec[X]]
15 | else null
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/core/GenCodecRegistry.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.core
3 |
4 | import com.avsystem.commons.serialization.GenCodec
5 | import org.bson.codecs.configuration.{CodecRegistries, CodecRegistry}
6 |
7 | object GenCodecRegistry {
8 | final val LegacyOptionEncoding: Boolean =
9 | System.getProperty("commons.mongo.legacyOptionEncoding").opt.fold(false)(_.toBoolean)
10 |
11 | def create[T: ClassTag : GenCodec](
12 | baseRegistry: CodecRegistry,
13 | legacyOptionEncoding: Boolean = LegacyOptionEncoding
14 | ): CodecRegistry = {
15 | val genProvider = new GenCodecProvider[T](legacyOptionEncoding)
16 | val genRegistry = CodecRegistries.fromProviders(genProvider)
17 | CodecRegistries.fromRegistries(genRegistry, baseRegistry)
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/core/ops/BaseIterableFiltering.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.core.ops
3 |
4 | import com.mongodb.client.model.Filters
5 | import org.bson.conversions.Bson
6 |
7 | trait BaseIterableFiltering[E, C[T] <: Iterable[T]] extends Any with BaseFiltering[C[E]] with KeyElementHandling[E] {
8 | def contains(e: E): Bson = useE(e)(Filters.eq(_, _))
9 | def all(es: E*): Bson = useEs(es)(Filters.all(_, _))
10 | def elemMatch(filter: Bson): Bson = Filters.elemMatch(key, filter)
11 | def size(size: Int): Bson = Filters.size(key, size)
12 | }
13 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/core/ops/BaseIterableUpdating.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.core.ops
3 |
4 | import com.mongodb.client.model.{PushOptions, Updates}
5 | import org.bson.conversions.Bson
6 |
7 | trait BaseIterableUpdating[E, C[T] <: Iterable[T]] extends Any with BaseUpdating[C[E]] with KeyElementHandling[E] {
8 | def addToSet(e: E): Bson = useE(e)(Updates.addToSet)
9 | def addEachToSet(es: E*): Bson = useEs(es)(Updates.addEachToSet)
10 |
11 | def push(e: E): Bson = useE(e)(Updates.push)
12 | //noinspection ConvertibleToMethodValue
13 | def pushEach(es: E*): Bson = useEs(es)(Updates.pushEach(_, _))
14 | def pushEachOptions(pushOptions: PushOptions, es: E*): Bson = useEs(es)(Updates.pushEach(_, _, pushOptions))
15 |
16 | def pull(e: E): Bson = useE(e)(Updates.pull)
17 | def pullAll(es: E*): Bson = useEs(es)(Updates.pullAll)
18 |
19 | def popFirst(): Bson = Updates.popFirst(key)
20 | def popLast(): Bson = Updates.popLast(key)
21 | }
22 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/core/ops/BaseSorting.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.core.ops
3 |
4 | import com.mongodb.client.model.Sorts
5 | import org.bson.conversions.Bson
6 |
7 | trait BaseSorting extends Any with KeyHandling {
8 | def ascending: Bson = Sorts.ascending(key)
9 | def descending: Bson = Sorts.descending(key)
10 | def metaTextScore: Bson = Sorts.metaTextScore(key)
11 | }
12 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/core/ops/BaseUpdating.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.core.ops
3 |
4 | import com.mongodb.client.model.Updates
5 | import org.bson.conversions.Bson
6 |
7 | trait BaseUpdating[T] extends Any with KeyValueHandling[T] {
8 | def set(t: T): Bson = use(t)(Updates.set)
9 | def setOnInsert(t: T): Bson = use(t)(Updates.setOnInsert(_, _))
10 | def unset(): Bson = Updates.unset(key)
11 |
12 | def rename(newName: String): Bson = Updates.rename(key, newName)
13 |
14 | def inc(number: JNumber): Bson = Updates.inc(key, number)
15 | def mul(number: JNumber): Bson = Updates.mul(key, number)
16 | def min(t: T): Bson = use(t)(Updates.min)
17 | def max(t: T): Bson = use(t)(Updates.max)
18 |
19 | def currentDate(): Bson = Updates.currentDate(key)
20 | def currentTimestamp(): Bson = Updates.currentTimestamp(key)
21 |
22 | def bitwiseAnd(int: Int): Bson = Updates.bitwiseAnd(key, int)
23 | def bitwiseAnd(long: Long): Bson = Updates.bitwiseAnd(key, long)
24 | def bitwiseOr(int: Int): Bson = Updates.bitwiseOr(key, int)
25 | def bitwiseOr(long: Long): Bson = Updates.bitwiseOr(key, long)
26 | def bitwiseXor(int: Int): Bson = Updates.bitwiseXor(key, int)
27 | def bitwiseXor(long: Long): Bson = Updates.bitwiseXor(key, long)
28 | }
29 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/core/ops/BsonFiltering.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.core.ops
3 |
4 | import com.mongodb.client.model.Filters
5 | import org.bson.conversions.Bson
6 |
7 | final class BsonFiltering(private val bson: Bson) extends AnyVal {
8 | def and(others: Bson*): Bson = Filters.and((bson +: others).asJava)
9 | def or(others: Bson*): Bson = Filters.or((bson +: others).asJava)
10 | def nor(others: Bson*): Bson = Filters.nor((bson +: others).asJava)
11 | def not: Bson = Filters.not(bson)
12 | }
13 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/core/ops/BsonRefFiltering.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.core.ops
3 |
4 | import com.avsystem.commons.mongo.BsonRef
5 |
6 | final class BsonRefFiltering[T](protected val bsonRef: BsonRef[_, T])
7 | extends AnyVal
8 | with BaseFiltering[T]
9 | with BsonRefKeyValueHandling[T]
10 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/core/ops/BsonRefIterableFiltering.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.core.ops
3 |
4 | import com.avsystem.commons.mongo.BsonRef
5 | import com.avsystem.commons.serialization.GenCodec
6 |
7 | final class BsonRefIterableFiltering[E, C[T] <: Iterable[T]](protected val bsonRef: BsonRef[_, C[E]])(
8 | implicit protected val elementCodec: GenCodec[E]) extends BaseIterableFiltering[E, C]
9 | with BsonRefKeyValueHandling[C[E]]
10 | with BsonRefKeyElementHandling[E, C]
11 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/core/ops/BsonRefIterableUpdating.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.core.ops
3 |
4 | import com.avsystem.commons.mongo.BsonRef
5 | import com.avsystem.commons.serialization.GenCodec
6 |
7 | final class BsonRefIterableUpdating[E, C[T] <: Iterable[T]](protected val bsonRef: BsonRef[_, C[E]])(
8 | implicit protected val elementCodec: GenCodec[E]) extends BaseIterableUpdating[E, C]
9 | with BsonRefKeyValueHandling[C[E]]
10 | with BsonRefKeyElementHandling[E, C]
11 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/core/ops/BsonRefKeyElementHandling.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.core.ops
3 |
4 | import com.avsystem.commons.mongo.BsonValueOutput
5 | import com.avsystem.commons.serialization.GenCodec
6 | import org.bson.BsonValue
7 |
8 | trait BsonRefKeyElementHandling[E, C[T] <: Iterable[T]] extends KeyElementHandling[E] with BsonRefKeyHandling[C[E]] {
9 | protected implicit def elementCodec: GenCodec[E]
10 |
11 | override protected def encodeElement(e: E): BsonValue = BsonValueOutput.write(e)
12 | }
13 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/core/ops/BsonRefKeyHandling.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.core.ops
3 |
4 | import com.avsystem.commons.mongo.BsonRef
5 |
6 | trait BsonRefKeyHandling[T] extends Any with KeyHandling {
7 | protected def bsonRef: BsonRef[_, T]
8 |
9 | override protected def key: String = bsonRef.path
10 | }
11 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/core/ops/BsonRefKeyValueHandling.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.core.ops
3 |
4 | import com.avsystem.commons.mongo.BsonValueOutput
5 | import org.bson.BsonValue
6 |
7 | trait BsonRefKeyValueHandling[T] extends Any with KeyValueHandling[T] with BsonRefKeyHandling[T] {
8 | override protected def encode(t: T): BsonValue = BsonValueOutput.write(t)(bsonRef.codec)
9 | }
10 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/core/ops/BsonRefSorting.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.core.ops
3 |
4 | import com.avsystem.commons.mongo.BsonRef
5 |
6 | class BsonRefSorting[T](val bsonRef: BsonRef[_, T])
7 | extends AnyVal
8 | with BaseSorting
9 | with BsonRefKeyHandling[T]
10 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/core/ops/BsonRefUpdating.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.core.ops
3 |
4 | import com.avsystem.commons.mongo.BsonRef
5 |
6 | class BsonRefUpdating[T](val bsonRef: BsonRef[_, T])
7 | extends AnyVal
8 | with BaseUpdating[T]
9 | with BsonRefKeyValueHandling[T]
10 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/core/ops/BsonUpdating.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.core.ops
3 |
4 | import com.mongodb.client.model.Updates
5 | import org.bson.conversions.Bson
6 |
7 | class BsonUpdating(val bson: Bson) extends AnyVal {
8 | def combine(others: Bson*): Bson = Updates.combine((bson +: others).to(JList))
9 | }
10 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/core/ops/DocKeyFiltering.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.core.ops
3 |
4 | import com.avsystem.commons.mongo.DocKey
5 | import org.bson.BsonValue
6 |
7 | class DocKeyFiltering[T](val docKey: DocKey[T, _ <: BsonValue])
8 | extends AnyVal
9 | with BaseFiltering[T]
10 | with DocKeyKeyValueHandling[T]
11 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/core/ops/DocKeyKeyHandling.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.core.ops
3 |
4 | import com.avsystem.commons.mongo.DocKey
5 | import org.bson.BsonValue
6 |
7 | trait DocKeyKeyHandling[T] extends Any with KeyHandling {
8 | protected def docKey: DocKey[T, _ <: BsonValue]
9 |
10 | override protected def key: String = docKey.key
11 | }
12 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/core/ops/DocKeyKeyValueHandling.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.core.ops
3 |
4 | import org.bson.BsonValue
5 |
6 | trait DocKeyKeyValueHandling[T] extends Any with KeyValueHandling[T] with DocKeyKeyHandling[T] {
7 | override protected def encode(t: T): BsonValue = docKey.codec.toBson(t)
8 | }
9 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/core/ops/DocKeySorting.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.core.ops
3 |
4 | import com.avsystem.commons.mongo.DocKey
5 | import org.bson.BsonValue
6 |
7 | class DocKeySorting[T](val docKey: DocKey[T, _ <: BsonValue])
8 | extends AnyVal
9 | with BaseSorting
10 | with DocKeyKeyValueHandling[T]
11 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/core/ops/Filtering.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.core.ops
3 |
4 | import com.mongodb.client.model.Filters
5 | import org.bson.conversions.Bson
6 |
7 | object Filtering {
8 | implicit def bsonFiltering(bson: Bson): BsonFiltering = new BsonFiltering(bson)
9 |
10 | def and(filters: Bson*): Bson = Filters.and(filters.asJava)
11 | def or(filters: Bson*): Bson = Filters.or(filters.asJava)
12 | def nor(filters: Bson*): Bson = Filters.nor(filters.asJava)
13 | def not(filter: Bson): Bson = Filters.not(filter)
14 | }
15 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/core/ops/KeyElementHandling.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.core.ops
3 |
4 | import org.bson.BsonValue
5 | import org.bson.conversions.Bson
6 |
7 | trait KeyElementHandling[E] extends Any with KeyHandling {
8 | protected def encodeElement(e: E): BsonValue
9 |
10 | protected def useE(e: E)(f: (String, BsonValue) => Bson): Bson = {
11 | f(key, encodeElement(e))
12 | }
13 |
14 | protected def useEs(es: Seq[E])(f: (String, JList[BsonValue]) => Bson): Bson = {
15 | f(key, es.iterator.map(encodeElement).to(JList))
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/core/ops/KeyGetter.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.core.ops
3 |
4 | import com.avsystem.commons.mongo.{BsonRef, DocKey}
5 |
6 | trait KeyGetter[-T] {
7 | def keyOf(t: T): String
8 | }
9 |
10 | object KeyGetter {
11 | implicit object bsonRefKeyGetter extends KeyGetter[BsonRef[_, _]] {
12 | override def keyOf(t: BsonRef[_, _]): String = t.path
13 | }
14 |
15 | implicit object docKeyKeyGetter extends KeyGetter[DocKey[_, _]] {
16 | override def keyOf(t: DocKey[_, _]): String = t.key
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/core/ops/KeyHandling.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.core.ops
3 |
4 | trait KeyHandling extends Any {
5 | protected def key: String
6 | }
7 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/core/ops/KeyValueHandling.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.core.ops
3 |
4 | import org.bson.BsonValue
5 | import org.bson.conversions.Bson
6 |
7 | trait KeyValueHandling[T] extends Any with KeyHandling {
8 | protected def encode(t: T): BsonValue
9 |
10 | protected def use(t: T)(f: (String, BsonValue) => Bson): Bson = {
11 | f(key, encode(t))
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/core/ops/Sorting.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.core.ops
3 |
4 | import com.mongodb.client.model.Sorts
5 | import org.bson.conversions.Bson
6 |
7 | object Sorting {
8 | def ascending[K](keys: K*)(implicit kg: KeyGetter[K]): Bson = Sorts.ascending(keys.map(kg.keyOf).asJava)
9 | def descending[K](keys: K*)(implicit kg: KeyGetter[K]): Bson = Sorts.descending(keys.map(kg.keyOf).asJava)
10 |
11 | def orderBy(sorts: Bson*): Bson = Sorts.orderBy(sorts.asJava)
12 | }
13 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/core/ops/Updating.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.core.ops
3 |
4 | import com.mongodb.client.model.Updates
5 | import org.bson.conversions.Bson
6 |
7 | object Updating {
8 | implicit def bsonUpdating(bson: Bson): BsonUpdating = new BsonUpdating(bson)
9 |
10 | def combine(updates: Bson*): Bson = Updates.combine(updates.to(JList))
11 | }
12 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/reactive/GenCodecCollection.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.reactive
3 |
4 | import com.avsystem.commons.mongo.core.GenCodecRegistry
5 | import com.avsystem.commons.serialization.GenCodec
6 | import com.mongodb.reactivestreams.client.{MongoCollection, MongoDatabase}
7 |
8 | object GenCodecCollection {
9 | def create[T: GenCodec : ClassTag](db: MongoDatabase, name: String,
10 | legacyOptionEncoding: Boolean = GenCodecRegistry.LegacyOptionEncoding): MongoCollection[T] = {
11 | val newRegistry = GenCodecRegistry.create[T](db.getCodecRegistry, legacyOptionEncoding)
12 | db.withCodecRegistry(newRegistry).getCollection(name, classTag[T].runtimeClass.asInstanceOf[Class[T]])
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/scala/GenCodecCollection.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.scala
3 |
4 | import com.avsystem.commons.mongo.core.GenCodecRegistry
5 | import com.avsystem.commons.serialization.GenCodec
6 | import org.mongodb.scala.{MongoCollection, MongoDatabase}
7 |
8 | object GenCodecCollection {
9 |
10 | def create[T: ClassTag : GenCodec](db: MongoDatabase, name: String,
11 | legacyOptionEncoding: Boolean = GenCodecRegistry.LegacyOptionEncoding): MongoCollection[T] = {
12 |
13 | val newRegistry = GenCodecRegistry.create[T](db.codecRegistry, legacyOptionEncoding)
14 | db.withCodecRegistry(newRegistry).getCollection(name)
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/scala/MongoScalaObservableExtensions.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.scala
3 |
4 | trait MongoScalaObservableExtensions {
5 | import MongoScalaObservableExtensions._
6 |
7 | implicit def mongoObservableOps[T](obs: org.mongodb.scala.Observable[T]): MongoObservableOps[T] = new MongoObservableOps[T](obs)
8 | }
9 |
10 | object MongoScalaObservableExtensions extends MongoScalaObservableExtensions {
11 |
12 | final class MongoObservableOps[T](private val obs: org.mongodb.scala.Observable[T]) extends AnyVal {
13 | def asMonix: monix.reactive.Observable[T] = monix.reactive.Observable.fromReactivePublisher(obs)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/sync/GenCodecCollection.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.sync
3 |
4 | import com.avsystem.commons.mongo.core.GenCodecRegistry
5 | import com.avsystem.commons.serialization.GenCodec
6 | import com.mongodb.client.{MongoCollection, MongoDatabase}
7 |
8 | object GenCodecCollection {
9 | def create[T: GenCodec : ClassTag](db: MongoDatabase, name: String,
10 | legacyOptionEncoding: Boolean = GenCodecRegistry.LegacyOptionEncoding): MongoCollection[T] = {
11 | val newRegistry = GenCodecRegistry.create[T](db.getCodecRegistry, legacyOptionEncoding)
12 | db.withCodecRegistry(newRegistry).getCollection(name, classTag[T].runtimeClass.asInstanceOf[Class[T]])
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/typed/Bson.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.typed
3 |
4 | import org.bson._
5 |
6 | object Bson {
7 | final val And = "$and"
8 | final val Or = "$or"
9 | final val Nor = "$nor"
10 |
11 | def document(key: String, value: BsonValue): BsonDocument =
12 | new BsonDocument(key, value)
13 |
14 | def document(keyValues: (String, BsonValue)*): BsonDocument =
15 | document(keyValues)
16 |
17 | def document(keyValues: Iterable[(String, BsonValue)]): BsonDocument =
18 | document(keyValues.iterator)
19 |
20 | def document(keyValues: Iterator[(String, BsonValue)]): BsonDocument = {
21 | val doc = new BsonDocument
22 | keyValues.foreach { case (key, value) =>
23 | doc.append(key, value)
24 | }
25 | doc
26 | }
27 |
28 | def array(values: BsonValue*): BsonArray =
29 | array(values)
30 |
31 | def array(values: Iterable[BsonValue]): BsonArray =
32 | array(values.iterator)
33 |
34 | def array(values: Iterator[BsonValue]): BsonArray = {
35 | val array = new BsonArray
36 | values.foreach(array.add)
37 | array
38 | }
39 |
40 | def boolean(value: Boolean): BsonBoolean =
41 | new BsonBoolean(value)
42 |
43 | def int(value: Int): BsonInt32 =
44 | new BsonInt32(value)
45 |
46 | def long(value: Long): BsonInt64 =
47 | new BsonInt64(value)
48 |
49 | def string(value: String): BsonString =
50 | new BsonString(value)
51 | }
52 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/typed/MongoTypedKey.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.typed
3 |
4 | import com.avsystem.commons.misc.TypedKey
5 | import com.avsystem.commons.misc.TypedMap.GenCodecMapping
6 | import com.avsystem.commons.serialization.GenCodec
7 |
8 | trait MongoFormatMapping[K[_]] extends GenCodecMapping[K] {
9 | def valueFormat[T](key: K[T]): MongoFormat[T]
10 | override def valueCodec[T](key: K[T]): GenCodec[T] = valueFormat(key).codec
11 | }
12 |
13 | trait MongoTypedKey[T] extends TypedKey[T] {
14 | def valueFormat: MongoFormat[T]
15 | override def valueCodec: GenCodec[T] = valueFormat.codec
16 | }
17 | object MongoTypedKey {
18 | implicit def mongoFormatMapping[K[X] <: MongoTypedKey[X]]: MongoFormatMapping[K] =
19 | new MongoFormatMapping[K] {
20 | override def valueFormat[T](key: K[T]): MongoFormat[T] = key.valueFormat
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/typed/ObjectIdWrapperCompanion.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.typed
3 |
4 | import com.avsystem.commons.mongo.BsonGenCodecs
5 | import com.avsystem.commons.serialization.{GenCodec, TransparentWrapperCompanion}
6 | import org.bson.types.ObjectId
7 |
8 | /**
9 | * Base class for companion objects of wrappers ("newtypes") over BSON [[ObjectId]] - the default type of
10 | * MongoDB document ID.
11 | * These wrappers usually serve to make the code more readable and typesafe. Because they are "transparent",
12 | * wrapping is not visible in any way after serialization.
13 | *
14 | * Example:
15 | * {{{
16 | * import org.bson.types.ObjectId
17 | *
18 | * case class MyEntityId(id: ObjectId) extends AnyVal
19 | * object MyEntityId extends ObjectIdWrapperCompanion[MyEntityId]
20 | * }}}
21 | */
22 | abstract class ObjectIdWrapperCompanion[ID] extends TransparentWrapperCompanion[ObjectId, ID] {
23 | /**
24 | * Generates new random, unique ID value.
25 | */
26 | def get(): ID = wrap(ObjectId.get())
27 |
28 | implicit val codec: GenCodec[ID] = GenCodec.fromTransparentWrapping(this, BsonGenCodecs.objectIdCodec)
29 | }
30 |
--------------------------------------------------------------------------------
/mongo/jvm/src/main/scala/com/avsystem/commons/mongo/typed/TypedMongoUtils.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.typed
3 |
4 | import com.avsystem.commons.macros.misc.MiscMacros
5 | import monix.eval.Task
6 | import monix.reactive.Observable
7 | import org.reactivestreams.Publisher
8 |
9 | trait TypedMongoUtils {
10 | import com.avsystem.commons.mongo.reactive.ReactiveMongoExtensions._
11 |
12 | protected final def empty(publisher: Publisher[Void]): Task[Unit] = publisher.completedL
13 | protected final def single[T](publisher: Publisher[T]): Task[T] = publisher.headL
14 | // handles both an empty Publisher and and a single null item
15 | protected final def singleOpt[T](publisher: Publisher[T]): Task[Option[T]] = publisher.headOptionL
16 | protected final def multi[T](publisher: Publisher[T]): Observable[T] = publisher.asMonix
17 |
18 | /**
19 | * Transforms an expression `method(nullableArg, moreArgs)` into
20 | * `if(nullableArg ne null) method(nullableArg, moreArgs) else method(moreArgs)`.
21 | *
22 | * Reduces boilerplate associated with calling overloaded methods from Mongo ReactiveStreams driver that
23 | * may or may not take `ClientSession` as its first argument (non-nullable).
24 | */
25 | protected def optionalizeFirstArg[T](expr: T): T = macro MiscMacros.optionalizeFirstArg
26 | }
27 |
--------------------------------------------------------------------------------
/mongo/jvm/src/test/scala-2.13/com/avsystem/commons/mongo/typed/MongoPolyDataTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.typed
3 |
4 | import com.avsystem.commons.serialization.flatten
5 | import org.scalatest.funsuite.AnyFunSuite
6 |
7 | @flatten sealed trait PolyMongoUnion[+T]
8 | object PolyMongoUnion extends MongoPolyDataCompanion[PolyMongoUnion] {
9 | case class CaseOne[+T](value: T, str: String) extends PolyMongoUnion[T]
10 | case class CaseTwo[+T](foo: T, int: Int) extends PolyMongoUnion[T]
11 | case object CaseThree extends PolyMongoUnion[Nothing]
12 | }
13 |
14 | case class PolyMongoRecord[+T](value: T, meta: String)
15 | object PolyMongoRecord extends MongoPolyDataCompanion[PolyMongoRecord]
16 |
17 | class MongoPolyDataTest extends AnyFunSuite {
18 | final val Pmr = PolyMongoRecord
19 | final val Pmu = PolyMongoUnion
20 |
21 | test("filterPath") {
22 | assert(Pmr.dsl[Int].ref(_.value).rawPath == "value")
23 | assert(Pmu.dsl[Int].ref(_.as[PolyMongoUnion.CaseOne[Int]].value).rawPath == "value")
24 | assert(Pmu.dsl[Int].as[PolyMongoUnion.CaseOne[Int]].ref(_.value).rawPath == "value")
25 | assert(Pmu.dsl[Int].ref(_.as[PolyMongoUnion.CaseOne[Int]].str).rawPath == "str")
26 | assert(Pmu.dsl[PolyMongoUnion[Int]].ref(x =>
27 | x.as[PolyMongoUnion.CaseOne[PolyMongoUnion[Int]]].value
28 | .as[PolyMongoUnion.CaseOne[Int]].str
29 | ).rawPath == "value.str")
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/mongo/jvm/src/test/scala-2.13/com/avsystem/commons/mongo/typed/PolyDataWithCustomImplicits.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.typed
3 |
4 | import com.avsystem.commons.meta.MacroInstances
5 | import com.avsystem.commons.serialization.GenCodec
6 |
7 | case class CustomWrappy(value: String)
8 |
9 | object CustomImplicits {
10 | implicit val customWrappyCodec: GenCodec[CustomWrappy] =
11 | GenCodec.transformed[CustomWrappy, String](_.value, CustomWrappy.apply)
12 | }
13 |
14 | abstract class CustomPolyDataCompanion[D[_]](
15 | implicit instances: MacroInstances[CustomImplicits.type, MongoPolyAdtInstances[D]],
16 | ) extends AbstractMongoPolyDataCompanion[CustomImplicits.type, D](CustomImplicits)
17 |
18 | /**
19 | * This class tests (through its compilation) if implicit resolution conflicts that were
20 | * previously present in [[MongoPolyAdtInstances]] are fixed
21 | * ([[https://github.com/AVSystem/scala-commons/pull/429 #429]]).
22 | */
23 | case class PolyDataWithCustomImplicits[+T](wrappy: CustomWrappy, value: List[T])
24 | object PolyDataWithCustomImplicits extends CustomPolyDataCompanion[PolyDataWithCustomImplicits]
25 |
--------------------------------------------------------------------------------
/mongo/jvm/src/test/scala/com/avsystem/commons/mongo/BigDecimalEncodingTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo
3 |
4 | import org.scalatest.funsuite.AnyFunSuite
5 | import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks
6 |
7 | class BigDecimalEncodingTest extends AnyFunSuite with ScalaCheckPropertyChecks {
8 | test("BigDecimal BSON encoding") {
9 | forAll { (value: BigDecimal) =>
10 | assert(value == BsonInput.bigDecimalFromBytes(BsonOutput.bigDecimalBytes(value)))
11 | }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/mongo/jvm/src/test/scala/com/avsystem/commons/mongo/BsonRefTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo
3 |
4 | import com.avsystem.commons.serialization.{GenCodec, name, transparent}
5 | import org.scalatest.funsuite.AnyFunSuite
6 |
7 | case class InnerClass(
8 | map: Map[String, String]
9 | )
10 | object InnerClass extends BsonRef.Creator[InnerClass] {
11 | implicit val codec: GenCodec[InnerClass] = GenCodec.materialize
12 |
13 | final val MapRef = ref(_.map)
14 | }
15 |
16 | @transparent
17 | case class Wrapper(s: String) extends AnyVal
18 | object Wrapper {
19 | implicit val codec: GenCodec[Wrapper] = GenCodec.materialize
20 | }
21 |
22 | case class TestEntity(
23 | `$special.field`: String,
24 | wrapper: Wrapper,
25 | @name("inner") innerClass: InnerClass
26 | )
27 | object TestEntity extends BsonRef.Creator[TestEntity] {
28 | implicit val codec: GenCodec[TestEntity] = GenCodec.materialize
29 | }
30 |
31 | class BsonRefTest extends AnyFunSuite with BsonRef.Creator[TestEntity] {
32 | test("basic test") {
33 | assert(ref(_.wrapper).path == "wrapper")
34 | assert(ref(_.wrapper.s).path == "wrapper")
35 | assert(ref(_.innerClass).path == "inner")
36 | assert(ref(_.innerClass.map).path == "inner.map")
37 | assert(ref(_.innerClass).andThen(InnerClass.MapRef).path == "inner.map")
38 | assert(ref(_.innerClass.map("key")).path == "inner.map.key")
39 | assert(ref(_.`$special.field`).path == "\\$special\\_field")
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/mongo/jvm/src/test/scala/com/avsystem/commons/mongo/Decimal128UtilsTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo
3 |
4 | import org.bson.types.Decimal128
5 | import org.scalacheck.Arbitrary
6 | import org.scalatest.funsuite.AnyFunSuite
7 | import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks
8 |
9 | class Decimal128UtilsTest extends AnyFunSuite with ScalaCheckPropertyChecks {
10 | test("Decimal128Utils.fromBigDecimal is equivalent to new Decimal128") {
11 | forAll(Arbitrary.arbitrary[BigDecimal]) { (bd: BigDecimal) =>
12 | val usingUtils = Decimal128Utils.fromBigDecimal(bd)
13 | val usingConstructor = try new Decimal128(bd.bigDecimal).opt catch {
14 | case _: NumberFormatException => Opt.Empty
15 | }
16 | assert(usingUtils == usingConstructor)
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/mongo/jvm/src/test/scala/com/avsystem/commons/mongo/core/ops/BsonEquality.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.core.ops
3 |
4 | import org.bson.conversions.Bson
5 | import org.mongodb.scala.MongoClient
6 | import org.mongodb.scala.bson.BsonDocument
7 | import org.scalactic.Equality
8 |
9 | object BsonEquality extends Equality[Bson] {
10 | def toDoc(bson: Bson): BsonDocument = bson.toBsonDocument(classOf[BsonDocument], MongoClient.DEFAULT_CODEC_REGISTRY)
11 |
12 | override def areEqual(a: Bson, b: Any): Boolean = {
13 | (a, b) match {
14 | case (null, null) =>
15 | true
16 | case (aBson, bBson: Bson) =>
17 | toDoc(aBson) == toDoc(bBson)
18 | case _ =>
19 | false
20 | }
21 | }
22 |
23 | implicit def bsonEquality: BsonEquality.type = this
24 | }
25 |
--------------------------------------------------------------------------------
/mongo/jvm/src/test/scala/com/avsystem/commons/mongo/core/ops/BsonRefIterableUpdatingCompilationTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.core.ops
3 |
4 | import com.avsystem.commons.mongo.BsonRef
5 | import com.avsystem.commons.serialization.GenCodec
6 |
7 | class BsonRefIterableUpdatingCompilationTest extends BsonRef.Creator[Something] {
8 | import Updating._
9 |
10 | implicit val codec: GenCodec[Something] = GenCodec.materialize
11 |
12 | new BsonRefIterableUpdating(ref(_.a)) //Instantiation without explicit type parameters
13 | ref(_.a).push(7) //Implicit conversion to BsonRef -> BsonRefIterableUpdating
14 |
15 | }
16 |
17 | case class Something(a: Set[Int])
18 |
--------------------------------------------------------------------------------
/mongo/jvm/src/test/scala/com/avsystem/commons/mongo/core/ops/SomeEntity.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.core.ops
3 |
4 | case class SomeEntity(s: String, a: List[String])
5 |
--------------------------------------------------------------------------------
/mongo/jvm/src/test/scala/com/avsystem/commons/mongo/typed/MongoIndexTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.typed
3 |
4 | import org.scalatest.funsuite.AnyFunSuite
5 |
6 | class MongoIndexTest extends AnyFunSuite {
7 | final val Rte = RecordTestEntity
8 | final val Ute = UnionTestEntity
9 | final val Ir = InnerRecord
10 |
11 | test("index bsons") {
12 | assert(Rte.ref(_.int).ascendingIndex.toBson.toString ==
13 | """{"int": 1}""")
14 | assert(Rte.ref(_.int).descendingIndex.toBson.toString ==
15 | """{"int": -1}""")
16 | assert(MongoIndex.ascending(Rte.ref(_.int), Rte.ref(_.renamedStr)).toBson.toString ==
17 | """{"int": 1, "str": 1}""")
18 | assert(MongoIndex.descending(Rte.ref(_.int), Rte.ref(_.renamedStr)).toBson.toString ==
19 | """{"int": -1, "str": -1}""")
20 |
21 | assert(Rte.ref(_.int).index(MongoIndexType.Hashed).toBson.toString ==
22 | """{"int": "hashed"}""")
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/mongo/jvm/src/test/scala/com/avsystem/commons/mongo/typed/MongoOrderTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo.typed
3 |
4 | import org.scalatest.funsuite.AnyFunSuite
5 |
6 | class MongoOrderTest extends AnyFunSuite {
7 | final val Rte = RecordTestEntity
8 | final val Ute = UnionTestEntity
9 | final val Ir = InnerRecord
10 |
11 | test("simple") {
12 | assert(MongoOrder.ascending[Int].toBson.toString == "BsonInt32{value=1}")
13 | assert(MongoOrder.descending[Int].toBson.toString == "BsonInt32{value=-1}")
14 | }
15 |
16 | test("empty") {
17 | assert(MongoDocumentOrder.unspecified[InnerRecord].toBson.toString == "{}")
18 | }
19 |
20 | test("single field") {
21 | assert(Rte.ref(_.int).ascending.toBson.toString ==
22 | """{"int": 1}""")
23 | assert(Rte.ref(_.int).descending.toBson.toString ==
24 | """{"int": -1}""")
25 | }
26 |
27 | test("multi field") {
28 | assert(Rte.ref(_.int).ascending.andThenDescendingBy(Rte.ref(_.renamedStr)).toBson.toString ==
29 | """{"int": 1, "str": -1}""")
30 | assert(MongoDocumentOrder(Rte.ref(_.int) -> true, Rte.ref(_.renamedStr) -> false).toBson.toString ==
31 | """{"int": 1, "str": -1}""")
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/mongo/src/main/scala/com/avsystem/commons/mongo/mongoId.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package mongo
3 |
4 | import com.avsystem.commons.annotation.AnnotationAggregate
5 | import com.avsystem.commons.serialization.{name, outOfOrder}
6 |
7 | /**
8 | * Shortcut annotation for applying `@name("_id")` and `@outOfOrder` annotation on a case class field,
9 | * which is typical for MongoDB ID field.
10 | */
11 | class mongoId extends AnnotationAggregate {
12 | @name(mongoId.Id) @outOfOrder
13 | final def aggregated: List[StaticAnnotation] = reifyAggregated
14 | }
15 | object mongoId {
16 | final val Id = "_id"
17 | }
18 |
--------------------------------------------------------------------------------
/project/build.properties:
--------------------------------------------------------------------------------
1 | # suppress inspection "UnusedProperty"
2 | sbt.version=1.11.1
3 |
--------------------------------------------------------------------------------
/project/plugins.sbt:
--------------------------------------------------------------------------------
1 | logLevel := Level.Warn
2 |
3 | addSbtPlugin("com.github.ghik" % "sbt-nosbt" % "0.2.1")
4 | addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.19.0")
5 | addSbtPlugin("org.scala-js" % "sbt-jsdependencies" % "1.0.2")
6 | addSbtPlugin("org.jetbrains.scala" % "sbt-ide-settings" % "1.1.2")
7 | addSbtPlugin("pl.project13.scala" % "sbt-jmh" % "0.4.7")
8 | addSbtPlugin("com.github.sbt" % "sbt-ci-release" % "1.11.1")
9 | addSbtPlugin("com.github.sbt" % "sbt-unidoc" % "0.5.0")
10 | addSbtPlugin("com.typesafe" % "sbt-mima-plugin" % "1.1.4")
11 | addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.6.4")
12 | addSbtPlugin("com.github.sbt" % "sbt-github-actions" % "0.25.0")
13 |
--------------------------------------------------------------------------------
/redis/preconfiguredCluster/9000/nodes.conf:
--------------------------------------------------------------------------------
1 | 18346204561dbae251912d8ae93fa4c78eeb3e16 127.0.0.1:9003@19003 slave 5f4c1e93370f3a60e9106cff3a613216abb1c8dc 0 1481032703835 2 connected
2 | 6a724c321662027e9c1c58684ea82a1315a294fb 127.0.0.1:9004@19004 master - 0 1481032702831 1 connected 10922-16383
3 | b714a8032b9c1d74a7adc7da75fdbde0517bdf1b 127.0.0.1:9000@19000 myself,master - 0 0 0 connected 0-5460
4 | cc8228f6e849ba1ee5abfc8f1ebde238e08c1d27 127.0.0.1:9001@19001 slave b714a8032b9c1d74a7adc7da75fdbde0517bdf1b 0 1481032701225 0 connected
5 | 5f4c1e93370f3a60e9106cff3a613216abb1c8dc 127.0.0.1:9002@19002 master - 0 1481032701229 2 connected 5461-10921
6 | 9a90efc8f9cf52de6aa60be3da3071798e0e365f 127.0.0.1:9005@19005 slave 6a724c321662027e9c1c58684ea82a1315a294fb 0 1481032701826 1 connected
7 | vars currentEpoch 3 lastVoteEpoch 0
8 |
--------------------------------------------------------------------------------
/redis/preconfiguredCluster/9001/nodes.conf:
--------------------------------------------------------------------------------
1 | 5f4c1e93370f3a60e9106cff3a613216abb1c8dc 127.0.0.1:9002@19002 master - 0 1481032706841 2 connected 5461-10921
2 | b714a8032b9c1d74a7adc7da75fdbde0517bdf1b 127.0.0.1:9000@19000 master - 0 1481032702835 0 connected 0-5460
3 | 9a90efc8f9cf52de6aa60be3da3071798e0e365f 127.0.0.1:9005@19005 slave 6a724c321662027e9c1c58684ea82a1315a294fb 0 1481032704837 1 connected
4 | cc8228f6e849ba1ee5abfc8f1ebde238e08c1d27 127.0.0.1:9001@19001 myself,slave b714a8032b9c1d74a7adc7da75fdbde0517bdf1b 0 0 0 connected
5 | 18346204561dbae251912d8ae93fa4c78eeb3e16 127.0.0.1:9003@19003 slave 5f4c1e93370f3a60e9106cff3a613216abb1c8dc 0 1481032705839 2 connected
6 | 6a724c321662027e9c1c58684ea82a1315a294fb 127.0.0.1:9004@19004 master - 0 1481032703834 1 connected 10922-16383
7 | vars currentEpoch 3 lastVoteEpoch 0
8 |
--------------------------------------------------------------------------------
/redis/preconfiguredCluster/9002/nodes.conf:
--------------------------------------------------------------------------------
1 | 9a90efc8f9cf52de6aa60be3da3071798e0e365f 127.0.0.1:9005@19005 slave 6a724c321662027e9c1c58684ea82a1315a294fb 0 1481032702853 1 connected
2 | 5f4c1e93370f3a60e9106cff3a613216abb1c8dc 127.0.0.1:9002@19002 myself,master - 0 0 2 connected 5461-10921
3 | b714a8032b9c1d74a7adc7da75fdbde0517bdf1b 127.0.0.1:9000@19000 master - 0 1481032703857 0 connected 0-5460
4 | 18346204561dbae251912d8ae93fa4c78eeb3e16 127.0.0.1:9003@19003 slave 5f4c1e93370f3a60e9106cff3a613216abb1c8dc 0 1481032705853 2 connected
5 | cc8228f6e849ba1ee5abfc8f1ebde238e08c1d27 127.0.0.1:9001@19001 slave b714a8032b9c1d74a7adc7da75fdbde0517bdf1b 0 1481032701947 0 connected
6 | 6a724c321662027e9c1c58684ea82a1315a294fb 127.0.0.1:9004@19004 master - 0 1481032704851 1 connected 10922-16383
7 | vars currentEpoch 3 lastVoteEpoch 0
8 |
--------------------------------------------------------------------------------
/redis/preconfiguredCluster/9003/nodes.conf:
--------------------------------------------------------------------------------
1 | 6a724c321662027e9c1c58684ea82a1315a294fb 127.0.0.1:9004@19004 master - 0 1481032704847 1 connected 10922-16383
2 | 18346204561dbae251912d8ae93fa4c78eeb3e16 127.0.0.1:9003@19003 myself,slave 5f4c1e93370f3a60e9106cff3a613216abb1c8dc 0 0 3 connected
3 | b714a8032b9c1d74a7adc7da75fdbde0517bdf1b 127.0.0.1:9000@19000 master - 0 1481032701845 0 connected 0-5460
4 | 9a90efc8f9cf52de6aa60be3da3071798e0e365f 127.0.0.1:9005@19005 slave 6a724c321662027e9c1c58684ea82a1315a294fb 0 1481032702842 1 connected
5 | cc8228f6e849ba1ee5abfc8f1ebde238e08c1d27 127.0.0.1:9001@19001 slave b714a8032b9c1d74a7adc7da75fdbde0517bdf1b 0 1481032701944 0 connected
6 | 5f4c1e93370f3a60e9106cff3a613216abb1c8dc 127.0.0.1:9002@19002 master - 0 1481032703848 2 connected 5461-10921
7 | vars currentEpoch 3 lastVoteEpoch 0
8 |
--------------------------------------------------------------------------------
/redis/preconfiguredCluster/9004/nodes.conf:
--------------------------------------------------------------------------------
1 | 18346204561dbae251912d8ae93fa4c78eeb3e16 127.0.0.1:9003@19003 slave 5f4c1e93370f3a60e9106cff3a613216abb1c8dc 0 1481032706858 3 connected
2 | 5f4c1e93370f3a60e9106cff3a613216abb1c8dc 127.0.0.1:9002@19002 master - 0 1481032708861 2 connected 5461-10921
3 | b714a8032b9c1d74a7adc7da75fdbde0517bdf1b 127.0.0.1:9000@19000 master - 0 1481032707859 0 connected 0-5460
4 | 9a90efc8f9cf52de6aa60be3da3071798e0e365f 127.0.0.1:9005@19005 slave 6a724c321662027e9c1c58684ea82a1315a294fb 0 1481032701956 1 connected
5 | 6a724c321662027e9c1c58684ea82a1315a294fb 127.0.0.1:9004@19004 myself,master - 0 0 1 connected 10922-16383
6 | cc8228f6e849ba1ee5abfc8f1ebde238e08c1d27 127.0.0.1:9001@19001 slave b714a8032b9c1d74a7adc7da75fdbde0517bdf1b 0 1481032705856 0 connected
7 | vars currentEpoch 3 lastVoteEpoch 0
8 |
--------------------------------------------------------------------------------
/redis/preconfiguredCluster/9005/nodes.conf:
--------------------------------------------------------------------------------
1 | 6a724c321662027e9c1c58684ea82a1315a294fb 127.0.0.1:9004@19004 master - 0 1481032708878 1 connected 10922-16383
2 | 5f4c1e93370f3a60e9106cff3a613216abb1c8dc 127.0.0.1:9002@19002 master - 0 1481032707876 2 connected 5461-10921
3 | b714a8032b9c1d74a7adc7da75fdbde0517bdf1b 127.0.0.1:9000@19000 master - 0 1481032706873 0 connected 0-5460
4 | cc8228f6e849ba1ee5abfc8f1ebde238e08c1d27 127.0.0.1:9001@19001 slave b714a8032b9c1d74a7adc7da75fdbde0517bdf1b 0 1481032704868 0 connected
5 | 9a90efc8f9cf52de6aa60be3da3071798e0e365f 127.0.0.1:9005@19005 myself,slave 6a724c321662027e9c1c58684ea82a1315a294fb 0 0 1 connected
6 | 18346204561dbae251912d8ae93fa4c78eeb3e16 127.0.0.1:9003@19003 slave 5f4c1e93370f3a60e9106cff3a613216abb1c8dc 0 1481032705871 2 connected
7 | vars currentEpoch 3 lastVoteEpoch 0
8 |
--------------------------------------------------------------------------------
/redis/preconfiguredMasterSlave/28000/sentinel.conf:
--------------------------------------------------------------------------------
1 | sentinel myid 5efabf779f04ebe0e292c2ea828fc8bffebd6616
2 | sentinel deny-scripts-reconfig yes
3 | sentinel monitor testmaster 127.0.0.1 8000 2
4 |
5 | # Generated by CONFIG REWRITE
6 | sentinel down-after-milliseconds testmaster 5000
7 | sentinel failover-timeout testmaster 60000
8 | sentinel config-epoch testmaster 0
9 | sentinel leader-epoch testmaster 0
10 | sentinel known-replica testmaster 127.0.0.1 8001
11 | sentinel known-sentinel testmaster 127.0.0.1 28002 0df1bfd87eb1a4b125f4cfa0f794c0d5bd4177cf
12 | sentinel known-sentinel testmaster 127.0.0.1 28001 abf3ec0cd1c6a493c9410ead6497e1e30a6c2be9
13 | sentinel current-epoch 0
14 |
--------------------------------------------------------------------------------
/redis/preconfiguredMasterSlave/28001/sentinel.conf:
--------------------------------------------------------------------------------
1 | sentinel myid abf3ec0cd1c6a493c9410ead6497e1e30a6c2be9
2 | sentinel deny-scripts-reconfig yes
3 | sentinel monitor testmaster 127.0.0.1 8000 2
4 |
5 | # Generated by CONFIG REWRITE
6 | sentinel down-after-milliseconds testmaster 5000
7 | sentinel failover-timeout testmaster 60000
8 | sentinel config-epoch testmaster 0
9 | sentinel leader-epoch testmaster 0
10 | sentinel known-replica testmaster 127.0.0.1 8001
11 | sentinel known-sentinel testmaster 127.0.0.1 28002 0df1bfd87eb1a4b125f4cfa0f794c0d5bd4177cf
12 | sentinel known-sentinel testmaster 127.0.0.1 28000 5efabf779f04ebe0e292c2ea828fc8bffebd6616
13 | sentinel current-epoch 0
14 |
--------------------------------------------------------------------------------
/redis/preconfiguredMasterSlave/28002/sentinel.conf:
--------------------------------------------------------------------------------
1 | sentinel myid 0df1bfd87eb1a4b125f4cfa0f794c0d5bd4177cf
2 | sentinel deny-scripts-reconfig yes
3 | sentinel monitor testmaster 127.0.0.1 8000 2
4 |
5 | # Generated by CONFIG REWRITE
6 | sentinel down-after-milliseconds testmaster 5000
7 | sentinel failover-timeout testmaster 60000
8 | sentinel config-epoch testmaster 0
9 | sentinel leader-epoch testmaster 0
10 | sentinel known-replica testmaster 127.0.0.1 8001
11 | sentinel known-sentinel testmaster 127.0.0.1 28000 5efabf779f04ebe0e292c2ea828fc8bffebd6616
12 | sentinel known-sentinel testmaster 127.0.0.1 28001 abf3ec0cd1c6a493c9410ead6497e1e30a6c2be9
13 | sentinel current-epoch 0
14 |
--------------------------------------------------------------------------------
/redis/preconfiguredMasterSlave/8000/redis.log:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AVSystem/scala-commons/2a951ce738dd0fa4387e5095e466b4df7505b024/redis/preconfiguredMasterSlave/8000/redis.log
--------------------------------------------------------------------------------
/redis/preconfiguredMasterSlave/8001/redis.log:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AVSystem/scala-commons/2a951ce738dd0fa4387e5095e466b4df7505b024/redis/preconfiguredMasterSlave/8001/redis.log
--------------------------------------------------------------------------------
/redis/src/main/resources/reference.conf:
--------------------------------------------------------------------------------
1 | redis {
2 | worker-dispatcher-path = redis.pinned-dispatcher
3 | default-dispatcher-path = pekko.actor.default-dispatcher
4 |
5 | pinned-dispatcher {
6 | executor = thread-pool-executor
7 | type = PinnedDispatcher
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/redis/src/main/scala/com/avsystem/commons/redis/NodeAddress.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package redis
3 |
4 | import java.net.InetSocketAddress
5 |
6 | object NodeAddress {
7 | final val DefaultIP = "127.0.0.1"
8 | final val DefaultPort = 6379
9 | final val DefaultSentinelPort = 26379
10 |
11 | final val Default = NodeAddress()
12 | final val DefaultSentinel = NodeAddress(port = DefaultSentinelPort)
13 |
14 | def parse(str: String): NodeAddress = {
15 | val Array(ip, port) = str.split(':')
16 | NodeAddress(ip, port.toInt)
17 | }
18 | }
19 | final case class NodeAddress(ip: String = NodeAddress.DefaultIP, port: Int = NodeAddress.DefaultPort) {
20 | def socketAddress = new InetSocketAddress(ip, port)
21 | override def toString = s"$ip:$port"
22 | }
23 |
--------------------------------------------------------------------------------
/redis/src/main/scala/com/avsystem/commons/redis/commands/common.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package redis.commands
3 |
4 | import com.avsystem.commons.misc.{NamedEnum, NamedEnumCompanion}
5 |
6 | import scala.collection.mutable
7 |
8 | sealed abstract class SortOrder(val name: String) extends NamedEnum
9 | object SortOrder extends NamedEnumCompanion[SortOrder] {
10 | case object Asc extends SortOrder("ASC")
11 | case object Desc extends SortOrder("DESC")
12 |
13 | def apply(asc: Boolean): SortOrder =
14 | if (asc) Asc else Desc
15 |
16 | val values: List[SortOrder] = caseObjects
17 | }
18 |
19 | case class Cursor(raw: Long) extends AnyVal {
20 | override def toString: String = raw.toString
21 | }
22 | object Cursor {
23 | final val NoCursor = Cursor(0)
24 | }
25 |
26 | abstract class ParsedInfo(info: String, attrSeparator: String, nameValueSeparator: String) {
27 | protected val attrMap: BMap[String, String] = mutable.HashMap() ++
28 | info.split(attrSeparator).iterator.map { attr =>
29 | val Array(name, value) = attr.split(nameValueSeparator, 2)
30 | (name, value)
31 | }
32 |
33 | override def toString: String = info
34 | }
35 |
--------------------------------------------------------------------------------
/redis/src/main/scala/com/avsystem/commons/redis/config/ExecutionConfig.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package redis.config
3 |
4 | import org.apache.pekko.util.Timeout
5 | import com.avsystem.commons.concurrent.RunNowEC
6 |
7 | import scala.concurrent.duration._
8 |
9 | /**
10 | * Additional options for executing a [[com.avsystem.commons.redis.RedisBatch RedisBatch]] on a
11 | * [[com.avsystem.commons.redis.RedisExecutor RedisExecutor]]
12 | *
13 | * @param responseTimeout Redis server response timeout. If executing a batch involves retries (e.g. because of
14 | * cluster redirections) then timeout is applied independently on every retry.
15 | * @param decodeOn execution context on which Redis response to a batch will be decoded. Normally this is happening
16 | * on one of the connection actor threads. This is ok for simple Redis commands but may introduce
17 | * performance bottleneck for large batches with more heavy decoding. In such case it may be
18 | * beneficial to delegate that work to some external executor.
19 | */
20 | case class ExecutionConfig(
21 | responseTimeout: Timeout = 10.seconds,
22 | decodeOn: ExecutionContext = RunNowEC
23 | )
24 | object ExecutionConfig {
25 | final val Default = ExecutionConfig()
26 | }
27 |
--------------------------------------------------------------------------------
/redis/src/main/scala/com/avsystem/commons/redis/monitoring/ClusterStateObserver.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package redis.monitoring
3 |
4 | import com.avsystem.commons.redis.RedisClusterClient
5 |
6 | /**
7 | * Intended for monitoring [[RedisClusterClient]]'s connections.
8 | * Should be non-blocking and handle internal exceptions by itself.
9 | */
10 | trait ClusterStateObserver extends ConnectionStateObserver {
11 | def onClusterRefresh(): Unit
12 | def onClusterRefreshFailure(): Unit
13 | def onClusterInitialized(): Unit
14 | def onClusterInitFailure(): Unit
15 | }
16 |
--------------------------------------------------------------------------------
/redis/src/main/scala/com/avsystem/commons/redis/monitoring/ConnectionStateObserver.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package redis.monitoring
3 |
4 | import com.avsystem.commons.misc.{AbstractValueEnum, AbstractValueEnumCompanion, EnumCtx}
5 | import com.avsystem.commons.redis.NodeAddress
6 |
7 | /**
8 | * Intended for monitoring the state of a single Redis connection.
9 | * Should be non-blocking and handle internal exceptions by itself.
10 | */
11 | trait ConnectionStateObserver {
12 | def onConnectionStateChange(addr: NodeAddress, state: ConnectionState): Unit
13 | }
14 |
15 | final class ConnectionState(implicit enumCtx: EnumCtx) extends AbstractValueEnum
16 |
17 | object ConnectionState extends AbstractValueEnumCompanion[ConnectionState] {
18 | final val Created, Connecting, Connected, Closed, Removed: ConnectionState = new ConnectionState
19 | }
20 |
--------------------------------------------------------------------------------
/redis/src/main/scala/com/avsystem/commons/redis/monitoring/SentinelStateObserver.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package redis.monitoring
3 |
4 | import com.avsystem.commons.redis.NodeAddress
5 | import com.avsystem.commons.redis.RedisMasterSlaveClient
6 |
7 | /**
8 | * Intended for monitoring [[RedisMasterSlaveClient]]'s state and connections.
9 | * Should be non-blocking and handle internal exceptions by itself.
10 | */
11 | trait SentinelStateObserver extends ConnectionStateObserver {
12 | def onMasterChange(master: NodeAddress): Unit
13 | }
14 |
--------------------------------------------------------------------------------
/redis/src/main/scala/com/avsystem/commons/redis/util/ActorLazyLogging.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package redis.util
3 |
4 | import org.apache.pekko.actor.Actor
5 | import org.apache.pekko.event.LoggingAdapter
6 |
7 | /**
8 | * Author: ghik
9 | * Created: 12/04/16.
10 | */
11 | trait ActorLazyLogging { self: Actor =>
12 | object log {
13 | val rawLog: LoggingAdapter = org.apache.pekko.event.Logging(context.system, self)
14 |
15 | def error(msg: => String, cause: Throwable = null): Unit =
16 | if (rawLog.isErrorEnabled) {
17 | if (cause == null) {
18 | rawLog.error(msg)
19 | } else {
20 | rawLog.error(cause, msg)
21 | }
22 | }
23 |
24 | def warning(msg: => String): Unit = macro macros.misc.LazyLoggingMacros.warningImpl
25 |
26 | def info(msg: => String): Unit = macro macros.misc.LazyLoggingMacros.infoImpl
27 |
28 | def debug(msg: => String): Unit = macro macros.misc.LazyLoggingMacros.debugImpl
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/redis/src/main/scala/com/avsystem/commons/redis/util/DelayedFuture.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package redis.util
3 |
4 | import org.apache.pekko.actor.ActorSystem
5 | import com.avsystem.commons.concurrent.RunNowEC
6 |
7 | import scala.concurrent.duration.{Duration, FiniteDuration}
8 |
9 | object DelayedFuture {
10 | def apply(delay: FiniteDuration)(implicit system: ActorSystem): Future[Unit] =
11 | if (delay <= Duration.Zero) Future.unit
12 | else {
13 | val promise = Promise[Unit]()
14 | system.scheduler.scheduleOnce(delay)(promise.success(()))(RunNowEC)
15 | promise.future
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/redis/src/main/scala/com/avsystem/commons/redis/util/FoldingBuilder.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package redis.util
3 |
4 | import com.avsystem.commons.collection.CrossBuilder
5 |
6 | class FoldingBuilder[A, B](zero: B, fun: (B, A) => B) extends CrossBuilder[A, B] {
7 | private[this] var res = zero
8 | def addOne(elem: A): this.type = {
9 | res = fun(res, elem)
10 | this
11 | }
12 | def clear(): Unit = res = zero
13 | def result(): B = res
14 | }
15 |
16 | object UnitBuilder extends CrossBuilder[Any, Unit] {
17 | def addOne(elem: Any): this.type = this
18 | def clear(): Unit = ()
19 | def result(): Unit = ()
20 | }
21 |
--------------------------------------------------------------------------------
/redis/src/main/scala/com/avsystem/commons/redis/util/HeadIterable.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package redis.util
3 |
4 | final class HeadIterable[+A](head: A, tail: Iterable[A]) extends Iterable[A] {
5 | override def iterator: HeadIterator[A] = new HeadIterator(head, tail.iterator)
6 |
7 | override def isEmpty = false
8 | override def foreach[U](f: A => U): Unit = {
9 | f(head)
10 | tail.foreach(f)
11 | }
12 | }
13 |
14 | final class HeadIterator[+A](head: A, tail: Iterator[A]) extends Iterator[A] {
15 | private[this] var atHead = true
16 | def hasNext: Boolean = atHead || tail.hasNext
17 | override def next(): A =
18 | if (atHead) {
19 | atHead = false
20 | head
21 | } else tail.next()
22 | }
23 |
--------------------------------------------------------------------------------
/redis/src/main/scala/com/avsystem/commons/redis/util/SingletonSeq.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package redis.util
3 |
4 |
5 | final class SingletonSeq[+A](value: A) extends IIndexedSeq[A] {
6 | def length: Int = 1
7 | def apply(idx: Int): A = idx match {
8 | case 0 => value
9 | case _ => throw new IndexOutOfBoundsException
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/redis/src/test/resources/application.conf:
--------------------------------------------------------------------------------
1 | pekko.loglevel = INFO
2 |
--------------------------------------------------------------------------------
/redis/src/test/scala/com/avsystem/commons/redis/ClusterUtils.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package redis
3 |
4 | import org.apache.pekko.util.ByteString
5 |
6 | import scala.io.Source
7 |
8 | /**
9 | * Author: ghik
10 | * Created: 28/06/16.
11 | */
12 | object ClusterUtils {
13 | final val SlotKeys =
14 | Source.fromInputStream(getClass.getResourceAsStream("/slotkeys.txt"))
15 | .getLines().toArray
16 |
17 | def keyWithSameSlotAs(key: String): String =
18 | SlotKeys(Hash.slot(ByteString(key)))
19 |
20 | def strings(length: Int): Iterator[String] =
21 | if (length == 0) Iterator("")
22 | else strings(length - 1).flatMap(p => ('a' to 'z').iterator.map(c => p + c))
23 |
24 | def findKeyForSlot(slot: Int, length: Int): String =
25 | strings(length).find(bs => Hash.slot(ByteString(bs)) == slot)
26 | .getOrElse(throw new IllegalArgumentException(s"Could not find key that would map to slot $slot"))
27 |
28 | def main(args: Array[String]): Unit = {
29 | println(SlotKeys(123))
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/redis/src/test/scala/com/avsystem/commons/redis/CommunicationLogging.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package redis
3 |
4 | import org.scalatest.{BeforeAndAfterEach, Suite}
5 |
6 | /**
7 | * Author: ghik
8 | * Created: 29/07/16.
9 | */
10 | trait CommunicationLogging extends BeforeAndAfterEach { this: Suite =>
11 | protected val listener = new TestDebugListener
12 |
13 | protected def assertCommunication(comm: String): Unit = {
14 | assert(listener.result().trim == comm.trim)
15 | }
16 |
17 | override protected def beforeEach() = {
18 | super.beforeEach()
19 | listener.clear()
20 | }
21 | }
--------------------------------------------------------------------------------
/redis/src/test/scala/com/avsystem/commons/redis/RedisDataGenCodecRoundtripTest.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package redis
3 |
4 | import org.apache.pekko.util.ByteString
5 | import com.avsystem.commons.serialization.{GenCodecRoundtripTest, Input, Output}
6 |
7 | class RedisDataGenCodecRoundtripTest extends GenCodecRoundtripTest {
8 | type Raw = ByteString
9 |
10 | def writeToOutput(write: Output => Unit): ByteString = {
11 | var result: ByteString = null
12 | write(new RedisDataOutput(result = _))
13 | result
14 | }
15 |
16 | def createInput(raw: ByteString): Input =
17 | new RedisDataInput(raw)
18 | }
19 |
--------------------------------------------------------------------------------
/redis/src/test/scala/com/avsystem/commons/redis/SeparateThreadExecutionContext.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package redis
3 |
4 | /**
5 | * Author: ghik
6 | * Created: 06/10/16.
7 | */
8 | object SeparateThreadExecutionContext extends ExecutionContext {
9 | def execute(runnable: Runnable): Unit = new Thread(runnable).start()
10 | def reportFailure(cause: Throwable): Unit = cause.printStackTrace()
11 | def submit[T](code: => T): Future[T] = Future(code)(this)
12 | }
13 |
--------------------------------------------------------------------------------
/redis/src/test/scala/com/avsystem/commons/redis/TestDebugListener.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package redis
3 |
4 | import org.apache.pekko.util.ByteString
5 | import com.avsystem.commons.redis.actor.RedisConnectionActor.DebugListener
6 | import com.avsystem.commons.redis.protocol.RedisMsg
7 |
8 | /**
9 | * Author: ghik
10 | * Created: 29/07/16.
11 | */
12 | class TestDebugListener extends DebugListener {
13 | private var receiving = false
14 | private val builder = new StringBuilder
15 |
16 | def clear(): Unit = synchronized {
17 | receiving = true
18 | builder.clear()
19 | }
20 |
21 | def onSend(data: ByteString) = synchronized {
22 | if (receiving) {
23 | builder.append("\n")
24 | receiving = false
25 | }
26 | builder.append(RedisMsg.escape(data, quote = false).replace("\\r\\n", "\\r\\n\n"))
27 | }
28 |
29 | def onReceive(data: ByteString) = synchronized {
30 | if (!receiving) {
31 | builder.append("\n")
32 | receiving = true
33 | }
34 | builder.append(RedisMsg.escape(data, quote = false).replace("\\r\\n", "\\r\\n\n"))
35 | }
36 |
37 | def result(): String = synchronized {
38 | builder.result()
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/redis/src/test/scala/com/avsystem/commons/redis/UsesPreconfiguredCluster.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package redis
3 |
4 | import java.io.File
5 |
6 | import org.apache.commons.io.FileUtils
7 | import org.scalatest.Suite
8 |
9 | import scala.concurrent.Await
10 | import scala.concurrent.duration._
11 |
12 | /**
13 | * Author: ghik
14 | * Created: 27/06/16.
15 | */
16 | trait UsesPreconfiguredCluster extends UsesActorSystem with UsesClusterServers { this: Suite =>
17 |
18 | def ports: Range = 9000 to 9005
19 | def preconfiguredDir: File = new File("preconfiguredCluster")
20 |
21 | protected def prepareDirectory(): Unit =
22 | FileUtils.copyDirectory(preconfiguredDir, clusterDir)
23 |
24 | override protected def beforeAll(): Unit = {
25 | super.beforeAll()
26 |
27 | val clients = addresses.map(addr => new RedisConnectionClient(addr))
28 | val commands = clients.map(client => RedisApi.Connection.Async.BinaryTyped(client))
29 | val initFuture = Future.traverse(commands) { c =>
30 | waitUntil(
31 | for {
32 | stateOk <- c.clusterInfo.map(_.stateOk)
33 | slavesHaveSlots <- c.clusterSlots.map(_.forall(_.slaves.nonEmpty))
34 | } yield stateOk && slavesHaveSlots,
35 | 500.millis,
36 | )
37 | }
38 |
39 | Await.result(initFuture, 30.seconds)
40 |
41 | clients.foreach(_.close())
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/redis/src/test/scala/com/avsystem/commons/redis/UsesPreconfiguredMasterSlave.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package redis
3 |
4 | import java.io.File
5 |
6 | import org.apache.commons.io.FileUtils
7 | import org.scalatest.Suite
8 |
9 | /**
10 | * Author: ghik
11 | * Created: 27/06/16.
12 | */
13 | trait UsesPreconfiguredMasterSlave extends UsesActorSystem with UsesMasterSlaveServers { this: Suite =>
14 | def masterName: String = "testmaster"
15 | def ports: Range = 8000 to 8001
16 | def sentinelPorts: Range = 28000 to 28002
17 | def preconfiguredDir: File = new File("preconfiguredMasterSlave")
18 |
19 | protected def prepareDirectory(): Unit =
20 | FileUtils.copyDirectory(preconfiguredDir, masterSlaveDir)
21 | }
22 |
--------------------------------------------------------------------------------
/redis/src/test/scala/com/avsystem/commons/redis/UsesRedisConnectionClient.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package redis
3 |
4 | import com.avsystem.commons.redis.config.ConnectionConfig
5 | import org.scalatest.Suite
6 |
7 | /**
8 | * Author: ghik
9 | * Created: 09/06/16.
10 | */
11 | trait UsesRedisConnectionClient extends UsesRedisServer with UsesActorSystem with UsesSslContext { this: Suite =>
12 | def useTls: Boolean = false
13 |
14 | def connectionConfig: ConnectionConfig =
15 | ConnectionConfig(sslEngineCreator = if (useTls) OptArg(() => sslContext.createSSLEngine()) else OptArg.Empty)
16 |
17 | var redisClient: RedisConnectionClient = _
18 |
19 | override protected def beforeAll(): Unit = {
20 | super.beforeAll()
21 | redisClient = new RedisConnectionClient(if (useTls) tlsAddress else address, connectionConfig)
22 | }
23 |
24 | override protected def afterAll(): Unit = {
25 | redisClient.close()
26 | super.afterAll()
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/redis/src/test/scala/com/avsystem/commons/redis/UsesRedisMasterSlaveClient.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package redis
3 |
4 | import com.avsystem.commons.redis.config.MasterSlaveConfig
5 | import org.scalatest.Suite
6 |
7 | /**
8 | * Author: ghik
9 | * Created: 14/04/16.
10 | */
11 | trait UsesRedisMasterSlaveClient extends UsesMasterSlaveServers with UsesActorSystem { this: Suite =>
12 | def masterSlaveConfig: MasterSlaveConfig = MasterSlaveConfig()
13 | def seedSentinels: Seq[NodeAddress] = sentinelAddresses.take(1)
14 |
15 | var redisClient: RedisMasterSlaveClient = _
16 |
17 | def switchMaster(): Future[Unit] = {
18 | val client = new RedisConnectionClient(seedSentinels.head)
19 | val api = RedisApi.Connection.Async.StringTyped(client)
20 | val result = api.sentinelFailover(masterName)
21 | result.onCompleteNow(_ => client.close())
22 | result
23 | }
24 |
25 | override protected def beforeAll(): Unit = {
26 | super.beforeAll()
27 | redisClient = new RedisMasterSlaveClient(masterName, seedSentinels, masterSlaveConfig)
28 | }
29 |
30 | override protected def afterAll(): Unit = {
31 | redisClient.close()
32 | super.afterAll()
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/redis/src/test/scala/com/avsystem/commons/redis/UsesRedisNodeClient.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package redis
3 |
4 | import com.avsystem.commons.redis.config.{ConnectionConfig, NodeConfig}
5 | import org.scalatest.Suite
6 |
7 | /**
8 | * Author: ghik
9 | * Created: 14/04/16.
10 | */
11 | trait UsesRedisNodeClient extends UsesRedisServer with UsesActorSystem with UsesSslContext { this: Suite =>
12 | def useTls: Boolean = false
13 |
14 | def connectionConfig: ConnectionConfig =
15 | ConnectionConfig(sslEngineCreator = if (useTls) OptArg(() => sslContext.createSSLEngine()) else OptArg.Empty)
16 |
17 | def nodeConfig: NodeConfig = NodeConfig(
18 | connectionConfigs = _ => connectionConfig,
19 | blockingConnectionConfigs = _ => connectionConfig
20 | )
21 |
22 | var redisClient: RedisNodeClient = _
23 |
24 | override protected def beforeAll(): Unit = {
25 | super.beforeAll()
26 | redisClient = new RedisNodeClient(if (useTls) tlsAddress else address, nodeConfig)
27 | }
28 |
29 | override protected def afterAll(): Unit = {
30 | redisClient.close()
31 | super.afterAll()
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/redis/src/test/scala/com/avsystem/commons/redis/UsesRedisServer.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package redis
3 |
4 | import org.scalatest.{BeforeAndAfterAll, Suite}
5 |
6 | import scala.concurrent.Await
7 | import scala.concurrent.duration._
8 |
9 | /**
10 | * Author: ghik
11 | * Created: 14/04/16.
12 | */
13 | trait UsesRedisServer extends BeforeAndAfterAll with RedisProcessUtils { this: Suite =>
14 | def port: Int = 7000
15 | def tlsPort: Int = 8000
16 |
17 | def address: NodeAddress = NodeAddress(port = port)
18 | def tlsAddress: NodeAddress = NodeAddress(port = tlsPort)
19 |
20 | var redisProcess: RedisProcess = _
21 |
22 | override protected def beforeAll(): Unit = {
23 | super.beforeAll()
24 |
25 | redisProcess = Await.result(
26 | launchRedis(
27 | "--daemonize", "no",
28 | "--port", port.toString,
29 | "--tls-port", tlsPort.toString,
30 | "--tls-cert-file", "./tls/redis.crt",
31 | "--tls-key-file", "./tls/redis.key",
32 | "--tls-ca-cert-file", "./tls/ca.crt"
33 | ),
34 | 10.seconds
35 | )
36 | }
37 |
38 | override protected def afterAll(): Unit = {
39 | Await.result(shutdownRedis(redisProcess), 10.seconds)
40 | super.afterAll()
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/redis/src/test/scala/com/avsystem/commons/redis/UsesSslContext.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package redis
3 |
4 | import java.io.FileInputStream
5 | import java.security.{KeyStore, SecureRandom}
6 |
7 | import javax.net.ssl.{KeyManagerFactory, SSLContext, SSLEngine, TrustManagerFactory}
8 |
9 | trait UsesSslContext {
10 | lazy val sslContext: SSLContext = SSLContext.getInstance("TLSv1.2").setup { sslc =>
11 | val ks = KeyStore.getInstance("PKCS12")
12 | ks.load(new FileInputStream("./tls/redis.p12"), Array.empty)
13 |
14 | val kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm)
15 | kmf.init(ks, Array.empty)
16 |
17 | val tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm)
18 | tmf.init(ks)
19 |
20 | sslc.init(kmf.getKeyManagers, tmf.getTrustManagers, new SecureRandom)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/redis/src/test/scala/com/avsystem/commons/redis/commands/ConnectionApiSuite.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package redis.commands
3 |
4 | import org.apache.pekko.util.ByteString
5 | import com.avsystem.commons.redis.exception.ErrorReplyException
6 | import com.avsystem.commons.redis.{RedisApi, RedisConnectionCommandsSuite}
7 |
8 | /**
9 | * Author: ghik
10 | * Created: 29/09/16.
11 | */
12 | trait ConnectionApiSuite extends RedisConnectionCommandsSuite {
13 |
14 | import RedisApi.Batches.StringTyped._
15 |
16 | apiTest("ECHO") {
17 | echo(ByteString("lol")).assertEquals(ByteString("lol"))
18 | }
19 |
20 | apiTest("PING") {
21 | ping.assertEquals(bs"PONG")
22 | }
23 |
24 | apiTest("SELECT") {
25 | select(1).get
26 | }
27 |
28 | apiTest("SWAPDB") {
29 | swapdb(0, 1).get
30 | }
31 | }
32 |
33 | class AuthenticationTest extends RedisConnectionCommandsSuite {
34 | override def password: Opt[String] = "hassword".opt
35 |
36 | import RedisApi.Batches.StringTyped._
37 |
38 | test("AUTH") {
39 | get("key").intercept[ErrorReplyException]
40 | auth("hassword").get
41 | get("key").assertEquals(Opt.Empty)
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/redis/src/test/scala/com/avsystem/commons/redis/commands/HyperLogLogApiSuite.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package redis.commands
3 |
4 | import com.avsystem.commons.redis._
5 |
6 | trait HyperLogLogApiSuite extends CommandsSuite {
7 |
8 | import RedisApi.Batches.StringTyped._
9 |
10 | apiTest("PFADD") {
11 | pfadd("key", Nil).assertEquals(true)
12 | pfadd("key", "lol", "foo", "bar").assertEquals(true)
13 | }
14 |
15 | apiTest("PFCOUNT") {
16 | setup(pfadd("key", "lol", "foo", "bar"))
17 | pfcount(Nil).assertEquals(0)
18 | pfcount("key").assert(_ > 0)
19 | }
20 |
21 | apiTest("PFMERGE") {
22 | setup(pfadd("{key}1", "lol", "foo", "bar"), pfadd("{key}2", "omg", "wtf", "baz"))
23 | pfmerge("{key}3", Nil).get
24 | pfmerge("{key}3", "{key}1", "{key}2").get
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/redis/src/test/scala/com/avsystem/commons/redis/commands/NodeOnlyServerApiSuite.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package redis.commands
3 |
4 | import com.avsystem.commons.redis.RedisNodeCommandsSuite
5 |
6 | import scala.concurrent.duration._
7 |
8 | class NodeOnlyServerApiSuite extends RedisNodeCommandsSuite {
9 |
10 | // ignored because of spurious failures
11 | /*
12 | import RedisApi.Batches.StringTyped._
13 |
14 | apiTest("CLIENT KILL") {
15 | val clients: Seq[ClientInfo] = waitFor(clientList.exec)(_.size >= 3, 100.millis).futureValue
16 | clientKill(clients.head.addr).get
17 | clientKill(clients(1).addr, Skipme(false)).assertEquals(1)
18 | clientKill(clients(2).id, Skipme(false)).assertEquals(1)
19 | clientKill(ClientType.Master).assertEquals(0)
20 | }
21 | */
22 | }
23 |
--------------------------------------------------------------------------------
/redis/src/test/scala/com/avsystem/commons/redis/commands/commandSuites.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package redis.commands
3 |
4 | import com.avsystem.commons.redis._
5 |
6 | trait KeyedFullApiSuite extends CommandsSuite
7 | with GeoApiSuite
8 | with KeyedScriptingApiSuite
9 | with KeyedKeysApiSuite
10 | with StringsApiSuite
11 | with HashesApiSuite
12 | with SortedSetsApiSuite
13 | with ListsApiSuite
14 | with SetsApiSuite
15 | with HyperLogLogApiSuite
16 | with StreamsApiSuite
17 |
18 | trait NodeFullApiSuite extends KeyedFullApiSuite
19 | with NodeKeysApiSuite
20 | with ServerApiSuite
21 | with NodeScriptingApiSuite
22 |
23 | trait ConnectionFullApiSuite extends NodeFullApiSuite
24 | with ConnectionScriptingApiSuite
25 |
26 | class RedisClusterCommandsTest extends RedisClusterCommandsSuite with KeyedFullApiSuite
27 | class RedisMasterSlaveCommandsTest extends RedisMasterSlaveCommandsSuite with NodeFullApiSuite
28 | class RedisNodeCommandsTest extends RedisNodeCommandsSuite with NodeFullApiSuite
29 | class RedisConnectionCommandsTest extends RedisConnectionCommandsSuite with ConnectionFullApiSuite
30 |
31 | class RedisTlsNodeCommandsTest extends RedisNodeCommandsSuite with NodeFullApiSuite {
32 | override def useTls: Boolean = true
33 | }
34 | class RedisTlsConnectionCommandsTest extends RedisConnectionCommandsSuite with ConnectionFullApiSuite {
35 | override def useTls: Boolean = true
36 | }
37 |
--------------------------------------------------------------------------------
/redis/src/test/scala/com/avsystem/commons/redis/examples/ClusterClientExample.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package redis.examples
3 |
4 | import org.apache.pekko.actor.ActorSystem
5 | import com.avsystem.commons.redis._
6 |
7 | // Global execution context is used for the sake of simplicity of this example,
8 | // think well if this is what you actually want.
9 | import scala.concurrent.ExecutionContext.Implicits.global
10 |
11 | /**
12 | * Basic example showing how to execute simple command on [[RedisClusterClient]].
13 | */
14 | object ClusterClientExample extends App {
15 | implicit val actorSystem: ActorSystem = ActorSystem()
16 |
17 | // The cluster client asks seed nodes about cluster state (by default local Redis instance is the only seed node)
18 | // and then uses separate RedisNodeClients to connect individually to every master mentioned in cluster state
19 | // that holds some data.
20 | val client = new RedisClusterClient
21 | // We can only execute keyed commands on cluster deployment
22 | val api = RedisApi.Keyed.Async.StringTyped(client)
23 |
24 | // Appropriate master node is automatically chosen for execution based on hash of the key and current cluster slot mapping
25 | api.get("key").onComplete {
26 | case Success(Opt(value)) => println(s"Got value $value")
27 | case Success(Opt.Empty) => println(s"Got no value")
28 | case Failure(t) => t.printStackTrace()
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/redis/src/test/scala/com/avsystem/commons/redis/examples/ConnectionClientExample.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package redis.examples
3 |
4 | import org.apache.pekko.actor.ActorSystem
5 | import com.avsystem.commons.redis._
6 |
7 | // Global execution context is used for the sake of simplicity of this example,
8 | // think well if this is what you actually want.
9 | import scala.concurrent.ExecutionContext.Implicits.global
10 |
11 | /**
12 | * Basic example showing how to execute simple command on [[RedisConnectionClient]].
13 | */
14 | object ConnectionClientExample extends App {
15 | implicit val actorSystem: ActorSystem = ActorSystem()
16 |
17 | // Connection client only uses a single, non-reconnectable connection
18 | val client = new RedisConnectionClient
19 | // but exposes API to manipulate that connection
20 | val api = RedisApi.Connection.Async.StringTyped(client)
21 |
22 | // for example, we can execute CLIENT GETNAME, which we cannot execute using RedisNodeClient
23 | api.clientGetname.onComplete {
24 | case Success(Opt(name)) => println(s"Connection name is $name")
25 | case Success(Opt.Empty) => println("No connection name")
26 | case Failure(t) => t.printStackTrace()
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/redis/src/test/scala/com/avsystem/commons/redis/protocol/CommonsScalacheck.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package redis.protocol
3 |
4 | import scala.annotation.nowarn
5 | import org.scalacheck.Shrink
6 |
7 | /**
8 | * Author: ghik
9 | * Created: 04/04/16.
10 | */
11 | object CommonsScalacheck {
12 | @nowarn("msg=deprecated")
13 | implicit def shrinkOpt[T: Shrink]: Shrink[Opt[T]] = Shrink {
14 | case Opt.Empty => Stream.empty
15 | case Opt(x) => Stream.cons(Opt.Empty, for (y <- Shrink.shrink(x)) yield Opt(y))
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/redis/tls/gen-test-certs.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # based on https://github.com/redis/redis/blob/unstable/utils/gen-test-certs.sh
4 | # Generate some test certificates which are used by the regression test suite:
5 | #
6 | # ca.{crt,key} Self signed CA certificate.
7 | # redis.{crt,key} A certificate with no key usage/policy restrictions.
8 |
9 | generate_cert() {
10 | local name=$1
11 | local cn="$2"
12 | local opts="$3"
13 |
14 | local keyfile=${name}.key
15 | local certfile=${name}.crt
16 |
17 | [ -f $keyfile ] || openssl genrsa -out $keyfile 2048
18 | openssl req \
19 | -new -sha256 \
20 | -subj "/O=Redis Test/CN=$cn" \
21 | -key $keyfile | \
22 | openssl x509 \
23 | -req -sha256 \
24 | -CA ca.crt \
25 | -CAkey ca.key \
26 | -CAserial ca.txt \
27 | -CAcreateserial \
28 | -days 3650 \
29 | $opts \
30 | -out $certfile
31 | }
32 |
33 | [ -f ca.key ] || openssl genrsa -out ca.key 4096
34 | openssl req \
35 | -x509 -new -nodes -sha256 \
36 | -key ca.key \
37 | -days 3650 \
38 | -subj '/O=Redis Test/CN=Certificate Authority' \
39 | -out ca.crt
40 |
41 | generate_cert redis "Generic-cert"
42 |
43 | openssl pkcs12 -export -in redis.crt -inkey redis.key -out redis.p12 -passout pass:
44 |
--------------------------------------------------------------------------------
/redis/tls/redis.crt:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIID6DCCAdACFA7xTsMYbPXDFmFDMEWRlVY2XOrmMA0GCSqGSIb3DQEBCwUAMDUx
3 | EzARBgNVBAoMClJlZGlzIFRlc3QxHjAcBgNVBAMMFUNlcnRpZmljYXRlIEF1dGhv
4 | cml0eTAeFw0yNDA3MTgxMTM2NTBaFw0zNDA3MTYxMTM2NTBaMCwxEzARBgNVBAoM
5 | ClJlZGlzIFRlc3QxFTATBgNVBAMMDEdlbmVyaWMtY2VydDCCASIwDQYJKoZIhvcN
6 | AQEBBQADggEPADCCAQoCggEBAOvSjrrOyHXYl1ASlvBNepT7VA8njdRIjoaVivBU
7 | OgBpnBzQyREAgUhtPTv7/skdux19pRgxMzXXsfGH7LXA9JKDVJWiYnUYVONad6jE
8 | +iMbVm8lRIwURj2RYWgkSqq8MjlbxWMm8yhuJ1X/xCSoV3kgG1/7dO6CM3j5PlUT
9 | HAmeA3Ix3etLcvAS4iT4DStd/2/FTRwptJf3xSE5fx66O+ZcchKoB6nrOAF4y9tU
10 | fBQiXJTNjIlZ6DNDR81PzEPD3VeJ9aK+BupdotwCr1MJYhOYKuMO+F/WUtU1AkBL
11 | pR05qqn4R9pCQ8XFjOUGyJY5xUUcfU/0Efmhu225VQSJffsCAwEAATANBgkqhkiG
12 | 9w0BAQsFAAOCAgEAOAclVDRCj/WUcFARkKbjr94+blEYA3S8GsFjaM2n4npISiVR
13 | rWVhc+9fhmY16gDHbJyoC6izxqYEjWz7wvwejxGRlRnMvP/1T7jTMOJ+YY9/0hox
14 | 3sjSmQgOMtMIvRo02mqMWlPoHV3NzJIpP8mA47cZvTzhJjdOljor5jIE0dj42192
15 | eJu13HoN1Aev3HrIKGOj1DEUGW4gpAQA8FNha2ROs63xyrXTt1gkXGbYI3koPSt2
16 | mJsBY0axZwAHxuM/LP38uV71zQVOduLdHTlppDUjuNabA5tac/A1kkSgFymnvGJ5
17 | 1J6cVdaveV7y9CnGXbFc1T6mFwJvsBsfki9SyVFvO9ieGT7i/CF6yh2cZemyIEKh
18 | MrnmnpdZX8NjM4g6voCH+8klI/irG8rnV/c6vsi73x4qIREerAcsEzAUsvzOrFov
19 | XWkDRX/PRnFFiZIEBEtoSXLIICPaNzRmWJ/OJHLr/9Dh61BZBFdlSgnq0RYIIGMP
20 | bfPIkZy0jmWfIljjwpHLocbSY5NdL+ZPeMHzoisiBNKfRAd0yHsh7DWiX57vFVwi
21 | OXMiITpNj/iNDWX1TzSVi7vtr6cBDE/2jhzvY4yjfZBFG2TudPGlcHmXLN27Enll
22 | YiAYnZ8PrH1zWT/xITdPcVlMyEwEyXMHWwjsgZR2vYzyCO0LS13oHWnhnIo=
23 | -----END CERTIFICATE-----
24 |
--------------------------------------------------------------------------------
/redis/tls/redis.p12:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AVSystem/scala-commons/2a951ce738dd0fa4387e5095e466b4df7505b024/redis/tls/redis.p12
--------------------------------------------------------------------------------
/spring/src/test/resources/bean1.conf:
--------------------------------------------------------------------------------
1 | beans {
2 | someBean {
3 | %class = hoconspring.TestBean
4 | stuff = 50
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/spring/src/test/resources/bean2.conf:
--------------------------------------------------------------------------------
1 | beans {
2 | someBean.dafuq = shietlol
3 | }
4 |
--------------------------------------------------------------------------------
/spring/src/test/resources/beans.conf:
--------------------------------------------------------------------------------
1 | beans {
2 | someAbstractBean {
3 | %abstract = true
4 |
5 | settings {
6 | inherited = 32
7 | }
8 | characters.%set = [a, g, o]
9 | }
10 |
11 | someBean {
12 | %class = com.example.SomeBeanClass
13 | %parent = someAbstractBean
14 |
15 | someNumberProperty = 42
16 | someStringProperty = someStringValue
17 | settings {
18 | %merge = true
19 | someSetting = 5
20 | anotherSetting = 20
21 | }
22 | numbers = [1, 2, 3, 4, 5]
23 | characters {
24 | %merge = true
25 | %set = [a, b, c, d]
26 | }
27 | nestedBean {
28 | %class = com.example.SomeOtherBeanClass
29 |
30 | otherNestedBean.%ref = otherBean
31 | }
32 | }
33 |
34 | otherBean {
35 | %class = com.example.YetAnotherBeanClass
36 |
37 | someRandomProperty = someRandomString
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/spring/src/test/resources/conditionalInclude.conf:
--------------------------------------------------------------------------------
1 | beans {
2 | beanFromConditional = {
3 | %class = com.avsystem.commons.spring.ConditionalTestBean, %construct = true
4 | int = 100
5 | }
6 | }
--------------------------------------------------------------------------------
/spring/src/test/resources/conditionalsDisabled.conf:
--------------------------------------------------------------------------------
1 | featureFlag.enabled = false
2 |
3 | beans.beanFromConditional = null
4 |
5 | conditionals = [
6 | {condition: ${featureFlag.enabled}, config: {include "conditionalInclude.conf"}},
7 | ]
8 |
--------------------------------------------------------------------------------
/spring/src/test/resources/conditionalsEnabled.conf:
--------------------------------------------------------------------------------
1 | featureFlag.enabled = true
2 |
3 | beans.beanFromConditional = null
4 |
5 | conditionals = [
6 | {condition: ${featureFlag.enabled}, config: {include "conditionalInclude.conf"}},
7 | ]
8 |
--------------------------------------------------------------------------------
/spring/src/test/resources/conditionalsNested.conf:
--------------------------------------------------------------------------------
1 | featureFlag.enabled = true
2 |
3 | beans {
4 | testBean {
5 | %class = com.avsystem.commons.spring.TestBean
6 | }
7 | }
8 |
9 | conditionals = [
10 | {condition: ${featureFlag.enabled}, config: {beans.testBean.int = 0}},
11 | {condition: ${featureFlag.enabled}, config: {beans.testBean.int = 1}},
12 | {
13 | condition: ${featureFlag.enabled}, config: {
14 | conditionals = [
15 | {condition: true, config: {beans.testBean.int = 2}}
16 | ]
17 | }
18 | },
19 | {condition: false, config: {beans.testBean.int = 3}},
20 | ]
--------------------------------------------------------------------------------
/spring/src/test/resources/testBean.conf:
--------------------------------------------------------------------------------
1 | abstract {
2 | testBean {
3 | %class = com.avsystem.commons.spring.TestBean
4 | }
5 | constrTestBean = ${abstract.testBean} {
6 | %construct = true
7 | }
8 | fmTestBean = ${abstract.constrTestBean} {
9 | %factory-method = create
10 | }
11 | }
12 |
13 | beans {
14 | testBean = ${abstract.testBean} {
15 | %constructor-args = [42, lolzsy]
16 | int = 5
17 | string = lol
18 | strIntMap {
19 | fuu = 42
20 | }
21 | strList = [a, b]
22 | strSet = [A, B]
23 | nestedBean = ${abstract.testBean} {
24 | %constructor-args {
25 | constrString = wut
26 | constrInt = 1
27 | }
28 | int = 6
29 | nestedBean = ${abstract.constrTestBean} {
30 | constrString = yes
31 | constrInt = 2
32 | }
33 | }
34 | config.%config {
35 | srsly = dafuq
36 | }
37 | }
38 |
39 | testBeanDefInt = ${abstract.constrTestBean} {
40 | constrString = constrNonDefault
41 | }
42 |
43 | testBeanDefString = ${abstract.constrTestBean} {
44 | constrInt = 2
45 | }
46 |
47 | testBeanDefAll = ${abstract.constrTestBean}
48 |
49 | testBeanFMDefInt = ${abstract.fmTestBean} {
50 | theString = factoryNonDefault
51 | }
52 |
53 | testBeanFMDefString = ${abstract.fmTestBean} {
54 | theInt = -2
55 | }
56 |
57 | testBeanFMDefAll = ${abstract.fmTestBean}
58 | }
59 |
--------------------------------------------------------------------------------
/spring/src/test/scala/com/avsystem/commons/spring/Playground.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package spring
3 |
4 | import com.typesafe.config.ConfigFactory
5 |
6 |
7 | object Playground {
8 | def main(args: Array[String]): Unit = {
9 | val conf =
10 | """
11 | |a = {b += 1}
12 | |a = ${a}{b += 2}
13 | """.stripMargin
14 |
15 | println(ConfigFactory.parseString(conf).resolve().root().render())
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/spring/src/test/scala/com/avsystem/commons/spring/XmlPlayground.scala:
--------------------------------------------------------------------------------
1 | package com.avsystem.commons
2 | package spring
3 |
4 | import com.typesafe.config.ConfigFactory
5 | import org.springframework.context.support.GenericApplicationContext
6 |
7 | object XmlPlayground {
8 | def main(args: Array[String]): Unit = {
9 | val ctx = new GenericApplicationContext
10 | val reader = new HoconBeanDefinitionReader(ctx)
11 | reader.loadBeanDefinitions(ConfigFactory.load("bean2").withFallback(ConfigFactory.load("bean1")).resolve())
12 |
13 | ctx.refresh()
14 | }
15 | }
16 |
--------------------------------------------------------------------------------