├── .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 | 20 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 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 | --------------------------------------------------------------------------------