From 0d55bd945766d8642f7aa221f92c08f7c3d1c992 Mon Sep 17 00:00:00 2001 From: Alexander Kiel Date: Fri, 10 Oct 2025 18:03:59 +0200 Subject: [PATCH] Implement FHIR Types in Java --- .github/distributed-test/docker-compose.yml | 2 - .github/scripts/age-test.sh | 57 + .github/scripts/count-test.sh | 35 + .github/scripts/distance-test.sh | 36 + .github/scripts/duration-test.sh | 57 + .github/workflows/build.yml | 52 +- Dockerfile | 5 +- cljfmt.edn | 1 + docs/deployment/distributed-backend.md | 1 - .../deployment/distributed/docker-compose.yml | 2 - docs/deployment/environment-variables.md | 20 +- docs/performance/fhir-search.md | 2 +- docs/performance/import.md | 2 +- docs/production-configuration.md | 21 +- modules/admin-api/src/blaze/admin_api.clj | 10 +- .../admin-api/test/blaze/admin_api_test.clj | 4 +- modules/byte-buffer/build.clj | 2 +- modules/coll/build.clj | 2 +- modules/cql/Makefile | 2 +- .../src/blaze/elm/compiler/external_data.clj | 13 +- .../src/blaze/elm/compiler/reusing_logic.clj | 43 +- .../blaze/elm/compiler/structured_values.clj | 47 +- .../src/blaze/elm/compiler/type_operators.clj | 5 +- modules/cql/src/blaze/elm/ts_util.clj | 4 +- .../blaze/elm/compiler/external_data_test.clj | 38 +- .../test/blaze/elm/compiler/library_test.clj | 2 +- .../test/blaze/elm/compiler/queries_test.clj | 76 +- .../blaze/elm/compiler/reusing_logic_test.clj | 64 +- .../elm/compiler/structured_values_test.clj | 35 +- .../elm/compiler/type_operators_test.clj | 12 +- modules/cql/test/blaze/elm/date_time_test.clj | 14 +- modules/db/build.clj | 2 +- modules/db/src/blaze/db/impl/search_param.clj | 6 +- .../src/blaze/db/impl/search_param/date.clj | 12 +- .../src/blaze/db/impl/search_param/near.clj | 13 +- .../src/blaze/db/impl/search_param/number.clj | 10 +- .../blaze/db/impl/search_param/quantity.clj | 41 +- .../src/blaze/db/impl/search_param/string.clj | 6 +- .../src/blaze/db/impl/search_param/token.clj | 100 +- .../src/blaze/db/impl/search_param/util.clj | 3 +- modules/db/src/blaze/db/node.clj | 2 +- .../db/src/blaze/db/node/resource_indexer.clj | 12 +- modules/db/src/blaze/db/node/transaction.clj | 5 +- modules/db/src/blaze/db/node/util.clj | 10 +- modules/db/src/blaze/db/resource_cache.clj | 99 +- .../src/blaze/db/resource_cache/protocol.clj | 2 - .../db/src/blaze/db/resource_cache/spec.clj | 4 +- .../db/test-perf/blaze/db/api_test_perf.clj | 2 +- modules/db/test/blaze/db/api_test.clj | 381 +- .../db/test/blaze/db/impl/codec/date_test.clj | 6 +- .../db/impl/search_param/chained_test.clj | 1 - .../composite/token_quantity_test.clj | 1 - .../composite/token_token_test.clj | 1 - .../db/impl/search_param/composite_test.clj | 1 - .../blaze/db/impl/search_param/date_test.clj | 14 +- .../blaze/db/impl/search_param/has_test.clj | 1 - .../db/impl/search_param/number_test.clj | 1 - .../db/impl/search_param/quantity_test.clj | 1 - .../blaze/db/impl/search_param/token_test.clj | 2 +- .../blaze/db/impl/search_param/util_test.clj | 2 +- .../test/blaze/db/impl/search_param_test.clj | 1 - .../blaze/db/node/resource_indexer_test.clj | 5 +- .../test/blaze/db/node/transaction_test.clj | 1 - .../blaze/db/node/tx_indexer/expand_test.clj | 5 +- .../blaze/db/node/tx_indexer/verify_test.clj | 1 - .../db/test/blaze/db/resource_cache_test.clj | 140 +- .../src/blaze/fhir_client/impl.clj | 5 +- modules/fhir-path/src/blaze/fhir_path.clj | 77 +- .../fhir-path/src/blaze/fhir_path_spec.clj | 3 +- .../fhir-path/test/blaze/fhir_path_test.clj | 192 +- modules/fhir-structure/Makefile | 7 +- modules/fhir-structure/build.clj | 2 +- modules/fhir-structure/deps.edn | 5 +- .../fhir-structure/java/blaze/Interner.java | 6 + .../fhir-structure/java/blaze/Interners.java | 76 + .../fhir-structure/java/blaze/fhir/Hash.java | 4 +- .../spec/type/AbstractBackboneElement.java | 42 + .../blaze/fhir/spec/type/AbstractElement.java | 59 + .../fhir/spec/type/AbstractQuantity.java | 210 + .../java/blaze/fhir/spec/type/Address.java | 367 ++ .../java/blaze/fhir/spec/type/Age.java | 71 + .../java/blaze/fhir/spec/type/Annotation.java | 197 + .../spec/type/AsciiByteArrayAppendable.java | 52 + .../java/blaze/fhir/spec/type/Attachment.java | 314 + .../java/blaze/fhir/spec/type/Base.java | 332 ++ .../blaze/fhir/spec/type/Base64Binary.java | 122 + .../blaze/fhir/spec/type/BaseIterator.java | 49 + .../java/blaze/fhir/spec/type/Boolean.java | 140 + .../fhir/spec/type/BundleEntrySearch.java | 160 + .../java/blaze/fhir/spec/type/Canonical.java | 147 + .../java/blaze/fhir/spec/type/Code.java | 160 + .../blaze/fhir/spec/type/CodeableConcept.java | 200 + .../java/blaze/fhir/spec/type/Coding.java | 265 + .../java/blaze/fhir/spec/type/Complex.java | 32 + .../blaze/fhir/spec/type/ContactDetail.java | 172 + .../blaze/fhir/spec/type/ContactPoint.java | 236 + .../blaze/fhir/spec/type/Contributor.java | 192 + .../java/blaze/fhir/spec/type/Count.java | 71 + .../blaze/fhir/spec/type/DataRequirement.java | 921 +++ .../java/blaze/fhir/spec/type/Date.java | 121 + .../java/blaze/fhir/spec/type/DateTime.java | 128 + .../java/blaze/fhir/spec/type/Decimal.java | 122 + .../java/blaze/fhir/spec/type/Distance.java | 71 + .../java/blaze/fhir/spec/type/Dosage.java | 652 +++ .../java/blaze/fhir/spec/type/Duration.java | 71 + .../java/blaze/fhir/spec/type/Element.java | 31 + .../java/blaze/fhir/spec/type/Expression.java | 242 + .../java/blaze/fhir/spec/type/Extension.java | 198 + .../blaze/fhir/spec/type/ExtensionData.java | 146 + .../blaze/fhir/spec/type/ExtensionValue.java | 6 + .../java/blaze/fhir/spec/type/FieldName.java | 24 + .../java/blaze/fhir/spec/type/HumanName.java | 296 + .../java/blaze/fhir/spec/type/Id.java | 121 + .../java/blaze/fhir/spec/type/Identifier.java | 261 + .../java/blaze/fhir/spec/type/Instant.java | 129 + .../java/blaze/fhir/spec/type/Integer.java | 151 + .../java/blaze/fhir/spec/type/Integer64.java | 105 + .../java/blaze/fhir/spec/type/Lists.java | 33 + .../java/blaze/fhir/spec/type/Markdown.java | 121 + .../java/blaze/fhir/spec/type/Meta.java | 311 + .../java/blaze/fhir/spec/type/Money.java | 197 + .../java/blaze/fhir/spec/type/Oid.java | 121 + .../fhir/spec/type/ParameterDefinition.java | 288 + .../java/blaze/fhir/spec/type/Period.java | 181 + .../blaze/fhir/spec/type/PositiveInt.java | 134 + .../java/blaze/fhir/spec/type/Primitive.java | 81 + .../fhir/spec/type/PrimitiveElement.java | 48 + .../java/blaze/fhir/spec/type/Quantity.java | 71 + .../java/blaze/fhir/spec/type/Range.java | 181 + .../java/blaze/fhir/spec/type/Ratio.java | 181 + .../java/blaze/fhir/spec/type/RatioRange.java | 198 + .../java/blaze/fhir/spec/type/Reference.java | 264 + .../blaze/fhir/spec/type/RelatedArtifact.java | 290 + .../blaze/fhir/spec/type/SampledData.java | 290 + .../java/blaze/fhir/spec/type/Signature.java | 293 + .../java/blaze/fhir/spec/type/String.java | 258 + .../java/blaze/fhir/spec/type/Time.java | 132 + .../java/blaze/fhir/spec/type/Timing.java | 660 +++ .../fhir/spec/type/TriggerDefinition.java | 261 + .../blaze/fhir/spec/type/UnsignedInt.java | 134 + .../java/blaze/fhir/spec/type/Uri.java | 259 + .../java/blaze/fhir/spec/type/Url.java | 124 + .../blaze/fhir/spec/type/UsageContext.java | 184 + .../java/blaze/fhir/spec/type/Uuid.java | 128 + .../java/blaze/fhir/spec/type/Xhtml.java | 124 + .../blaze/fhir/spec/type/system/Booleans.java | 12 + .../blaze/fhir/spec/type/system/Date.java | 4 +- .../blaze/fhir/spec/type/system/DateDate.java | 28 +- .../blaze/fhir/spec/type/system/DateTime.java | 3 +- .../fhir/spec/type/system/DateTimeDate.java | 27 +- .../fhir/spec/type/system/DateTimeYear.java | 26 +- .../spec/type/system/DateTimeYearMonth.java | 27 +- .../fhir/spec/type/system/DateTimes.java | 71 + .../blaze/fhir/spec/type/system/DateYear.java | 26 +- .../fhir/spec/type/system/DateYearMonth.java | 27 +- .../blaze/fhir/spec/type/system/Decimals.java | 23 + .../blaze/fhir/spec/type/system/Integers.java | 12 + .../fhir/spec/type/system/JavaSystemType.java | 6 +- .../blaze/fhir/spec/type/system/Longs.java | 12 + .../blaze/fhir/spec/type/system/Strings.java | 27 + .../blaze/fhir/spec/type/system/Times.java | 30 + .../fhir-structure/resources/data_readers.clj | 45 +- .../fhir-structure/src/blaze/fhir/hash.clj | 6 +- .../fhir-structure/src/blaze/fhir/spec.clj | 32 +- .../src/blaze/fhir/spec/impl.clj | 422 +- .../src/blaze/fhir/spec/impl/intern.clj | 38 - .../src/blaze/fhir/spec/impl/specs.clj | 2 +- .../src/blaze/fhir/spec/impl/xml.clj | 18 +- .../src/blaze/fhir/spec/resource.clj | 429 +- .../src/blaze/fhir/spec/spec.clj | 3 + .../src/blaze/fhir/spec/type.clj | 1823 ++---- .../src/blaze/fhir/spec/type/json.clj | 76 - .../src/blaze/fhir/spec/type/macros.clj | 404 -- .../src/blaze/fhir/spec/type/protocols.clj | 34 - .../src/blaze/fhir/spec/type/system.clj | 142 +- .../src/blaze/fhir/spec/type/system/spec.clj | 24 +- .../src/blaze/fhir/spec/type_spec.clj | 183 +- .../src/blaze/fhir/spec_spec.clj | 22 +- .../blaze/fhir/structure_definition_repo.clj | 3 +- .../fhir/structure_definition_repo_spec.clj | 9 +- .../fhir-structure/src/blaze/fhir/util.clj | 17 +- .../src/blaze/fhir/writing_context.clj | 52 +- .../test-perf/blaze/fhir/hash_test_perf.clj | 16 +- .../test-perf/blaze/fhir/spec/memory.clj | 21 - .../blaze/fhir/spec/type_test_mem.clj | 285 +- .../test-perf/blaze/fhir/spec_test_perf.clj | 60 +- .../test/blaze/fhir/spec/generators.clj | 1026 +++- .../test/blaze/fhir/spec/impl/intern_test.clj | 44 - .../test/blaze/fhir/spec/impl_test.clj | 68 +- .../test/blaze/fhir/spec/memory.clj | 67 + .../test/blaze/fhir/spec/resource_test.clj | 284 +- .../test/blaze/fhir/spec/type/json_test.clj | 48 - .../test/blaze/fhir/spec/type/system_test.clj | 438 +- .../test/blaze/fhir/spec/type_test.clj | 3353 ++++++----- .../test/blaze/fhir/spec_test.clj | 5095 ++++++++++++----- .../test/blaze/fhir/util_test.clj | 7 +- .../src/blaze/fhir/spec/generators.clj | 1026 +++- .../src/blaze/fhir/test_util.clj | 3 +- modules/frontend-e2e/docker-compose.yml | 1 - .../src/blaze/interaction/history/util.clj | 5 +- .../src/blaze/interaction/transaction.clj | 22 +- .../blaze/interaction/transaction/bundle.clj | 14 +- .../interaction/transaction/bundle/links.clj | 22 +- .../test/blaze/interaction/create_test.clj | 13 +- .../interaction/history/instance_test.clj | 26 +- .../blaze/interaction/history/system_test.clj | 20 +- .../blaze/interaction/history/type_test.clj | 20 +- .../blaze/interaction/history/util_test.clj | 16 +- .../test/blaze/interaction/read_test.clj | 6 +- .../interaction/search_compartment_test.clj | 5 +- .../blaze/interaction/search_system_test.clj | 12 +- .../blaze/interaction/search_type_test.clj | 37 +- .../test/blaze/interaction/test_util.clj | 5 +- .../transaction/bundle/links_test.clj | 3 +- .../interaction/transaction/bundle_test.clj | 20 +- .../blaze/interaction/transaction_test.clj | 98 +- .../test/blaze/interaction/update_test.clj | 38 +- .../test/blaze/interaction/vread_test.clj | 6 +- modules/jepsen/src/blaze/jepsen/register.clj | 2 +- .../src/blaze/jepsen/resource_history.clj | 2 +- .../src/blaze/job/async_interaction.clj | 12 +- .../src/blaze/job/async_interaction/util.clj | 6 +- .../blaze/job/async_interaction/util_test.clj | 7 +- .../test/blaze/job/async_interaction_spec.clj | 2 +- .../test/blaze/job/async_interaction_test.clj | 17 +- modules/job-compact/src/blaze/job/compact.clj | 20 +- .../src/blaze/job/compact_spec.clj | 2 +- .../test/blaze/job/compact_test.clj | 5 +- .../job-re-index/src/blaze/job/re_index.clj | 19 +- .../test/blaze/job/re_index_test.clj | 19 +- .../job-scheduler/src/blaze/job_scheduler.clj | 45 +- .../job-test-util/src/blaze/job/test_util.clj | 11 +- modules/job-util/src/blaze/job/util.clj | 18 +- modules/job-util/test/blaze/job/util_test.clj | 2 +- modules/kv/build.clj | 2 +- .../code_system/validate_code_test.clj | 4 +- .../src/blaze/fhir/operation/compact.clj | 3 +- .../src/blaze/operation/graph.clj | 2 +- .../src/blaze/operation/graph/compiler.clj | 9 +- .../test/blaze/operation/graph_test.clj | 27 +- .../blaze/fhir/operation/graphql_test.clj | 4 +- .../operation/evaluate_measure/measure.clj | 41 +- .../evaluate_measure/measure/stratifier.clj | 39 +- .../evaluate_measure/measure/util.clj | 8 +- .../evaluate_measure/middleware/params.clj | 26 +- .../operation/evaluate_measure/cql_test.clj | 1 - .../measure/stratifier_test.clj | 39 +- .../evaluate_measure/measure/util_test.clj | 23 +- .../evaluate_measure/measure_test.clj | 27 +- .../middleware/params_test.clj | 18 +- .../fhir/operation/evaluate_measure_test.clj | 59 +- .../operation/patient/everything_test.clj | 75 +- .../fhir/operation/value_set/expand_test.clj | 2 +- .../operation/value_set/validate_code.clj | 2 +- .../value_set/validate_code_test.clj | 16 +- .../src/blaze/page_id_cipher.clj | 6 +- .../blaze/rest_api/async_status_handler.clj | 3 +- .../blaze/rest_api/capabilities_handler.clj | 7 +- .../blaze/rest_api/middleware/auth_guard.clj | 2 +- .../blaze/rest_api/structure_definitions.clj | 13 +- .../rest_api/capabilities_handler_test.clj | 12 +- modules/rest-api/test/blaze/rest_api_test.clj | 3 +- .../src/blaze/fhir/response/create.clj | 10 +- .../rest-util/src/blaze/handler/fhir/util.clj | 18 +- modules/rest-util/src/blaze/handler/util.clj | 8 +- .../rest-util/src/blaze/handler/util_spec.clj | 4 + .../src/blaze/interaction/search/util.clj | 8 +- .../src/blaze/middleware/fhir/output.clj | 7 +- .../src/blaze/middleware/link_headers.clj | 5 +- .../test/blaze/handler/fhir/util_test.clj | 9 +- .../blaze/interaction/search/util_test.clj | 6 +- .../blaze/middleware/fhir/resource_test.clj | 13 +- modules/server/build.clj | 2 +- modules/spec/src/blaze/spec.clj | 3 +- .../src/blaze/terminology_service/local.clj | 69 +- .../terminology_service/local/code_system.clj | 25 +- .../local/code_system/bcp_13.clj | 8 +- .../local/code_system/bcp_47.clj | 8 +- .../local/code_system/core.clj | 16 +- .../local/code_system/default.clj | 22 +- .../local/code_system/filter/core.clj | 10 +- .../code_system/filter/descendent_of.clj | 31 +- .../local/code_system/filter/equals.clj | 13 +- .../local/code_system/filter/exists.clj | 15 +- .../local/code_system/filter/is_a.clj | 30 +- .../local/code_system/filter/regex.clj | 19 +- .../local/code_system/loinc.clj | 9 +- .../local/code_system/loinc/context.clj | 28 +- .../local/code_system/loinc/filter/core.clj | 5 +- .../local/code_system/loinc/filter/equals.clj | 57 +- .../local/code_system/loinc/filter/regex.clj | 45 +- .../local/code_system/sct.clj | 16 +- .../local/code_system/sct/context.clj | 9 +- .../local/code_system/sct/filter/core.clj | 5 +- .../code_system/sct/filter/descendent_of.clj | 15 +- .../local/code_system/sct/filter/equals.clj | 21 +- .../local/code_system/sct/filter/is_a.clj | 15 +- .../local/code_system/ucum.clj | 10 +- .../local/code_system/util.clj | 9 +- .../blaze/terminology_service/local/graph.clj | 8 +- .../local/validate_code.clj | 29 +- .../terminology_service/local/value_set.clj | 10 +- .../local/value_set/expand.clj | 47 +- .../local/value_set/loinc.clj | 6 +- .../local/value_set/util.clj | 3 +- .../local/value_set/validate_code.clj | 41 +- .../local/value_set/validate_code/issue.clj | 18 +- .../local/code_system/sct/context_test.clj | 10 +- .../value_set/validate_code/issue_test.clj | 5 +- .../blaze/terminology_service/local_test.clj | 38 +- modules/test-util/src/blaze/test_util.clj | 2 +- modules/util/build.clj | 2 +- resources/blaze.edn | 9 +- src/blaze/core.clj | 2 +- src/blaze/system.clj | 3 +- test/blaze/system_test.clj | 3 +- 316 files changed, 24694 insertions(+), 8052 deletions(-) create mode 100755 .github/scripts/age-test.sh create mode 100755 .github/scripts/count-test.sh create mode 100755 .github/scripts/distance-test.sh create mode 100755 .github/scripts/duration-test.sh create mode 100644 modules/fhir-structure/java/blaze/Interner.java create mode 100644 modules/fhir-structure/java/blaze/Interners.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/AbstractBackboneElement.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/AbstractElement.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/AbstractQuantity.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Address.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Age.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Annotation.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/AsciiByteArrayAppendable.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Attachment.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Base.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Base64Binary.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/BaseIterator.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Boolean.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/BundleEntrySearch.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Canonical.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Code.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/CodeableConcept.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Coding.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Complex.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/ContactDetail.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/ContactPoint.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Contributor.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Count.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/DataRequirement.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Date.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/DateTime.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Decimal.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Distance.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Dosage.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Duration.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Element.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Expression.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Extension.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/ExtensionData.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/ExtensionValue.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/FieldName.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/HumanName.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Id.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Identifier.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Instant.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Integer.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Integer64.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Lists.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Markdown.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Meta.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Money.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Oid.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/ParameterDefinition.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Period.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/PositiveInt.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Primitive.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/PrimitiveElement.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Quantity.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Range.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Ratio.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/RatioRange.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Reference.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/RelatedArtifact.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/SampledData.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Signature.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/String.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Time.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Timing.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/TriggerDefinition.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/UnsignedInt.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Uri.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Url.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/UsageContext.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Uuid.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/Xhtml.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/system/Booleans.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/system/DateTimes.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/system/Decimals.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/system/Integers.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/system/Longs.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/system/Strings.java create mode 100644 modules/fhir-structure/java/blaze/fhir/spec/type/system/Times.java delete mode 100644 modules/fhir-structure/src/blaze/fhir/spec/impl/intern.clj delete mode 100644 modules/fhir-structure/src/blaze/fhir/spec/type/json.clj delete mode 100644 modules/fhir-structure/src/blaze/fhir/spec/type/macros.clj delete mode 100644 modules/fhir-structure/src/blaze/fhir/spec/type/protocols.clj delete mode 100644 modules/fhir-structure/test-perf/blaze/fhir/spec/memory.clj delete mode 100644 modules/fhir-structure/test/blaze/fhir/spec/impl/intern_test.clj create mode 100644 modules/fhir-structure/test/blaze/fhir/spec/memory.clj delete mode 100644 modules/fhir-structure/test/blaze/fhir/spec/type/json_test.clj diff --git a/.github/distributed-test/docker-compose.yml b/.github/distributed-test/docker-compose.yml index 2672cc145..bb258fb02 100644 --- a/.github/distributed-test/docker-compose.yml +++ b/.github/distributed-test/docker-compose.yml @@ -122,7 +122,6 @@ services: DB_CASSANDRA_REQUEST_TIMEOUT: "60000" DB_CASSANDRA_MAX_CONCURRENT_REQUESTS: "128" DB_BLOCK_CACHE_SIZE: "128" - DB_RESOURCE_CACHE_SIZE: "100000" DB_RESOURCE_INDEXER_THREADS: "8" ALLOW_MULTIPLE_DELETE: "true" ENABLE_INTERACTION_DELETE_HISTORY: "true" @@ -156,7 +155,6 @@ services: DB_CASSANDRA_REQUEST_TIMEOUT: "60000" DB_CASSANDRA_MAX_CONCURRENT_REQUESTS: "128" DB_BLOCK_CACHE_SIZE: "128" - DB_RESOURCE_CACHE_SIZE: "100000" DB_RESOURCE_INDEXER_THREADS: "8" ALLOW_MULTIPLE_DELETE: "true" ENABLE_INTERACTION_DELETE_HISTORY: "true" diff --git a/.github/scripts/age-test.sh b/.github/scripts/age-test.sh new file mode 100755 index 000000000..f89e3c31d --- /dev/null +++ b/.github/scripts/age-test.sh @@ -0,0 +1,57 @@ +#!/bin/bash -e + +# +# This script test that the complex data type Age works as expected +# + +script_dir="$(dirname "$(readlink -f "$0")")" +. "$script_dir/util.sh" + +base="http://localhost:8080/fhir" + +echo "ℹ️ Age as polymorph data element in Condition" + +condition() { +cat < +#### `DB_RESOURCE_CACHE_SIZE` -The size of the resource cache of the DB in number of resources. +The size of the resource cache of the DB in number of resources. (Deprecated) **Default:** 100000 +#### `DB_RESOURCE_CACHE_SIZE_RATIO` + +The ratio of JVM heap size that is allocated to the resource cache. A value of 0 disables the resource cache and a value of 0.8 is the maximum. + +**Default:** 0.25 + #### `DB_SCALE_FACTOR` Scales sizes of DB in-memory buffers and SST files. See also: [Production Configuration](../production-configuration.md#db-scale-factor). @@ -166,12 +172,18 @@ The size of the [block cache][2] of the DB in MiB. This cache is outside of the **Default:** 128 -#### `DB_RESOURCE_CACHE_SIZE` +#### `DB_RESOURCE_CACHE_SIZE` -The size of the resource cache of the DB in number of resources. +The size of the resource cache of the DB in number of resources. (Deprecated) **Default:** 100000 +#### `DB_RESOURCE_CACHE_SIZE_RATIO` + +The ratio of JVM heap size that is allocated to the resource cache. A value of 0 disables the resource cache and a value of 0.8 is the maximum. + +**Default:** 0.25 + #### `DB_MAX_BACKGROUND_JOBS` The maximum number of the [background jobs][3] used for DB compactions. diff --git a/docs/performance/fhir-search.md b/docs/performance/fhir-search.md index 4b98fff3e..f84680639 100644 --- a/docs/performance/fhir-search.md +++ b/docs/performance/fhir-search.md @@ -702,7 +702,7 @@ The dataset was generated with Synthea v3.1.1. The resource generation process i ## Controlling and Monitoring the Caches -The size of the resource cache can be set with the `DB_RESOURCE_CACHE_SIZE` environment variable, which specifies the number of resources to cache. It is important to know the memory footprint of a resource, as it can vary widely. Monitoring heap usage is critical. +The size of the resource cache can be set with the `DB_RESOURCE_CACHE_SIZE_RATIO` environment variable, which specifies the ratio of JVM heap size that is allocated to the resource cache. ### Monitoring diff --git a/docs/performance/import.md b/docs/performance/import.md index b62fd2120..984b2b57d 100644 --- a/docs/performance/import.md +++ b/docs/performance/import.md @@ -43,6 +43,6 @@ blazectl --server http://localhost:8080/fhir upload -c8 | LEA58 | 100k-fh | 1 | 7.426 | 11843 | | LEA58 | 1M | 4 | 26,480 | 10955 | | LEA79 | 1M | 1 | 18.227 | 15915 | -| LEA79 | 1M | 4 | 12.583 | 23053 | +| LEA79 | 1M | 4 | 12.174 | 23829 | Read more about the DB Scale Factor in the [Environment Variables Section](../deployment/environment-variables.md). diff --git a/docs/production-configuration.md b/docs/production-configuration.md index f264aa9cb..dc03f7952 100644 --- a/docs/production-configuration.md +++ b/docs/production-configuration.md @@ -23,14 +23,14 @@ aside: false The following tables lists the minimum recommended system resources based on patient count, assuming approximately 1,000 resources per patient. Each configuration should be dedicated to a single Blaze instance. Avoid running other memory-intensive processes on the same system. -| # Patients | Cores | RAM | SSD | Heap Mem | Block Cache | Resource Cache | DB Scale Factor | CQL Cache | -|-----------:|------:|--------:|-------:|---------:|------------:|---------------:|----------------:|----------:| -| 10 k | 2 | 8 GiB | 100 GB | 2 GiB | 2 GiB | 0.25 M | 1 | 128 MiB | -| < 50 k | 4 | 16 GiB | 250 GB | 4 GiB | 4 GiB | 0.5 M | 1 | 128 MiB | -| < 100 k | 4 | 32 GiB | 500 GB | 8 GiB | 8 GiB | 1.25 M | 2 | 512 MiB | -| 100 k | 8 | 64 GiB | 1 TB | 16 GiB | 16 GiB | 2.5 M | 2 | 512 MiB | -| 1 M | 16 | 128 GiB | 2 TB | 32 GiB | 32 GiB | 5 M | 4 | 1 GiB | -| > 1 M | 32 | 256 GiB | 4 TB | 64 GiB | 64 GiB | 10 M | 4 | 1 GiB | +| # Patients | Cores | RAM | SSD | Heap Mem | Block Cache | DB Scale Factor | CQL Cache | +|-----------:|------:|--------:|-------:|---------:|------------:|----------------:|----------:| +| 10 k | 2 | 8 GiB | 100 GB | 2 GiB | 2 GiB | 1 | 128 MiB | +| < 50 k | 4 | 16 GiB | 250 GB | 4 GiB | 4 GiB | 1 | 128 MiB | +| < 100 k | 4 | 32 GiB | 500 GB | 8 GiB | 8 GiB | 2 | 512 MiB | +| 100 k | 8 | 64 GiB | 1 TB | 16 GiB | 16 GiB | 2 | 512 MiB | +| 1 M | 16 | 128 GiB | 2 TB | 32 GiB | 32 GiB | 4 | 1 GiB | +| > 1 M | 32 | 256 GiB | 4 TB | 64 GiB | 64 GiB | 4 | 1 GiB | ### Memory Allocation Strategy @@ -39,8 +39,6 @@ In general, available RAM should be distributed as follows: * **25%** → RocksDB block cache * **50%** → OS page cache (for RocksDB database file access) -The resource cache is configured by the number of resources instead of the amount of memory. The resource numbers given assume a certain resource size taken from [Synthea][1] resources. For fine-tuning that number, the Metric `JVM Memory Used by Pool` should be used. - > [!important] > Leave half of the available system memory "free" for the OS page cache > See [System Memory](#memory) @@ -70,7 +68,6 @@ The list of all environment variables can be found in the [Environment Variables |:-------------------------------|-------------------------|:--------|:------------------------------------------------------| | `JAVA_TOOL_OPTIONS` | Heap Mem | — | -Xmx2g, -Xmx4g, -Xmx8g, -Xmx16g, -Xmx32g or -Xmx64g | | `DB_BLOCK_CACHE_SIZE` | Block Cache | 128 | 2048, 4096, 8192, 16384, 32768 or 65536 (in megabyte) | -| `DB_RESOURCE_CACHE_SIZE` | Resource Cache | 100000 | 250000, 500000, 1250000, 2500000, 5000000 or 10000000 | | `DB_SCALE_FACTOR` | DB Buffers/File Sizes | 1 | 1, 2, 4, 8 or 16 | | `CQL_EXPR_CACHE_SIZE` | CQL Expression Cache | — | 128, 512, 1024 (in megabyte) | | `DB_RESOURCE_STORE_KV_THREADS` | Read-/Writing Resources | 4 | 4, 8, 16 or 32 | @@ -126,4 +123,4 @@ Blaze maintains indexes for FHIR search parameters and CQL evaluation. The index Depending on your system, indexing can be I/O or CPU-bound. Performance tests show that at least 64 CPU cores can be utilized for resource indexing. [1]: -[2]: \ No newline at end of file +[2]: diff --git a/modules/admin-api/src/blaze/admin_api.clj b/modules/admin-api/src/blaze/admin_api.clj index d9b15d6f1..4cb97f6e7 100644 --- a/modules/admin-api/src/blaze/admin_api.clj +++ b/modules/admin-api/src/blaze/admin_api.clj @@ -188,12 +188,12 @@ :wrap link-headers/wrap-link-headers}) (def ^:private allowed-profiles - #{#fhir/canonical "https://samply.github.io/blaze/fhir/StructureDefinition/AsyncInteractionJob" - #fhir/canonical "https://samply.github.io/blaze/fhir/StructureDefinition/CompactJob" - #fhir/canonical "https://samply.github.io/blaze/fhir/StructureDefinition/ReIndexJob"}) + #{"https://samply.github.io/blaze/fhir/StructureDefinition/AsyncInteractionJob" + "https://samply.github.io/blaze/fhir/StructureDefinition/CompactJob" + "https://samply.github.io/blaze/fhir/StructureDefinition/ReIndexJob"}) (defn- check-profile [resource] - (if (some allowed-profiles (-> resource :meta :profile)) + (if (some allowed-profiles (map :value (-> resource :meta :profile))) resource (ba/incorrect "No allowed profile found." @@ -213,7 +213,7 @@ (datafy/datafy))) (defn- error-issues [outcome] - (update outcome :issue (partial filterv (comp #{#fhir/code "error"} :severity)))) + (update outcome :issue (partial filterv (comp #{"error"} :value :severity)))) (def ^:private wrap-validate-job {:name :wrap-validate-job diff --git a/modules/admin-api/test/blaze/admin_api_test.clj b/modules/admin-api/test/blaze/admin_api_test.clj index ddf828d7b..d4bcc1d3f 100644 --- a/modules/admin-api/test/blaze/admin_api_test.clj +++ b/modules/admin-api/test/blaze/admin_api_test.clj @@ -735,7 +735,7 @@ {:system #fhir/uri "https://samply.github.io/blaze/fhir/CodeSystem/JobType" :code #fhir/code "re-index" :display #fhir/string "(Re)Index a Search Parameter"}]} - :authoredOn #fhir/dateTime "2024-04-13T10:05:20.927Z" + :authoredOn #fhir/dateTime #system/date-time "2024-04-13T10:05:20.927Z" :input [{:fhir/type :fhir.Task/input :type #fhir/CodeableConcept @@ -756,7 +756,7 @@ {:system #fhir/uri "https://samply.github.io/blaze/fhir/CodeSystem/JobType" :code #fhir/code "compact" :display #fhir/string "Compact a Database Column Family"}]} - :authoredOn #fhir/dateTime "2024-04-13T10:05:20.927Z" + :authoredOn #fhir/dateTime #system/date-time "2024-04-13T10:05:20.927Z" :input [{:fhir/type :fhir.Task/input :type #fhir/CodeableConcept diff --git a/modules/byte-buffer/build.clj b/modules/byte-buffer/build.clj index d1465ac5a..796754344 100644 --- a/modules/byte-buffer/build.clj +++ b/modules/byte-buffer/build.clj @@ -7,4 +7,4 @@ {:basis (b/create-basis {:project "deps.edn"}) :src-dirs ["java"] :class-dir "target/classes" - :javac-opts ["-Xlint:all" "-proc:none" "--release" "17"]})) + :javac-opts ["-Xlint:all" "-proc:none" "--release" "21"]})) diff --git a/modules/coll/build.clj b/modules/coll/build.clj index d1465ac5a..796754344 100644 --- a/modules/coll/build.clj +++ b/modules/coll/build.clj @@ -7,4 +7,4 @@ {:basis (b/create-basis {:project "deps.edn"}) :src-dirs ["java"] :class-dir "target/classes" - :javac-opts ["-Xlint:all" "-proc:none" "--release" "17"]})) + :javac-opts ["-Xlint:all" "-proc:none" "--release" "21"]})) diff --git a/modules/cql/Makefile b/modules/cql/Makefile index ad725b2e9..9b2029e91 100644 --- a/modules/cql/Makefile +++ b/modules/cql/Makefile @@ -11,7 +11,7 @@ cql-test: # Please remove --no-check-certificate if the certificate is ok gain. # We can do that because the check the SHA256 of the ZIP file. wget --no-check-certificate https://cql.hl7.org/tests.zip - echo "0d48a7441c43b6ee46e71d73decfa0cf4ea81e2ce70951f20e9163c3bebfc49a tests.zip" | sha256sum --check --status + echo "0d48a7441c43b6ee46e71d73decfa0cf4ea81e2ce70951f20e9163c3bebfc49a tests.zip" | sha256sum --check --status - unzip -jd cql-test tests.zip rm tests.zip # See: https://github.com/HL7/cql/pull/69 diff --git a/modules/cql/src/blaze/elm/compiler/external_data.clj b/modules/cql/src/blaze/elm/compiler/external_data.clj index c5a2985e1..5f4f55ede 100644 --- a/modules/cql/src/blaze/elm/compiler/external_data.clj +++ b/modules/cql/src/blaze/elm/compiler/external_data.clj @@ -63,13 +63,12 @@ (reify-expr core/Expression (-eval [_ {:keys [db]} resource _] (prom/inc! retrieve-total) - (let [{{:keys [reference]} :subject} resource] - (when reference - (when-let [[type id] (fsr/split-literal-ref reference)] - (when (and (= "Patient" type) (string? id)) - (when-let [handle (d/resource-handle db "Patient" id)] - (when-not (d/deleted? handle) - [(cr/mk-resource db handle)]))))))) + (when-let [reference (-> resource :subject :reference :value)] + (when-let [[type id] (fsr/split-literal-ref reference)] + (when (and (= "Patient" type) (string? id)) + (when-let [handle (d/resource-handle db "Patient" id)] + (when-not (d/deleted? handle) + [(cr/mk-resource db handle)])))))) (-form [_] '(retrieve (Specimen) "Patient")))) diff --git a/modules/cql/src/blaze/elm/compiler/reusing_logic.clj b/modules/cql/src/blaze/elm/compiler/reusing_logic.clj index 59ad4d4e1..4181d72b5 100644 --- a/modules/cql/src/blaze/elm/compiler/reusing_logic.clj +++ b/modules/cql/src/blaze/elm/compiler/reusing_logic.clj @@ -13,10 +13,9 @@ [blaze.elm.interval :as interval] [blaze.elm.protocols :as p] [blaze.elm.quantity :as quantity] - [blaze.fhir.spec.type :as type]) + [blaze.fhir.spec :as fhir-spec]) (:import - [blaze.fhir.spec.type Period] - [java.util Map])) + [blaze.fhir.spec.type Period Quantity])) (set! *warn-on-reflection* true) @@ -84,10 +83,10 @@ (-to-quantity [x])) (extend-protocol ToQuantity - Map - (-to-quantity [m] - (when-let [value (:value m)] - (quantity/quantity value (or (-> m :code type/value) "1")))) + Quantity + (-to-quantity [fhir-quantity] + (when-let [decimal (-> fhir-quantity :value :value)] + (quantity/quantity decimal (or (-> fhir-quantity :code :value) "1")))) Object (-to-quantity [x] @@ -112,7 +111,7 @@ `(~'call "ToQuantity" ~(core/-form operand))))) (defn- to-code [{:keys [system version code]}] - (code/code (type/value system) (type/value version) (type/value code))) + (code/code (:value system) (:value version) (:value code))) (defn- to-code-function-expr [operand] (reify-expr core/Expression @@ -129,6 +128,21 @@ (-form [_] `(~'call "ToCode" ~(core/-form operand))))) +(defn- to-decimal-function-expr [operand] + (reify-expr core/Expression + (-attach-cache [_ cache] + (core/attach-cache-helper to-decimal-function-expr cache operand)) + (-resolve-refs [_ expression-defs] + (to-decimal-function-expr (core/-resolve-refs operand expression-defs))) + (-resolve-params [_ parameters] + (core/resolve-params-helper to-decimal-function-expr parameters operand)) + (-optimize [_ db] + (core/optimize-helper to-decimal-function-expr db operand)) + (-eval [_ context resource scope] + (:value (core/-eval operand context resource scope))) + (-form [_] + `(~'call "ToDecimal" ~(core/-form operand))))) + (defn- to-date-function-expr [operand] (reify-expr core/Expression (-attach-cache [_ cache] @@ -140,7 +154,7 @@ (-optimize [_ db] (core/optimize-helper to-date-function-expr db operand)) (-eval [_ context resource scope] - (type/value (core/-eval operand context resource scope))) + (:value (core/-eval operand context resource scope))) (-form [_] `(~'call "ToDate" ~(core/-form operand))))) @@ -155,7 +169,7 @@ (-optimize [_ parameters] (to-date-time-function-expr (core/-optimize operand parameters))) (-eval [_ {:keys [now] :as context} resource scope] - (p/to-date-time (type/value (core/-eval operand context resource scope)) now)) + (p/to-date-time (:value (core/-eval operand context resource scope)) now)) (-form [_] `(~'call "ToDateTime" ~(core/-form operand))))) @@ -170,7 +184,8 @@ (-optimize [_ db] (core/optimize-helper to-string-function-expr db operand)) (-eval [_ context resource scope] - (some-> (type/value (core/-eval operand context resource scope)) str)) + (let [value (core/-eval operand context resource scope)] + (some-> (cond-> value (fhir-spec/primitive-val? value) :value) str))) (-form [_] `(~'call "ToString" ~(core/-form operand))))) @@ -181,8 +196,8 @@ Period (-to-interval [{:keys [start end]} {:keys [now]}] (interval/interval - (p/to-date-time (type/value start) now) - (p/to-date-time (type/value end) now))) + (p/to-date-time (:value start) now) + (p/to-date-time (:value end) now))) nil (-to-interval [_ _])) @@ -240,7 +255,7 @@ (to-code-function-expr (first operands)) "ToDecimal" - (first operands) + (to-decimal-function-expr (first operands)) "ToInterval" (to-interval-function-expr (first operands)) diff --git a/modules/cql/src/blaze/elm/compiler/structured_values.clj b/modules/cql/src/blaze/elm/compiler/structured_values.clj index 7b8370127..d0286506b 100644 --- a/modules/cql/src/blaze/elm/compiler/structured_values.clj +++ b/modules/cql/src/blaze/elm/compiler/structured_values.clj @@ -11,12 +11,9 @@ [blaze.elm.compiler.macros :refer [reify-expr]] [blaze.elm.protocols :as p] [blaze.elm.util :as elm-util] - [blaze.fhir.spec.type :as type] [clojure.string :as str]) (:import - [clojure.lang ILookup IReduceInit] - [java.time Instant LocalDateTime LocalTime OffsetDateTime] - [java.util UUID])) + [clojure.lang ILookup IReduceInit])) (set! *warn-on-reflection* true) @@ -36,43 +33,7 @@ (throw-anom (invalid-structured-type-access-anom coll key))) ILookup (get [m key] - (.valAt m key)) - Boolean - (get [boolean key] - (when (identical? :value key) - boolean)) - Integer - (get [int key] - (when (identical? :value key) - int)) - String - (get [s key] - (when (identical? :value key) - s)) - BigDecimal - (get [decimal key] - (when (identical? :value key) - decimal)) - Instant - (get [instant key] - (when (identical? :value key) - instant)) - LocalDateTime - (get [date-time key] - (when (identical? :value key) - date-time)) - OffsetDateTime - (get [date-time key] - (when (identical? :value key) - date-time)) - LocalTime - (get [time key] - (when (identical? :value key) - time)) - UUID - (get [uuid key] - (when (identical? :value key) - uuid))) + (.valAt m key))) (defn- compile-elements [context elements] (reduce @@ -195,7 +156,7 @@ (-resolve-params [_ parameters] (source-property-value-expr (core/-resolve-params source parameters) key)) (-eval [_ context resource scope] - (type/value (p/get (core/-eval source context resource scope) key))) + (:value (key (core/-eval source context resource scope)))) (-form [_] `(:value (~key ~(core/-form source)))))) @@ -209,7 +170,7 @@ (defn- scope-property-value-expr [scope-key key] (reify-expr core/Expression (-eval [_ _ _ scope] - (type/value (p/get (get scope scope-key) key))) + (:value (key (get scope scope-key)))) (-form [_] `(:value (~key ~(symbol (name scope-key))))))) diff --git a/modules/cql/src/blaze/elm/compiler/type_operators.clj b/modules/cql/src/blaze/elm/compiler/type_operators.clj index c72096394..0012541da 100644 --- a/modules/cql/src/blaze/elm/compiler/type_operators.clj +++ b/modules/cql/src/blaze/elm/compiler/type_operators.clj @@ -11,7 +11,6 @@ [blaze.elm.protocols :as p] [blaze.elm.quantity :as quantity] [blaze.elm.util :as elm-util] - [blaze.fhir.spec :as fhir-spec] [blaze.fhir.spec.type.system :as system])) (set! *warn-on-reflection* true) @@ -32,7 +31,7 @@ (let [[type-ns type-name] (elm-util/parse-qualified-name type-name)] (case type-ns "http://hl7.org/fhir" - [(symbol "fhir" type-name) (comp #{(keyword "fhir" type-name)} fhir-spec/fhir-type)] + [(symbol "fhir" type-name) (comp #{(keyword "fhir" type-name)} :fhir/type)] "urn:hl7-org:elm-types:r1" (matches-elm-named-type-fn type-name) (throw-anom @@ -236,7 +235,7 @@ "http://hl7.org/fhir" [(symbol "fhir" type-name) (let [fhir-type (keyword "fhir" type-name)] - #(identical? fhir-type (fhir-spec/fhir-type %)))] + #(identical? fhir-type (:fhir/type %)))] "urn:hl7-org:elm-types:r1" (matches-elm-named-type-is type-name)))) diff --git a/modules/cql/src/blaze/elm/ts_util.clj b/modules/cql/src/blaze/elm/ts_util.clj index abd4d4448..3245de93c 100644 --- a/modules/cql/src/blaze/elm/ts_util.clj +++ b/modules/cql/src/blaze/elm/ts_util.clj @@ -4,11 +4,11 @@ [blaze.fhir.spec.type :as type])) (def ^:private result-pred - #(when (= "result" (type/value (:name %))) %)) + #(when (= "result" (:value (:name %))) %)) (defn extract-result [response msg-fn] (try - (type/value (:value (some result-pred (:parameter @response)))) + (:value (:value (some result-pred (:parameter @response)))) (catch Exception e (ba/throw-anom (ba/fault (msg-fn (ex-message (ex-cause e)))))))) diff --git a/modules/cql/test/blaze/elm/compiler/external_data_test.clj b/modules/cql/test/blaze/elm/compiler/external_data_test.clj index a5409ed1f..0430281e1 100644 --- a/modules/cql/test/blaze/elm/compiler/external_data_test.clj +++ b/modules/cql/test/blaze/elm/compiler/external_data_test.clj @@ -23,7 +23,6 @@ [blaze.terminology-service :as-alias ts] [blaze.terminology-service-spec] [blaze.terminology-service.local :as ts-local] - [blaze.terminology-service.protocols :as p] [blaze.util-spec] [clojure.spec.test.alpha :as st] [clojure.test :as test :refer [deftest is testing]] @@ -394,10 +393,20 @@ (testing "Specimen context" (testing "Patient" - (with-system-data [{:blaze.db/keys [node]} mem-node-config] + (with-system-data [{:blaze.db/keys [node]} (assoc-in mem-node-config [:blaze.db/node :enforce-referential-integrity] false)] [[[:put {:fhir/type :fhir/Patient :id "0"}] [:put {:fhir/type :fhir/Specimen :id "0" - :subject #fhir/Reference{:reference #fhir/string "Patient/0"}}]]] + :subject #fhir/Reference{:reference #fhir/string "Patient/0"}}] + [:put {:fhir/type :fhir/Specimen :id "1"}] + [:put {:fhir/type :fhir/Group :id "0"}] + [:put {:fhir/type :fhir/Specimen :id "2" + :subject #fhir/Reference{:reference #fhir/string "Group/0"}}] + [:put {:fhir/type :fhir/Specimen :id "3" + :subject #fhir/Reference{:reference #fhir/string "invalid"}}] + [:put {:fhir/type :fhir/Patient :id "1"}] + [:put {:fhir/type :fhir/Specimen :id "4" + :subject #fhir/Reference{:reference #fhir/string "Patient/1"}}]] + [[:delete "Patient" "1"]]] (let [context {:node node @@ -405,13 +414,26 @@ :library {}} expr (c/compile context ctu/patient-retrieve-elm) db (d/db node) - specimen (ctu/resource db "Specimen" "0")] + eval-ctx (eval-context db)] (testing "eval" - (given (expr/eval (eval-context db) expr specimen) - count := 1 - [0 :fhir/type] := :fhir/Patient - [0 :id] := "0")) + (testing "specimen with patient subject" + (given (expr/eval eval-ctx expr (ctu/resource db "Specimen" "0")) + count := 1 + [0 :fhir/type] := :fhir/Patient + [0 :id] := "0")) + + (testing "specimen without subject" + (is (nil? (expr/eval eval-ctx expr (ctu/resource db "Specimen" "1"))))) + + (testing "specimen with group subject" + (is (nil? (expr/eval eval-ctx expr (ctu/resource db "Specimen" "2"))))) + + (testing "specimen with invalid subject reference" + (is (nil? (expr/eval eval-ctx expr (ctu/resource db "Specimen" "3"))))) + + (testing "specimen with deleted patient subject" + (is (nil? (expr/eval eval-ctx expr (ctu/resource db "Specimen" "4")))))) (testing "expression is dynamic" (is (false? (core/-static expr)))) diff --git a/modules/cql/test/blaze/elm/compiler/library_test.clj b/modules/cql/test/blaze/elm/compiler/library_test.clj index 090bf4bda..f2505b222 100644 --- a/modules/cql/test/blaze/elm/compiler/library_test.clj +++ b/modules/cql/test/blaze/elm/compiler/library_test.clj @@ -1232,7 +1232,7 @@ :expansion {:fhir/type :fhir.ValueSet/expansion :identifier #fhir/uri "urn:uuid:b01db38a-3ec8-4167-a279-0bb1200624a8" - :timestamp #fhir/dateTime"1970-01-01T00:00:00Z" + :timestamp #fhir/dateTime #system/date-time "1970-01-01T00:00:00Z" :contains [{:fhir/type :fhir.ValueSet.expansion/contains :system #fhir/uri "http://hl7.org/fhir/administrative-gender" diff --git a/modules/cql/test/blaze/elm/compiler/queries_test.clj b/modules/cql/test/blaze/elm/compiler/queries_test.clj index 05aa99319..f24d382b7 100644 --- a/modules/cql/test/blaze/elm/compiler/queries_test.clj +++ b/modules/cql/test/blaze/elm/compiler/queries_test.clj @@ -15,18 +15,22 @@ [blaze.elm.compiler.test-util :as ctu :refer [has-form]] [blaze.elm.literal :as elm] [blaze.elm.literal-spec] + [blaze.elm.protocols :as p] [blaze.elm.util-spec] - [blaze.fhir.spec.type] + [blaze.fhir.spec.generators :as fg] [blaze.fhir.spec.type.system.spec] [blaze.test-util :refer [satisfies-prop]] [blaze.util-spec] - [clojure.spec.alpha :as s] [clojure.spec.test.alpha :as st] [clojure.test :as test :refer [are deftest is testing]] [clojure.test.check.generators :as gen] [clojure.test.check.properties :as prop] - [juxt.iota :refer [given]])) + [java-time.api :as time] + [juxt.iota :refer [given]]) + (:import + [java.time OffsetDateTime])) +(set! *warn-on-reflection* true) (st/instrument) (ctu/instrument-compile) @@ -38,31 +42,35 @@ (test/use-fixtures :each fixture) -(defn- rare-nil [gen] - (gen/frequency [[9 gen] [1 (gen/return nil)]])) +(defn- dateTime-value [] + (gen/fmap #(p/to-date-time % (OffsetDateTime/now)) (fg/dateTime-value))) (deftest sort-by-test (testing "date" (testing "asc" (satisfies-prop 1000 - (prop/for-all [dates (gen/vector (rare-nil (s/gen :system/date)))] - (= (sort dates) (queries/sort-by identity "asc" dates))))) + (prop/for-all [dates (gen/vector (fg/rare-nil fg/date-value))] + (= (sort (map time/year dates)) + (map time/year (queries/sort-by identity "asc" dates)))))) (testing "desc" (satisfies-prop 1000 - (prop/for-all [dates (gen/vector (rare-nil (s/gen :system/date)))] - (= (reverse (sort dates)) (queries/sort-by identity "desc" dates)))))) + (prop/for-all [dates (gen/vector (fg/rare-nil fg/date-value))] + (= (reverse (sort (map time/year dates))) + (map time/year (queries/sort-by identity "desc" dates))))))) (testing "date-time" (testing "asc" (satisfies-prop 1000 - (prop/for-all [date-times (gen/vector (rare-nil (s/gen :system/date-time)))] - (= (sort date-times) (queries/sort-by identity "asc" date-times))))) + (prop/for-all [date-times (gen/vector (fg/rare-nil (dateTime-value)))] + (= (sort (map time/year date-times)) + (map time/year (queries/sort-by identity "asc" date-times)))))) (testing "desc" (satisfies-prop 1000 - (prop/for-all [date-times (gen/vector (rare-nil (s/gen :system/date-time)))] - (= (reverse (sort date-times)) (queries/sort-by identity "desc" date-times))))))) + (prop/for-all [date-times (gen/vector (fg/rare-nil (dateTime-value)))] + (= (reverse (sort (map time/year date-times))) + (map time/year (queries/sort-by identity "desc" date-times)))))))) ;; 10.1. Query ;; @@ -370,7 +378,7 @@ [{:type "With" :expression #elm/retrieve{:type "Encounter"} :alias "E" - :suchThat #elm/equal [#elm/source-property [#elm/scope-property ["O" "encounter"] "reference"] + :suchThat #elm/equal [#elm/function-ref ["ToString" #elm/source-property [#elm/scope-property ["O" "encounter"] "reference"]] #elm/concatenate [#elm/string "Encounter/" #elm/scope-property ["E" "id"]]]}]}] (let [expr (c/compile {:node node :eval-context "Patient"} elm)] @@ -389,7 +397,7 @@ (exists (fn [E] (equal - (:reference (:encounter O)) + (call "ToString" (:reference (:encounter O))) (concatenate "Encounter/" (:id E)))) (retrieve "Encounter")))) distinct) @@ -413,7 +421,7 @@ (exists (fn [E] (equal - (:reference (:encounter O)) + (call "ToString" (:reference (:encounter O))) (concatenate "Encounter/" (:id E)))) (retrieve "Encounter")))) (retrieve "Observation"))))))) @@ -425,7 +433,7 @@ [{:type "With" :expression #elm/retrieve{:type "Encounter"} :alias "E" - :suchThat #elm/equal [#elm/source-property [#elm/scope-property ["O" "encounter"] "reference"] + :suchThat #elm/equal [#elm/function-ref ["ToString" #elm/source-property [#elm/scope-property ["O" "encounter"] "reference"]] #elm/concatenate [#elm/string "Encounter/" #elm/scope-property ["E" "id"]]]}] :return {:expression #elm/scope-property ["O" "id"]}}] @@ -444,7 +452,7 @@ (exists (fn [E] (equal - (:reference (:encounter O)) + (call "ToString" (:reference (:encounter O))) (concatenate "Encounter/" (:id E)))) (retrieve "Encounter")))) (comp @@ -471,7 +479,7 @@ (exists (fn [E] (equal - (:reference (:encounter O)) + (call "ToString" (:reference (:encounter O))) (concatenate "Encounter/" (:id E)))) (retrieve "Encounter")))) (map @@ -485,7 +493,7 @@ [{:type "With" :expression #elm/retrieve{:type "Encounter"} :alias "E" - :suchThat #elm/equal [#elm/source-property [#elm/scope-property ["O" "encounter"] "reference"] + :suchThat #elm/equal [#elm/function-ref ["ToString" #elm/source-property [#elm/scope-property ["O" "encounter"] "reference"]] #elm/concatenate [#elm/string "Encounter/" #elm/scope-property ["E" "id"]]]}] :return {:distinct false :expression #elm/scope-property ["O" "id"]}} expr (c/compile {:node node :eval-context "Patient"} elm)] @@ -504,7 +512,7 @@ (exists (fn [E] (equal - (:reference (:encounter O)) + (call "ToString" (:reference (:encounter O))) (concatenate "Encounter/" (:id E)))) (retrieve "Encounter")))) (map @@ -518,7 +526,7 @@ [{:type "With" :expression #elm/retrieve{:type "Encounter"} :alias "E" - :suchThat #elm/equal [#elm/source-property [#elm/scope-property ["O" "encounter"] "reference"] + :suchThat #elm/equal [#elm/function-ref ["ToString" #elm/source-property [#elm/scope-property ["O" "encounter"] "reference"]] #elm/concatenate [#elm/string "Encounter/" #elm/scope-property ["E" "id"]]]}] :where #elm/equal [#elm/string "1" #elm/scope-property ["O" "id"]]}] @@ -537,7 +545,7 @@ (exists (fn [E] (equal - (:reference (:encounter O)) + (call "ToString" (:reference (:encounter O))) (concatenate "Encounter/" (:id E)))) (retrieve "Encounter")))) distinct) @@ -561,7 +569,7 @@ (exists (fn [E] (equal - (:reference (:encounter O)) + (call "ToString" (:reference (:encounter O))) (concatenate "Encounter/" (:id E)))) (retrieve "Encounter"))))) (retrieve "Observation")))))))) @@ -573,12 +581,12 @@ [{:type "With" :expression #elm/retrieve{:type "Encounter"} :alias "E" - :suchThat #elm/equal [#elm/source-property [#elm/scope-property ["O" "encounter"] "reference"] + :suchThat #elm/equal [#elm/function-ref ["ToString" #elm/source-property [#elm/scope-property ["O" "encounter"] "reference"]] #elm/concatenate [#elm/string "Encounter/" #elm/scope-property ["E" "id"]]]} {:type "With" :expression #elm/retrieve{:type "Specimen"} :alias "S" - :suchThat #elm/equal [#elm/source-property [#elm/scope-property ["O" "specimen"] "reference"] + :suchThat #elm/equal [#elm/function-ref ["ToString" #elm/source-property [#elm/scope-property ["O" "specimen"] "reference"]] #elm/concatenate [#elm/string "Specimen/" #elm/scope-property ["S" "id"]]]}]}] (let [expr (c/compile {:node node :eval-context "Patient"} elm)] @@ -597,7 +605,7 @@ (exists (fn [E] (equal - (:reference (:encounter O)) + (call "ToString" (:reference (:encounter O))) (concatenate "Encounter/" (:id E)))) (retrieve "Encounter")))) (filter @@ -605,7 +613,7 @@ (exists (fn [S] (equal - (:reference (:specimen O)) + (call "ToString" (:reference (:specimen O))) (concatenate "Specimen/" (:id S)))) (retrieve "Specimen")))) distinct) @@ -630,7 +638,7 @@ (exists (fn [E] (equal - (:reference (:encounter O)) + (call "ToString" (:reference (:encounter O))) (concatenate "Encounter/" (:id E)))) (retrieve "Encounter")))) (filter @@ -638,7 +646,7 @@ (exists (fn [S] (equal - (:reference (:specimen O)) + (call "ToString" (:reference (:specimen O))) (concatenate "Specimen/" (:id S)))) (retrieve "Specimen"))))) (retrieve "Observation"))))))))))) @@ -660,7 +668,7 @@ [{:type "Without" :expression #elm/retrieve{:type "Encounter"} :alias "E" - :suchThat #elm/equal [#elm/source-property [#elm/scope-property ["O" "encounter"] "reference"] + :suchThat #elm/equal [#elm/function-ref ["ToString" #elm/source-property [#elm/scope-property ["O" "encounter"] "reference"]] #elm/concatenate [#elm/string "Encounter/" #elm/scope-property ["E" "id"]]]}]} expr (c/compile {:node node :eval-context "Patient"} elm) db (d/db node) @@ -681,7 +689,7 @@ (not-exists (fn [E] (equal - (:reference (:encounter O)) + (call "ToString" (:reference (:encounter O))) (concatenate "Encounter/" (:id E)))) (retrieve "Encounter")))) distinct) @@ -693,10 +701,10 @@ [[[:put {:fhir/type :fhir/Patient :id "0"}] [:put {:fhir/type :fhir/Encounter :id "0" :subject #fhir/Reference{:reference #fhir/string "Patient/0"} - :period #fhir/Period{:start #fhir/dateTime "2025-05-15"}}] + :period #fhir/Period{:start #fhir/dateTime #system/date-time "2025-05-15"}}] [:put {:fhir/type :fhir/Encounter :id "1" :subject #fhir/Reference{:reference #fhir/string "Patient/0"} - :period #fhir/Period{:start #fhir/dateTime "2025-05-16"}}]]] + :period #fhir/Period{:start #fhir/dateTime #system/date-time "2025-05-16"}}]]] (let [elm {:type "Query" :source diff --git a/modules/cql/test/blaze/elm/compiler/reusing_logic_test.clj b/modules/cql/test/blaze/elm/compiler/reusing_logic_test.clj index d9b36ef4d..069df407e 100644 --- a/modules/cql/test/blaze/elm/compiler/reusing_logic_test.clj +++ b/modules/cql/test/blaze/elm/compiler/reusing_logic_test.clj @@ -382,9 +382,9 @@ (are [x res] (= res (core/-eval expr (eval-ctx x) nil nil)) #fhir/date{:id "foo"} nil #fhir/date{:extension [#fhir/Extension{:url "foo"}]} nil - #fhir/date "2023" #system/date"2023" - #fhir/date "2023-05" #system/date"2023-05" - #fhir/date "2023-05-07" #system/date"2023-05-07")) + #fhir/date #system/date "2023" #system/date"2023" + #fhir/date #system/date "2023-05" #system/date"2023-05" + #fhir/date #system/date "2023-05-07" #system/date"2023-05-07")) (testing "expression is dynamic" (is (false? (core/-static expr)))) @@ -412,15 +412,15 @@ (are [x res] (= res (core/-eval expr (eval-ctx x) nil nil)) #fhir/dateTime{:id "foo"} nil #fhir/dateTime{:extension [#fhir/Extension{:url "foo"}]} nil - #fhir/dateTime "2022" #system/date-time"2022" - #fhir/dateTime "2022-02" #system/date-time"2022-02" - #fhir/dateTime "2022-02-22" #system/date-time"2022-02-22" - #fhir/dateTime "2023-05-07T17:39" #system/date-time"2023-05-07T17:39" + #fhir/dateTime #system/date-time "2022" #system/date-time"2022" + #fhir/dateTime #system/date-time "2022-02" #system/date-time"2022-02" + #fhir/dateTime #system/date-time "2022-02-22" #system/date-time"2022-02-22" + #fhir/dateTime #system/date-time "2023-05-07T17:39" #system/date-time"2023-05-07T17:39" #fhir/instant{:id "foo"} nil #fhir/instant{:extension [#fhir/Extension{:url "foo"}]} nil - #fhir/instant "2021-02-23T15:12:45Z" #system/date-time"2021-02-23T15:12:45" - #fhir/instant "2021-02-23T15:12:45+01:00" #system/date-time"2021-02-23T14:12:45")) + #fhir/instant #system/date-time "2021-02-23T15:12:45Z" #system/date-time"2021-02-23T15:12:45" + #fhir/instant #system/date-time "2021-02-23T15:12:45+01:00" #system/date-time"2021-02-23T14:12:45")) (testing "expression is dynamic" (is (false? (core/-static expr)))) @@ -430,8 +430,8 @@ (testing-function-ref-resolve-refs "ToDateTime") (testing "resolve parameters" - (has-form (core/-resolve-params expr {"x" #fhir/dateTime "2022-02"}) - '(call "ToDateTime" #fhir/dateTime "2022-02")) + (has-form (core/-resolve-params expr {"x" #fhir/dateTime #system/date-time "2022-02"}) + '(call "ToDateTime" #fhir/dateTime #system/date-time "2022-02")) (has-form (core/-resolve-params expr {}) '(call "ToDateTime" (param-ref "x")))) @@ -499,6 +499,32 @@ (testing "form" (has-form expr '(call "ToCode" (param-ref "x")))))) + (testing "ToDecimal" + (let [compile-ctx {:library {:parameters {:def [{:name "x"}]}}} + elm #elm/function-ref ["ToDecimal" #elm/parameter-ref "x"] + expr (c/compile compile-ctx elm)] + + (testing "eval" + (are [x res] (= res (core/-eval expr {:parameters {"x" x}} nil nil)) + nil nil + #fhir/decimal 123M 123M)) + + (testing "expression is dynamic" + (is (false? (core/-static expr)))) + + (testing-function-ref-attach-cache "ToDecimal") + + (testing-function-ref-resolve-refs "ToDecimal") + + (testing "resolve parameters" + (has-form (core/-resolve-params expr {}) + '(call "ToDecimal" (param-ref "x")))) + + (testing-function-ref-optimize "ToDecimal") + + (testing "form" + (has-form expr '(call "ToDecimal" (param-ref "x")))))) + (testing "ToInterval" (let [compile-ctx {:library {:parameters {:def [{:name "x"}]}}} elm #elm/function-ref ["ToInterval" #elm/parameter-ref "x"] @@ -508,19 +534,19 @@ (testing "eval" (are [x res] (= res (core/-eval expr (eval-ctx x) nil nil)) #fhir/Period - {:start #fhir/dateTime "2021-02-23T15:12:45+01:00" - :end #fhir/dateTime "2021-02-23T16:00:00+01:00"} + {:start #fhir/dateTime #system/date-time "2021-02-23T15:12:45+01:00" + :end #fhir/dateTime #system/date-time "2021-02-23T16:00:00+01:00"} (interval/interval (system/date-time 2021 2 23 14 12 45) (system/date-time 2021 2 23 15 0 0)) #fhir/Period {:start nil - :end #fhir/dateTime "2021-02-23T16:00:00+01:00"} + :end #fhir/dateTime #system/date-time "2021-02-23T16:00:00+01:00"} (interval/interval nil (system/date-time 2021 2 23 15 0 0)) #fhir/Period - {:start #fhir/dateTime "2021-02-23T15:12:45+01:00" + {:start #fhir/dateTime #system/date-time "2021-02-23T15:12:45+01:00" :end nil} (interval/interval (system/date-time 2021 2 23 14 12 45) @@ -537,11 +563,11 @@ (testing "resolve parameters" (has-form (core/-resolve-params expr {"x" #fhir/Period - {:start #fhir/dateTime "2021-02-23T15:12:45+01:00" - :end #fhir/dateTime "2021-02-23T16:00:00+01:00"}}) + {:start #fhir/dateTime #system/date-time "2021-02-23T15:12:45+01:00" + :end #fhir/dateTime #system/date-time "2021-02-23T16:00:00+01:00"}}) '(call "ToInterval" #fhir/Period - {:start #fhir/dateTime "2021-02-23T15:12:45+01:00" - :end #fhir/dateTime "2021-02-23T16:00:00+01:00"})) + {:start #fhir/dateTime #system/date-time "2021-02-23T15:12:45+01:00" + :end #fhir/dateTime #system/date-time "2021-02-23T16:00:00+01:00"})) (testing-function-ref-optimize "ToInterval") diff --git a/modules/cql/test/blaze/elm/compiler/structured_values_test.clj b/modules/cql/test/blaze/elm/compiler/structured_values_test.clj index 7d6784155..80cd11b68 100644 --- a/modules/cql/test/blaze/elm/compiler/structured_values_test.clj +++ b/modules/cql/test/blaze/elm/compiler/structured_values_test.clj @@ -22,8 +22,7 @@ [cognitect.anomalies :as anom] [juxt.iota :refer [given]]) (:import - [blaze.elm.code Code] - [java.time Instant])) + [blaze.elm.code Code])) (st/instrument) (ctu/instrument-compile) @@ -59,27 +58,27 @@ (is (= "value-170805" (p/get #fhir/base64Binary "value-170805" :value))) - (is (= Instant/EPOCH (p/get #fhir/instant "1970-01-01T00:00:00Z" :value))) + (is (= #system/date-time "1970-01-01T00:00:00Z" (p/get #fhir/instant #system/date-time "1970-01-01T00:00:00Z" :value))) - (is (= #system/date"2025" (p/get #fhir/date "2025" :value))) + (is (= #system/date"2025" (p/get #fhir/date #system/date "2025" :value))) - (is (= #system/date"2025-04" (p/get #fhir/date "2025-04" :value))) + (is (= #system/date"2025-04" (p/get #fhir/date #system/date "2025-04" :value))) - (is (= #system/date"2025-04-09" (p/get #fhir/date "2025-04-09" :value))) + (is (= #system/date"2025-04-09" (p/get #fhir/date #system/date "2025-04-09" :value))) - (is (= #system/date-time"2025" (p/get #fhir/dateTime "2025" :value))) + (is (= #system/date-time"2025" (p/get #fhir/dateTime #system/date-time "2025" :value))) - (is (= #system/date-time"2025-04" (p/get #fhir/dateTime "2025-04" :value))) + (is (= #system/date-time"2025-04" (p/get #fhir/dateTime #system/date-time "2025-04" :value))) - (is (= #system/date-time"2025-04-09" (p/get #fhir/dateTime "2025-04-09" :value))) + (is (= #system/date-time"2025-04-09" (p/get #fhir/dateTime #system/date-time "2025-04-09" :value))) - (is (= #system/date-time"2025-04-09T12:34:56" (p/get #fhir/dateTime "2025-04-09T12:34:56" :value))) + (is (= #system/date-time"2025-04-09T12:34:56" (p/get #fhir/dateTime #system/date-time "2025-04-09T12:34:56" :value))) - (is (= #system/date-time"2025-04-09T12:34:56Z" (p/get #fhir/dateTime "2025-04-09T12:34:56Z" :value))) + (is (= #system/date-time"2025-04-09T12:34:56Z" (p/get #fhir/dateTime #system/date-time "2025-04-09T12:34:56Z" :value))) - (is (= #system/date-time"2025-04-09T12:34:56+01:00" (p/get #fhir/dateTime "2025-04-09T12:34:56+01:00" :value))) + (is (= #system/date-time"2025-04-09T12:34:56+01:00" (p/get #fhir/dateTime #system/date-time "2025-04-09T12:34:56+01:00" :value))) - (is (= #system/time"17:20:08" (p/get #fhir/time "17:20:08" :value))) + (is (= #system/time"17:20:08" (p/get #fhir/time #system/time "17:20:08" :value))) (is (= "value-165314" (p/get #fhir/code "value-165314" :value))) @@ -95,7 +94,7 @@ (is (= 1 (p/get #fhir/positiveInt 1 :value))) - (is (= #uuid"6a989368-0d9a-48b0-8bdb-5b61e29f9b39" (p/get #fhir/uuid "urn:uuid:6a989368-0d9a-48b0-8bdb-5b61e29f9b39" :value)))) + (is (= "urn:uuid:6a989368-0d9a-48b0-8bdb-5b61e29f9b39" (p/get #fhir/uuid "urn:uuid:6a989368-0d9a-48b0-8bdb-5b61e29f9b39" :value)))) ;; 2.1. Tuple ;; @@ -336,7 +335,7 @@ (testing "eval" (are [birth-date res] (= res (core/-eval expr nil nil {"R" (entity birth-date)})) - #fhir/date "2023-05-07" #system/date"2023-05-07" + #fhir/date #system/date "2023-05-07" #system/date"2023-05-07" #fhir/date{:id "foo" :value #system/date"2023-05-07"} #system/date"2023-05-07" #fhir/date{:id "foo"} nil #fhir/date{:extension [#fhir/Extension{:url "foo"}]} nil)) @@ -510,7 +509,7 @@ (testing "eval" (are [birth-date res] (= res (core/-eval expr {:expression-defs {"Patient" {:expression (source birth-date)}}} nil nil)) - #fhir/date "2023-05-07" #system/date"2023-05-07" + #fhir/date #system/date "2023-05-07" #system/date"2023-05-07" #fhir/date{:id "foo" :value #system/date"2023-05-07"} #system/date"2023-05-07" #fhir/date{:id "foo"} nil #fhir/date{:extension [#fhir/Extension{:url "foo"}]} nil)) @@ -526,9 +525,9 @@ (let [expr-def {:type "ExpressionDef" :context "Patient" :name "Patient" - :expression (source #fhir/date "2023-05-07")} + :expression (source #fhir/date #system/date "2023-05-07")} expr (c/resolve-refs expr {"Patient" expr-def})] - (has-form expr (list :value (list :birthDate (source #fhir/date "2023-05-07")))))) + (has-form expr (list :value (list :birthDate (source #fhir/date #system/date "2023-05-07")))))) (testing "resolve parameters" (let [expr (c/resolve-params expr {})] diff --git a/modules/cql/test/blaze/elm/compiler/type_operators_test.clj b/modules/cql/test/blaze/elm/compiler/type_operators_test.clj index b2e1bbf6b..2291d5f22 100644 --- a/modules/cql/test/blaze/elm/compiler/type_operators_test.clj +++ b/modules/cql/test/blaze/elm/compiler/type_operators_test.clj @@ -78,12 +78,12 @@ #elm/as ["{http://hl7.org/fhir}dateTime" #elm/scope-property ["R" "value"]] - {:fhir/type :fhir/Observation :value #fhir/dateTime "2019-09-04"} - #fhir/dateTime "2019-09-04" + {:fhir/type :fhir/Observation :value #fhir/dateTime #system/date-time "2019-09-04"} + #fhir/dateTime #system/date-time "2019-09-04" #elm/as ["{http://hl7.org/fhir}Quantity" #elm/scope-property ["R" "value"]] - {:fhir/type :fhir/Observation :value #fhir/dateTime "2019-09-04"} + {:fhir/type :fhir/Observation :value #fhir/dateTime #system/date-time "2019-09-04"} nil)) (testing "ELM types" @@ -1012,7 +1012,7 @@ #elm/is ["{http://hl7.org/fhir}dateTime" #elm/scope-property ["R" "value"]] - {:fhir/type :fhir/Observation :value #fhir/dateTime "2019-09-04"}) + {:fhir/type :fhir/Observation :value #fhir/dateTime #system/date-time "2019-09-04"}) (are [elm resource] (false? (core/-eval (c/compile {} elm) {} nil {"R" resource})) #elm/is ["{http://hl7.org/fhir}boolean" @@ -1037,7 +1037,7 @@ #elm/is ["{http://hl7.org/fhir}url" #elm/scope-property ["R" "address"]] - {:fhir/type :fhir/Endpoint :address #fhir/dateTime "2019-09-04"} + {:fhir/type :fhir/Endpoint :address #fhir/dateTime #system/date-time "2019-09-04"} #elm/is ["{http://hl7.org/fhir}dateTime" #elm/scope-property ["R" "value"]] @@ -1045,7 +1045,7 @@ #elm/is ["{http://hl7.org/fhir}Quantity" #elm/scope-property ["R" "value"]] - {:fhir/type :fhir/Observation :value #fhir/dateTime "2019-09-04"})) + {:fhir/type :fhir/Observation :value #fhir/dateTime #system/date-time "2019-09-04"})) (testing "ELM types" (are [elm] (true? (core/-eval (c/compile {} elm) {} nil nil)) diff --git a/modules/cql/test/blaze/elm/date_time_test.clj b/modules/cql/test/blaze/elm/date_time_test.clj index c0c92b937..2ffa5420a 100644 --- a/modules/cql/test/blaze/elm/date_time_test.clj +++ b/modules/cql/test/blaze/elm/date_time_test.clj @@ -13,10 +13,10 @@ (deftest print-form-test (are [date res] (= res (pr-str (c/form date))) - #system/date"2023" "#system/date\"2023\"" - #system/date"2023-11" "#system/date\"2023-11\"" - #system/date"2023-11-02" "#system/date\"2023-11-02\"" - #system/date-time"2023" "#system/date-time\"2023\"" - #system/date-time"2023-11" "#system/date-time\"2023-11\"" - #system/date-time"2023-11-02" "#system/date-time\"2023-11-02\"" - #system/date-time"2023-11-02T14:49" "#system/date-time\"2023-11-02T14:49\"")) + #system/date"2023" "#system/date \"2023\"" + #system/date"2023-11" "#system/date \"2023-11\"" + #system/date"2023-11-02" "#system/date \"2023-11-02\"" + #system/date-time"2023" "#system/date-time \"2023\"" + #system/date-time"2023-11" "#system/date-time \"2023-11\"" + #system/date-time"2023-11-02" "#system/date-time \"2023-11-02\"" + #system/date-time"2023-11-02T14:49" "#system/date-time \"2023-11-02T14:49:00\"")) diff --git a/modules/db/build.clj b/modules/db/build.clj index c18d08051..f5f20b809 100644 --- a/modules/db/build.clj +++ b/modules/db/build.clj @@ -7,7 +7,7 @@ {:basis (b/create-basis {:project "deps.edn"}) :src-dirs ["java"] :class-dir "target/classes" - :javac-opts ["-Xlint:all" "-proc:none" "--release" "17"]})) + :javac-opts ["-Xlint:all" "-proc:none" "--release" "21"]})) (defn copy-profiles [_] (doseq [file ["Bundle-JobSearchParameterBundle" diff --git a/modules/db/src/blaze/db/impl/search_param.clj b/modules/db/src/blaze/db/impl/search_param.clj index cfe325179..84f8606f9 100644 --- a/modules/db/src/blaze/db/impl/search_param.clj +++ b/modules/db/src/blaze/db/impl/search_param.clj @@ -19,7 +19,6 @@ [blaze.db.impl.search-param.string] [blaze.db.impl.search-param.token] [blaze.fhir-path :as fhir-path] - [blaze.fhir.spec :as fhir-spec] [blaze.fhir.spec.references :as fsr] [blaze.util :refer [str]])) @@ -158,9 +157,8 @@ {:arglists '([search-param linked-compartments hash resource])} [{:keys [code c-hash] :as search-param} linked-compartments hash resource] (when-ok [triples (p/-index-values search-param stub-resolver resource)] - (let [{:keys [id]} resource - type (name (fhir-spec/fhir-type resource)) - tid (codec/tid type) + (let [{:fhir/keys [type] :keys [id]} resource + tid (codec/tid (name type)) id (codec/id-byte-string id) linked-compartments (mapv diff --git a/modules/db/src/blaze/db/impl/search_param/date.clj b/modules/db/src/blaze/db/impl/search_param/date.clj index 201483967..d3b010fea 100644 --- a/modules/db/src/blaze/db/impl/search_param/date.clj +++ b/modules/db/src/blaze/db/impl/search_param/date.clj @@ -11,8 +11,6 @@ [blaze.db.impl.search-param.core :as sc] [blaze.db.impl.search-param.util :as u] [blaze.fhir-path :as fhir-path] - [blaze.fhir.spec :as fhir-spec] - [blaze.fhir.spec.type :as type] [blaze.fhir.spec.type.system :as system] [cognitect.anomalies :as anom] [taoensso.timbre :as log])) @@ -22,26 +20,26 @@ (defmulti index-entries "Returns index entries for `value` from a resource." {:arglists '([url value])} - (fn [_ value] (fhir-spec/fhir-type value))) + (fn [_ value] (:fhir/type value))) (defmethod index-entries :fhir/date [_ date] - (when-let [value (type/value date)] + (when-let [value (:value date)] [[nil (codec-date/encode-range value)]])) (defmethod index-entries :fhir/dateTime [_ date-time] - (when-let [value (type/value date-time)] + (when-let [value (:value date-time)] [[nil (codec-date/encode-range value)]])) (defmethod index-entries :fhir/instant [_ date-time] - (when-let [value (type/value date-time)] + (when-let [value (:value date-time)] [[nil (codec-date/encode-range value)]])) (defmethod index-entries :fhir/Period [_ {:keys [start end]}] - [[nil (codec-date/encode-range (type/value start) (type/value end))]]) + [[nil (codec-date/encode-range (:value start) (:value end))]]) (defmethod index-entries :default [url value] diff --git a/modules/db/src/blaze/db/impl/search_param/near.clj b/modules/db/src/blaze/db/impl/search_param/near.clj index bf696e150..f77b17048 100644 --- a/modules/db/src/blaze/db/impl/search_param/near.clj +++ b/modules/db/src/blaze/db/impl/search_param/near.clj @@ -20,8 +20,8 @@ (set! *warn-on-reflection* true) (defn- position->coords [{:keys [longitude latitude]}] - (let [lat-val (type/value latitude) - lon-val (type/value longitude)] + (let [lat-val (:value latitude) + lon-val (:value longitude)] (when (and lat-val lon-val) {:latitude lat-val :longitude lon-val}))) @@ -108,11 +108,10 @@ (defn- distance-extension [distance] (type/extension {:url "http://hl7.org/fhir/StructureDefinition/location-distance" - :value {:fhir/type :fhir/Distance - :value (type/decimal distance) - :unit #fhir/string "m" - :system #fhir/uri "http://unitsofmeasure.org" - :code #fhir/code "m"}})) + :value (type/distance {:value (type/decimal distance) + :unit #fhir/string "m" + :system #fhir/uri "http://unitsofmeasure.org" + :code #fhir/code "m"})})) (defn- add-match-extension [meta distance] (update meta ::sp/match-extension conj-vec (distance-extension distance))) diff --git a/modules/db/src/blaze/db/impl/search_param/number.clj b/modules/db/src/blaze/db/impl/search_param/number.clj index e9afcefe6..448bb2f4a 100644 --- a/modules/db/src/blaze/db/impl/search_param/number.clj +++ b/modules/db/src/blaze/db/impl/search_param/number.clj @@ -8,8 +8,6 @@ [blaze.db.impl.search-param.quantity :as spq] [blaze.db.impl.search-param.util :as u] [blaze.fhir-path :as fhir-path] - [blaze.fhir.spec :as fhir-spec] - [blaze.fhir.spec.type :as type] [blaze.fhir.spec.type.system :as system] [cognitect.anomalies :as anom] [taoensso.timbre :as log])) @@ -19,15 +17,15 @@ (defmulti index-entries "Returns index entries for `value` from a resource." {:arglists '([url value])} - (fn [_ value] (fhir-spec/fhir-type value))) + (fn [_ value] (:fhir/type value))) (defmethod index-entries :fhir/decimal - [_ value] - [[nil (codec/number (type/value value))]]) + [_ decimal] + [[nil (codec/number (:value decimal))]]) (defn- encode-int [value] ;; TODO: we should not store the decimal form - (codec/number (BigDecimal/valueOf ^long (type/value value)))) + (codec/number (BigDecimal/valueOf ^long (:value value)))) (defmethod index-entries :fhir/integer [_ value] diff --git a/modules/db/src/blaze/db/impl/search_param/quantity.clj b/modules/db/src/blaze/db/impl/search_param/quantity.clj index 682680ab3..8fc9a7dc0 100644 --- a/modules/db/src/blaze/db/impl/search_param/quantity.clj +++ b/modules/db/src/blaze/db/impl/search_param/quantity.clj @@ -13,8 +13,6 @@ [blaze.db.impl.search-param.util :as u] [blaze.db.kv :as kv] [blaze.fhir-path :as fhir-path] - [blaze.fhir.spec :as fhir-spec] - [blaze.fhir.spec.type :as type] [blaze.fhir.spec.type.system :as system] [blaze.util :refer [str]] [clojure.string :as str] @@ -26,27 +24,36 @@ (defmulti index-entries "Returns index entries for `value` from a resource." {:arglists '([url value])} - (fn [_ value] (fhir-spec/fhir-type value))) + (fn [_ value] (:fhir/type value))) (defn- index-quantity-entries - [{:keys [value system code unit]}] - (let [system (type/value system) - code (type/value code) - unit (type/value unit)] - (when-let [value (type/value value)] - (cond-> [[nil (codec/quantity nil value)]] - code - (conj [nil (codec/quantity code value)]) - (and unit (not= unit code)) - (conj [nil (codec/quantity unit value)]) - (and system code) - (conj [nil (codec/quantity (str system "|" code) value)]))))) + [{:keys [value] {system :value} :system {code :value} :code {unit :value} :unit}] + (when-let [value (:value value)] + (cond-> [[nil (codec/quantity nil value)]] + code + (conj [nil (codec/quantity code value)]) + (and unit (not= unit code)) + (conj [nil (codec/quantity unit value)]) + (and system code) + (conj [nil (codec/quantity (str system "|" code) value)])))) -(defmethod index-entries :fhir/Quantity +(defmethod index-entries :fhir/Age [_ quantity] (index-quantity-entries quantity)) -(defmethod index-entries :fhir/Age +(defmethod index-entries :fhir/Count + [_ quantity] + (index-quantity-entries quantity)) + +(defmethod index-entries :fhir/Distance + [_ quantity] + (index-quantity-entries quantity)) + +(defmethod index-entries :fhir/Duration + [_ quantity] + (index-quantity-entries quantity)) + +(defmethod index-entries :fhir/Quantity [_ quantity] (index-quantity-entries quantity)) diff --git a/modules/db/src/blaze/db/impl/search_param/string.clj b/modules/db/src/blaze/db/impl/search_param/string.clj index e06fece76..3db388c4d 100644 --- a/modules/db/src/blaze/db/impl/search_param/string.clj +++ b/modules/db/src/blaze/db/impl/search_param/string.clj @@ -10,8 +10,6 @@ [blaze.db.impl.search-param.core :as sc] [blaze.db.impl.search-param.util :as u] [blaze.fhir-path :as fhir-path] - [blaze.fhir.spec :as fhir-spec] - [blaze.fhir.spec.type :as type] [clojure.string :as str] [taoensso.timbre :as log])) @@ -20,7 +18,7 @@ (defmulti index-entries "Returns index entries for `value` from a resource." {:arglists '([normalize value])} - (fn [_ value] (fhir-spec/fhir-type value))) + (fn [_ value] (:fhir/type value))) (defn- normalize-string [s] (-> (str/trim s) @@ -29,7 +27,7 @@ str/lower-case)) (defn- index-entry [normalize value] - (when-let [s (some-> value type/value normalize)] + (when-let [s (some-> value :value normalize)] [nil (codec/string s)])) (defmethod index-entries :fhir/string diff --git a/modules/db/src/blaze/db/impl/search_param/token.clj b/modules/db/src/blaze/db/impl/search_param/token.clj index bdef2500f..adf9a73e1 100644 --- a/modules/db/src/blaze/db/impl/search_param/token.clj +++ b/modules/db/src/blaze/db/impl/search_param/token.clj @@ -16,9 +16,8 @@ [blaze.db.impl.search-param.core :as sc] [blaze.db.impl.search-param.util :as u] [blaze.fhir-path :as fhir-path] - [blaze.fhir.spec :as fhir-spec] [blaze.fhir.spec.references :as fsr] - [blaze.fhir.spec.type :as type] + [blaze.fhir.spec.type.system :as system] [blaze.util :refer [str]] [clojure.string :as str] [taoensso.timbre :as log])) @@ -30,36 +29,40 @@ Index entries are `[modifier value include-in-compartments?]` triples." {:arglists '([url value])} - (fn [_ value] (fhir-spec/fhir-type value))) + (fn [_ value] (or (:fhir/type value) (system/type value)))) (defmethod index-entries :fhir/id [_ id] - (when-let [value (type/value id)] + (when-let [value (:value id)] [[nil (codec/v-hash value)]])) (defmethod index-entries :fhir/string [_ s] - (when-let [value (type/value s)] + (when-let [value (:value s)] [[nil (codec/v-hash value)]])) (defmethod index-entries :fhir/uri [_ uri] - (when-let [value (type/value uri)] + (when-let [value (:value uri)] [[nil (codec/v-hash value)]])) (defmethod index-entries :fhir/url [_ url] - (when-let [value (type/value url)] + (when-let [value (:value url)] [[nil (codec/v-hash value)]])) (defmethod index-entries :fhir/boolean [_ boolean] - (when-some [value (type/value boolean)] + (when-some [value (:value boolean)] [[nil (codec/v-hash (str value))]])) +(defmethod index-entries :system/boolean + [_ value] + [[nil (codec/v-hash (str value))]]) + (defmethod index-entries :fhir/canonical - [_ uri] - (when-let [value (type/value uri)] + [_ canonical] + (when-let [value (:value canonical)] (let [[url version-parts] (u/canonical-parts value)] (into [[nil (codec/v-hash value)] @@ -72,21 +75,19 @@ (defmethod index-entries :fhir/code [_ code] ;; TODO: system - (when-let [value (type/value code)] + (when-let [value (:value code)] [[nil (codec/v-hash value) true]])) -(defn token-coding-entries [{:keys [code system]}] - (let [code (type/value code) - system (type/value system)] - (cond-> [] - code - (conj [nil (codec/v-hash code)]) - system - (conj [nil (codec/v-hash (str system "|"))]) - (and code system) - (conj [nil (codec/v-hash (str system "|" code)) true]) - (and code (nil? system)) - (conj [nil (codec/v-hash (str "|" code))])))) +(defn token-coding-entries [{{code :value} :code {system :value} :system}] + (cond-> [] + code + (conj [nil (codec/v-hash code)]) + system + (conj [nil (codec/v-hash (str system "|"))]) + (and code system) + (conj [nil (codec/v-hash (str system "|" code)) true]) + (and code (nil? system)) + (conj [nil (codec/v-hash (str "|" code))]))) (defmethod index-entries :fhir/Coding [_ coding] @@ -96,25 +97,24 @@ [_ {:keys [coding]}] (coll/eduction (mapcat token-coding-entries) coding)) -(defn- identifier-entries [modifier {:keys [value system]}] - (let [value (type/value value) - system (type/value system)] - (cond-> [] - value - (conj [modifier (codec/v-hash value)]) - system - (conj [modifier (codec/v-hash (str system "|"))]) - (and value system) - (conj [modifier (codec/v-hash (str system "|" value))]) - (and value (nil? system)) - (conj [modifier (codec/v-hash (str "|" value))])))) +(defn- identifier-entries + [modifier {{:keys [value]} :value {system :value} :system}] + (cond-> [] + value + (conj [modifier (codec/v-hash value)]) + system + (conj [modifier (codec/v-hash (str system "|"))]) + (and value system) + (conj [modifier (codec/v-hash (str system "|" value))]) + (and value (nil? system)) + (conj [modifier (codec/v-hash (str "|" value))]))) (defmethod index-entries :fhir/Identifier [_ identifier] (identifier-entries nil identifier)) (defn- literal-reference-entries [reference] - (when-let [value (type/value reference)] + (when-let [value (:value reference)] (if-let [[type id] (fsr/split-literal-ref value)] [[nil (codec/v-hash id)] [nil (codec/v-hash (str type "/" id))] @@ -134,7 +134,7 @@ (defmethod index-entries :fhir/ContactPoint [_ {:keys [value]}] - (when-let [value (type/value value)] + (when-let [value (:value value)] [[nil (codec/v-hash value)]])) (defmethod index-entries :default @@ -245,8 +245,8 @@ (coll/eduction (keep (fn [value] - (when (identical? :fhir/Reference (fhir-spec/fhir-type value)) - (when-let [reference (type/value (:reference value))] + (when (identical? :fhir/Reference (:fhir/type value)) + (when-let [reference (:value (:reference value))] (when-let [[type id] (fsr/split-literal-ref reference)] (when (= "Patient" type) id)))))) @@ -262,18 +262,16 @@ (def ^:private noop-resolver (reify fhir-path/Resolver (-resolve [_ _]))) -(defn- identifier-values [{:keys [value system]}] - (let [value (type/value value) - system (type/value system)] - (cond-> [] - value - (conj value) - system - (conj (str system "|")) - (and value system) - (conj (str system "|" value)) - (and value (nil? system)) - (conj (str "|" value))))) +(defn- identifier-values [{{:keys [value]} :value {system :value} :system}] + (cond-> [] + value + (conj value) + system + (conj (str system "|")) + (and value system) + (conj (str system "|" value)) + (and value (nil? system)) + (conj (str "|" value)))) (defn- matches-identifier-values? [db expression value-set resource-handle] (let [resource @(d/pull db resource-handle) diff --git a/modules/db/src/blaze/db/impl/search_param/util.clj b/modules/db/src/blaze/db/impl/search_param/util.clj index 8093624cc..5f01459a1 100644 --- a/modules/db/src/blaze/db/impl/search_param/util.clj +++ b/modules/db/src/blaze/db/impl/search_param/util.clj @@ -11,7 +11,6 @@ [blaze.db.impl.index.resource-handle :as rh] [blaze.db.impl.index.single-version-id :as svi] [blaze.db.impl.protocols :as p] - [blaze.fhir.spec :as fhir-spec] [blaze.util :refer [str]] [clojure.string :as str]) (:import @@ -33,7 +32,7 @@ (defn format-skip-indexing-msg [value url type] (format "Skip indexing value `%s` of type `%s` for search parameter `%s` with type `%s` because the rule is missing." - (str value) (fhir-spec/fhir-type value) url type)) + (str value) (:fhir/type value) url type)) (def by-id-grouper "Returns a stateful transducer which partitions multiple consecutive diff --git a/modules/db/src/blaze/db/node.clj b/modules/db/src/blaze/db/node.clj index a587ccfb8..c1d00f769 100644 --- a/modules/db/src/blaze/db/node.clj +++ b/modules/db/src/blaze/db/node.clj @@ -174,7 +174,7 @@ (defn- enhance-resource-meta [meta t {:blaze.db.tx/keys [instant]}] (-> (or meta #fhir/Meta{}) (assoc :versionId (type/id (str t))) - (assoc :lastUpdated instant))) + (assoc :lastUpdated (node-util/instant instant)))) (defn- mk-meta [handle tx] (assoc (meta handle) diff --git a/modules/db/src/blaze/db/node/resource_indexer.clj b/modules/db/src/blaze/db/node/resource_indexer.clj index d41a85ade..e2bbc3254 100644 --- a/modules/db/src/blaze/db/node/resource_indexer.clj +++ b/modules/db/src/blaze/db/node/resource_indexer.clj @@ -14,7 +14,6 @@ [blaze.db.resource-store :as rs] [blaze.db.search-param-registry :as sr] [blaze.executors :as ex] - [blaze.fhir.spec :as fhir-spec] [blaze.module :as m :refer [reg-collector]] [clojure.spec.alpha :as s] [cognitect.anomalies :as anom] @@ -44,19 +43,18 @@ "Returns an entry into the :compartment-resource-type-index where `resource` is linked to `compartment`." {:arglists '([compartment resource])} - [[comp-code comp-id] {:keys [id] :as resource}] + [[comp-code comp-id] {:fhir/keys [type] :keys [id]}] (cr/index-entry [(codec/c-hash comp-code) (codec/id-byte-string comp-id)] - (codec/tid (name (fhir-spec/fhir-type resource))) + (codec/tid (name type)) (codec/id-byte-string id))) (defn- compartment-resource-type-entries [resource compartments] (mapv #(compartment-resource-type-entry % resource) compartments)) -(defn- skip-indexing-msg [search-param resource cause-msg] +(defn- skip-indexing-msg [search-param {:fhir/keys [type] :keys [id]} cause-msg] (format "Skip indexing for search parameter `%s` on resource `%s/%s`. Cause: %s" - (:url search-param) (name (fhir-spec/fhir-type resource)) - (:id resource) (or cause-msg ""))) + (:url search-param) (name type) id (or cause-msg ""))) (defn- search-param-index-entries [search-param linked-compartments hash resource] @@ -124,7 +122,7 @@ {:arglists '([resource-indexer tx-data])} [{:keys [resource-store] :as resource-indexer} {:keys [tx-cmds] resources :local-payload last-updated :instant}] - (let [context (assoc resource-indexer :last-updated last-updated)] + (let [context (assoc resource-indexer :last-updated (node-util/instant last-updated))] (if resources (index-resources* context resources) (-> (rs/multi-get resource-store (cmd-rs-keys tx-cmds :complete)) diff --git a/modules/db/src/blaze/db/node/transaction.clj b/modules/db/src/blaze/db/node/transaction.clj index dacdf7051..be1acd061 100644 --- a/modules/db/src/blaze/db/node/transaction.clj +++ b/modules/db/src/blaze/db/node/transaction.clj @@ -5,7 +5,6 @@ [blaze.db.impl.index.tx-error :as tx-error] [blaze.db.impl.index.tx-success :as tx-success] [blaze.fhir.hash :as hash] - [blaze.fhir.spec :as fhir-spec] [blaze.fhir.spec.type :as type] [taoensso.timbre :as log])) @@ -20,7 +19,7 @@ :blaze.db/tx-cmd (cond-> {:op (name op) - :type (name (fhir-spec/fhir-type resource)) + :type (name (:fhir/type resource)) :id (:id resource) :hash hash} (seq refs) @@ -42,7 +41,7 @@ :blaze.db/tx-cmd (cond-> {:op (name op) - :type (name (fhir-spec/fhir-type resource)) + :type (name (:fhir/type resource)) :id (:id resource) :hash hash} (seq refs) diff --git a/modules/db/src/blaze/db/node/util.clj b/modules/db/src/blaze/db/node/util.clj index 6408bade3..ad9d3f677 100644 --- a/modules/db/src/blaze/db/node/util.clj +++ b/modules/db/src/blaze/db/node/util.clj @@ -1,8 +1,13 @@ (ns blaze.db.node.util (:refer-clojure :exclude [str]) (:require + [blaze.fhir.spec.type :as type] [blaze.util :refer [str]] - [clojure.string :as str])) + [clojure.string :as str]) + (:import + [java.time Instant ZoneOffset])) + +(set! *warn-on-reflection* true) (defn name-part [[_ key]] (-> key namespace (str/split #"\.") last)) @@ -21,3 +26,6 @@ "Returns the resource-store key of `resource-handle` in `variant`." [resource-handle variant] [(:fhir/type resource-handle) (:hash resource-handle) variant]) + +(defn instant [last-updated] + (type/instant (.atOffset ^Instant last-updated ZoneOffset/UTC))) diff --git a/modules/db/src/blaze/db/resource_cache.clj b/modules/db/src/blaze/db/resource_cache.clj index 511527b50..b8e05f3b4 100644 --- a/modules/db/src/blaze/db/resource_cache.clj +++ b/modules/db/src/blaze/db/resource_cache.clj @@ -16,8 +16,9 @@ [integrant.core :as ig] [taoensso.timbre :as log]) (:import + [blaze.fhir.spec.type Base] [com.github.benmanes.caffeine.cache - AsyncCacheLoader AsyncLoadingCache Caffeine] + AsyncCacheLoader AsyncLoadingCache Caffeine Weigher] [com.github.benmanes.caffeine.cache.stats CacheStats] [java.lang.reflect Array] [java.util ArrayList Collection Collections HashMap Map] @@ -33,11 +34,6 @@ [cache key] (p/-get cache key)) -(defn contains? - "Returns true if `cache` contains `key`." - [cache key] - (p/-contains? cache key)) - (defn multi-get "Returns a CompletableFuture that will complete with a map from `key` to the resource content of all found `keys`. @@ -75,9 +71,6 @@ (-get [_ key] (.get cache key)) - (-contains? [_ key] - (some? (.getIfPresent cache key))) - (-multi-get [_ keys] (.getAll cache keys)) @@ -92,7 +85,9 @@ keys) (cond-> (wait-for-all futures) (pos? (.size keys-to-load)) - (merge-futures (rs/multi-get resource-store (Collections/unmodifiableList keys-to-load)))))) + ;; use the protocol method directly because keys will not + ;; satisfy the spec of the rs/multi-get function + (merge-futures (rs/-multi-get resource-store (Collections/unmodifiableList keys-to-load)))))) ccp/StatsCache (-stats [_] @@ -104,42 +99,54 @@ (-> (.synchronous ^AsyncLoadingCache (.cache ^DefaultResourceCache resource-cache)) (.invalidateAll))) +(def ^:private weigher + (reify Weigher + (weigh [_ _ resource] + (Base/memSize resource)))) + (defmethod m/pre-init-spec :blaze.db/resource-cache [_] - (s/keys :req-un [:blaze.db/resource-store] :opt-un [::max-size])) + (s/keys :req-un [:blaze.db/resource-store] :opt-un [::max-size-ratio])) + +(def ^:private ^:const default-max-size-ratio 0.25) +(def ^:private ^:const max-max-size-ratio 0.8) (defmethod ig/init-key :blaze.db/resource-cache - [_ {:keys [resource-store max-size] :or {max-size 0}}] - (log/info "Create resource cache with a size of" max-size "resources") - (if (zero? max-size) - (reify - p/ResourceCache - (-get [_ key] - (rs/get resource-store key)) - - (-contains? [_ _] - false) - - (-multi-get [_ keys] - (rs/multi-get resource-store keys)) - - (-multi-get-skip-cache-insertion [_ keys] - (rs/multi-get resource-store keys)) - - ccp/StatsCache - (-stats [_] - (CacheStats/empty)) - (-estimated-size [_] - 0)) - - (->DefaultResourceCache - (-> (Caffeine/newBuilder) - (.maximumSize max-size) - (.recordStats) - (.buildAsync - (reify AsyncCacheLoader - (asyncLoad [_ key _] - (rs/get resource-store key)) - - (asyncLoadAll [_ keys _] - (rs/multi-get resource-store keys))))) - resource-store))) + [_ {:keys [resource-store max-size-ratio] :or {max-size-ratio default-max-size-ratio}}] + (let [max-memory (.maxMemory (Runtime/getRuntime)) + max-size-ratio (if (< max-max-size-ratio max-size-ratio) max-max-size-ratio max-size-ratio) + max-size-in-bytes (long (* max-memory max-size-ratio))] + (log/info (format "Create resource cache with a memory size of %d MiB (%d%% of max memory size)" + (bit-shift-right max-size-in-bytes 20) (long (* 100 max-size-ratio)))) + (if (zero? max-size-in-bytes) + (reify + p/ResourceCache + (-get [_ key] + (rs/get resource-store key)) + + (-multi-get [_ keys] + (rs/multi-get resource-store keys)) + + (-multi-get-skip-cache-insertion [_ keys] + (rs/multi-get resource-store keys)) + + ccp/StatsCache + (-stats [_] + (CacheStats/empty)) + (-estimated-size [_] + 0)) + + (->DefaultResourceCache + (-> (Caffeine/newBuilder) + (.weigher weigher) + (.maximumWeight max-size-in-bytes) + (.recordStats) + (.buildAsync + (reify AsyncCacheLoader + (asyncLoad [_ key _] + (rs/get resource-store key)) + + (asyncLoadAll [_ keys _] + ;; use the protocol method directly because keys will not + ;; satisfy the spec of the rs/multi-get function + (rs/-multi-get resource-store keys))))) + resource-store)))) diff --git a/modules/db/src/blaze/db/resource_cache/protocol.clj b/modules/db/src/blaze/db/resource_cache/protocol.clj index 0d636438c..1fc9c45b0 100644 --- a/modules/db/src/blaze/db/resource_cache/protocol.clj +++ b/modules/db/src/blaze/db/resource_cache/protocol.clj @@ -3,8 +3,6 @@ (defprotocol ResourceCache (-get [cache key]) - (-contains? [cache key]) - (-multi-get [cache key]) (-multi-get-skip-cache-insertion [cache key])) diff --git a/modules/db/src/blaze/db/resource_cache/spec.clj b/modules/db/src/blaze/db/resource_cache/spec.clj index d7959999c..26ba81a1e 100644 --- a/modules/db/src/blaze/db/resource_cache/spec.clj +++ b/modules/db/src/blaze/db/resource_cache/spec.clj @@ -6,5 +6,5 @@ (s/def :blaze.db/resource-cache #(satisfies? p/ResourceCache %)) -(s/def :blaze.db.resource-cache/max-size - nat-int?) +(s/def :blaze.db.resource-cache/max-size-ratio + (s/or :int int? :double double?)) diff --git a/modules/db/test-perf/blaze/db/api_test_perf.clj b/modules/db/test-perf/blaze/db/api_test_perf.clj index 7492dc745..c6ea6fa10 100644 --- a/modules/db/test-perf/blaze/db/api_test_perf.clj +++ b/modules/db/test-perf/blaze/db/api_test_perf.clj @@ -122,7 +122,7 @@ #fhir/CodeableConcept {:coding [#fhir/Coding - {:system #fhir/uri "system-191514" + {:system #fhir/uri-interned "system-191514" :code #fhir/code "code-191518"}]}}])) (deftest type-test diff --git a/modules/db/test/blaze/db/api_test.clj b/modules/db/test/blaze/db/api_test.clj index 2edf4b3f9..dc25bf583 100644 --- a/modules/db/test/blaze/db/api_test.clj +++ b/modules/db/test/blaze/db/api_test.clj @@ -23,7 +23,6 @@ [blaze.db.tx-log-spec] [blaze.db.tx-log.local-spec] [blaze.fhir.hash :as hash] - [blaze.fhir.spec :as fhir-spec] [blaze.fhir.spec.generators :as fg] [blaze.fhir.spec.type :as type] [blaze.fhir.spec.type.system :as system] @@ -168,7 +167,7 @@ (= (count tx-ops) (count (into #{} (map (comp :id second)) tx-ops)))) (defn- create-tx [resource-gen max-ops] - (gen/such-that unique-ids? (gen/vector (create-tx-op resource-gen) 1 max-ops))) + (gen/such-that unique-ids? (gen/vector (create-tx-op resource-gen) 1 max-ops) 1000)) (defn- kebab->pascal [s] (.to CaseFormat/LOWER_HYPHEN CaseFormat/UPPER_CAMEL s)) @@ -207,7 +206,7 @@ (concat observations encounters procedures medication-administrations)))) (defn- pull-resource [db type id] - (d/pull db (d/resource-handle db type id))) + (some->> (d/resource-handle db type id) (d/pull db))) (deftest transact-create-test (testing "one Patient" @@ -276,9 +275,11 @@ @(d/pull-many node-or-db (vec (d/type-list (ensure-db node-or-db) type start-id))))) (deftest ^:slow transact-create-property-test - (doseq [gen `[fg/patient fg/observation fg/encounter fg/procedure - fg/allergy-intolerance fg/diagnostic-report fg/library]] - (satisfies-prop 20 + (doseq [gen `[fg/activity-definition fg/allergy-intolerance fg/bundle fg/claim + fg/code-system fg/condition fg/consent fg/diagnostic-report + fg/encounter fg/imaging-study fg/library fg/medication-administration + fg/observation fg/patient fg/procedure fg/task fg/value-set]] + (satisfies-prop 10 (prop/for-all [tx-ops (create-tx ((resolve gen)) 20)] (with-system-data [{:blaze.db/keys [node]} config] [tx-ops] @@ -328,9 +329,9 @@ (testing "on multiple matching Patients" (with-system-data [{:blaze.db/keys [node]} config] [[[:put {:fhir/type :fhir/Patient :id "0" - :birthDate #fhir/date "2020"}] + :birthDate #fhir/date #system/date "2020"}] [:put {:fhir/type :fhir/Patient :id "1" - :birthDate #fhir/date "2020"}]]] + :birthDate #fhir/date #system/date "2020"}]]] (testing "causes a transaction abort with conflict" (given-failed-future @@ -392,7 +393,7 @@ :birthDate #fhir/date {:extension [#fhir/Extension{:url "foo" :value #fhir/code "bar"}] - :value "2022"}}]]] + :value #system/date "2022"}}]]] (testing "the Patient was created" (given @(pull-resource (d/db node) "Patient" "0") @@ -1797,14 +1798,13 @@ (is (= 1 (d/since-t batch-db))))) (testing "multiple calls to since" - (st/unstrument) (is (= 1 (-> db-2 (d/since Instant/EPOCH) (d/since inst-1) d/since-t)))) (let [db-1->2 (d/since db-2 inst-1) dbs [db-1->2 db-2]] (testing "since db looks the same as db" (doseq [db dbs] - (given @(d/pull node (d/resource-handle db "Patient" "0")) + (given @(pull-resource db "Patient" "0") :id := "0" [:meta :versionId] := #fhir/id "2" :gender := #fhir/code "female")) @@ -1888,7 +1888,7 @@ dbs [db2->4 db-4]] (testing "since db looks the same as db for changed instances" (doseq [db dbs] - (given @(d/pull node (d/resource-handle db "Patient" "1")) + (given @(pull-resource db "Patient" "1") :id := "1" [:meta :versionId] := #fhir/id "4" :gender := #fhir/code "female")) @@ -1909,7 +1909,7 @@ (is (nil? (d/resource-handle db2->4 "Patient" "0"))) - (given @(d/pull node (d/resource-handle db2->4 "Patient" "1")) + (given @(pull-resource db2->4 "Patient" "1") :id := "1" [:meta :versionId] := #fhir/id "4") @@ -2011,11 +2011,11 @@ (let [resource-handle (d/resource-handle (d/db node) "Patient" "0")] - (testing "is not deleted" - (is (not (d/deleted? resource-handle)))) - (testing "is actually one" - (is (d/resource-handle? resource-handle)))))) + (is (some? (d/resource-handle? resource-handle)))) + + (testing "is not deleted" + (is (not (d/deleted? resource-handle))))))) (testing "doesn't find a resource handle mit prefix of it's id" (with-system-data [{:blaze.db/keys [node]} config] @@ -2091,9 +2091,9 @@ count := 1 [0 :fhir/type] := :fhir/Patient [0 :id] := "0" - [0 :meta fhir-spec/fhir-type] := :fhir/Meta + [0 :meta :fhir/type] := :fhir/Meta [0 :meta :versionId] := #fhir/id "1" - [0 :meta :lastUpdated] := Instant/EPOCH)))) + [0 :meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")))) (testing "a node with one deleted patient" (with-system-data [{:blaze.db/keys [node]} config] @@ -2496,7 +2496,7 @@ :identifier [#fhir/Identifier{:value #fhir/string "0"}] :active #fhir/boolean false :gender #fhir/code "male" - :birthDate #fhir/date "2020-02-08" + :birthDate #fhir/date #system/date "2020-02-08" :deceased #fhir/boolean true :address [#fhir/Address{:line [#fhir/string "Philipp-Rosenthal-Straße 27"] @@ -2505,28 +2505,28 @@ [:put {:fhir/type :fhir/Patient :id "id-1" :active #fhir/boolean true :gender #fhir/code "female" - :birthDate #fhir/date "2020-02" + :birthDate #fhir/date #system/date "2020-02" :address [#fhir/Address{:city #fhir/string "Berlin"}] :telecom - [{:fhir/type :fhir/ContactPoint - :system #fhir/code "email" - :value #fhir/string "foo@bar.baz"} - {:fhir/type :fhir/ContactPoint - :system #fhir/code "phone" - :value #fhir/string "0815"}]}] + [#fhir/ContactPoint + {:system #fhir/code "email" + :value #fhir/string "foo@bar.baz"} + #fhir/ContactPoint + {:system #fhir/code "phone" + :value #fhir/string "0815"}]}] [:put {:fhir/type :fhir/Patient :id "id-2" :active #fhir/boolean false :gender #fhir/code "female" - :birthDate #fhir/date "2020" - :deceased #fhir/dateTime "2020-03" + :birthDate #fhir/date #system/date "2020" + :deceased #fhir/dateTime #system/date-time "2020-03" :address [#fhir/Address{:line [#fhir/string "Liebigstraße 20a"] :city #fhir/string "Leipzig"}] :name [#fhir/HumanName{:family #fhir/string "Schmidt"}]}] [:put {:fhir/type :fhir/Patient :id "id-3" - :birthDate #fhir/date "2019"}] + :birthDate #fhir/date #system/date "2019"}] [:put {:fhir/type :fhir/Patient :id "id-4" - :birthDate #fhir/date "2021"}] + :birthDate #fhir/date #system/date "2021"}] [:put {:fhir/type :fhir/Patient :id "id-5"}]] [[:delete "Patient" "id-5"]]] @@ -3351,13 +3351,13 @@ [:put {:fhir/type :fhir/TestScript :id "id-0" :useContext - [{:fhir/type :fhir/UsageContext - :value - #fhir/Quantity - {:value #fhir/decimal 0M - :unit #fhir/string "m" - :code #fhir/code "m" - :system #fhir/uri "http://unitsofmeasure.org"}}]}]]] + [#fhir/UsageContext + {:value + #fhir/Quantity + {:value #fhir/decimal 0M + :unit #fhir/string "m" + :code #fhir/code "m" + :system #fhir/uri "http://unitsofmeasure.org"}}]}]]] (testing "ResourceSearchParamValue index looks like it should" (is (= (r-sp-v-tu/decode-index-entries @@ -3377,13 +3377,13 @@ "combo-value-quantity" #blaze/byte-string"9B780D9180"] ["Observation" "id-0" #blaze/hash-prefix"36A9F36D" "_lastUpdated" #blaze/byte-string"80008001"] - ["TestScript" "id-0" #blaze/hash-prefix"51E67D28" + ["TestScript" "id-0" #blaze/hash-prefix"9668006A" "context-quantity" #blaze/byte-string"0000000080"] - ["TestScript" "id-0" #blaze/hash-prefix"51E67D28" + ["TestScript" "id-0" #blaze/hash-prefix"9668006A" "context-quantity" #blaze/byte-string"5C38E45A80"] - ["TestScript" "id-0" #blaze/hash-prefix"51E67D28" + ["TestScript" "id-0" #blaze/hash-prefix"9668006A" "context-quantity" #blaze/byte-string"9B780D9180"] - ["TestScript" "id-0" #blaze/hash-prefix"51E67D28" + ["TestScript" "id-0" #blaze/hash-prefix"9668006A" "_lastUpdated" #blaze/byte-string"80008001"]]))) (testing "TestScript would be found" @@ -3402,22 +3402,22 @@ (with-system-data [{:blaze.db/keys [node]} config] [[[:put {:fhir/type :fhir/Patient :id "id-0" - :birthDate #fhir/date "1900"}] + :birthDate #fhir/date #system/date "1900"}] [:put {:fhir/type :fhir/Patient :id "id-1" - :birthDate #fhir/date "1960"}] + :birthDate #fhir/date #system/date "1960"}] [:put {:fhir/type :fhir/Patient :id "id-2" - :birthDate #fhir/date "1970"}] + :birthDate #fhir/date #system/date "1970"}] [:put {:fhir/type :fhir/Patient :id "id-3" - :birthDate #fhir/date "1980"}] + :birthDate #fhir/date #system/date "1980"}] [:put {:fhir/type :fhir/Patient :id "id-4" - :birthDate #fhir/date "2020"}] + :birthDate #fhir/date #system/date "2020"}] [:put {:fhir/type :fhir/Patient :id "id-5" - :birthDate #fhir/date "2100"}]]] + :birthDate #fhir/date #system/date "2100"}]]] (given-type-query node "Patient" [["birthdate" "ge1900"]] count := 6 @@ -3441,13 +3441,13 @@ :code #fhir/code "code-164847"}]} :prediction [{:fhir/type :fhir.RiskAssessment/prediction - :probability 0.9M}]}] + :probability #fhir/decimal 0.9M}]}] [:put {:fhir/type :fhir/RiskAssessment :id "id-1" :status #fhir/code "final" :prediction [{:fhir/type :fhir.RiskAssessment/prediction - :probability 0.1M}]}] + :probability #fhir/decimal 0.1M}]}] [:put {:fhir/type :fhir/RiskAssessment :id "id-2" :method @@ -3810,22 +3810,22 @@ [[[:put {:fhir/type :fhir/Patient :id "0" :active #fhir/boolean true}]] [[:put {:fhir/type :fhir/Patient :id "0" :active #fhir/boolean false}]] [[:put {:fhir/type :fhir/Patient :id "0" :active #fhir/boolean true - :birthDate #fhir/date "2020"}]]] + :birthDate #fhir/date #system/date "2020"}]]] (given-type-query node "Patient" [["active" "true"]] count := 1 [0 :fhir/type] := :fhir/Patient [0 :id] := "0" - [0 :birthDate] := #fhir/date "2020"))) + [0 :birthDate] := #fhir/date #system/date "2020"))) (testing "date search param" (with-system-data [{:blaze.db/keys [node]} config] [[[:put {:fhir/type :fhir/Patient :id "0" :active #fhir/boolean true - :birthDate #fhir/date "2025"}]] + :birthDate #fhir/date #system/date "2025"}]] [[:put {:fhir/type :fhir/Patient :id "0" :active #fhir/boolean true - :birthDate #fhir/date "2024"}]] + :birthDate #fhir/date #system/date "2024"}]] [[:put {:fhir/type :fhir/Patient :id "0" :active #fhir/boolean true - :birthDate #fhir/date "2025" + :birthDate #fhir/date #system/date "2025" :gender #fhir/code "female"}]]] (doseq [clauses [[["birthdate" "2025"]] @@ -3842,7 +3842,7 @@ [[[:put {:fhir/type :fhir/Patient :id "0" :active #fhir/boolean true}]] [[:put {:fhir/type :fhir/Patient :id "0" :active #fhir/boolean false}]] [[:put {:fhir/type :fhir/Patient :id "0" :active #fhir/boolean true - :birthDate #fhir/date "2020"}]] + :birthDate #fhir/date #system/date "2020"}]] [[:put {:fhir/type :fhir/Patient :id "0" :active #fhir/boolean false}]]] (given-type-query node "Patient" [["active" "true"]] @@ -4140,9 +4140,7 @@ {:system #fhir/uri "http://fhir.de/CodeSystem/dimdi/icd-10-gm" :code #fhir/code "C71.4"}]} :subject #fhir/Reference{:reference #fhir/string "Patient/id-0"} - :onset - {:fhir/type :fhir/Age - :value #fhir/decimal 63M}}] + :onset #fhir/Age{:value #fhir/decimal 63M}}] [:put {:fhir/type :fhir/Condition :id "id-1"}]]] (testing "patient" @@ -4262,8 +4260,8 @@ :status #fhir/code "final" :effective #fhir/Period - {:start #fhir/dateTime "2021-02-23T15:12:45+01:00" - :end #fhir/dateTime "2021-02-23T16:00:00+01:00"} + {:start #fhir/dateTime #system/date-time "2021-02-23T15:12:45+01:00" + :end #fhir/dateTime #system/date-time "2021-02-23T16:00:00+01:00"} :value #fhir/Quantity {:value #fhir/decimal 0M @@ -4273,7 +4271,7 @@ [:put {:fhir/type :fhir/Observation :id "id-1" :meta #fhir/Meta{:profile [#fhir/canonical "http://example.com/profile-uri-091902|1.1.0"]} :status #fhir/code "final" - :effective #fhir/dateTime "2021-02-25" + :effective #fhir/dateTime #system/date-time "2021-02-25" :value #fhir/Quantity {:value #fhir/decimal 1M @@ -5585,10 +5583,10 @@ [[[:put {:fhir/type :fhir/Patient :id "0"}] [:put {:fhir/type :fhir/Patient :id "1"}] [:put {:fhir/type :fhir/Observation :id "0" - :effective #fhir/dateTime "1990-06-14T12:24:48Z" + :effective #fhir/dateTime #system/date-time "1990-06-14T12:24:48Z" :subject #fhir/Reference{:reference #fhir/string "Patient/0"}}] [:put {:fhir/type :fhir/Observation :id "1" - :effective #fhir/dateTime "1990-06-14T12:24:48Z" + :effective #fhir/dateTime #system/date-time "1990-06-14T12:24:48Z" :subject #fhir/Reference{:reference #fhir/string "Patient/1"}}]]] (testing "as first clause" @@ -5742,15 +5740,13 @@ (deftest type-query-encounter-test (testing "duplicates are removed" (with-system-data [{:blaze.db/keys [node]} config] - [[[:put {:fhir/type :fhir/Encounter - :id "0" + [[[:put {:fhir/type :fhir/Encounter :id "0" :diagnosis [{:fhir/type :fhir.Encounter/diagnosis :condition #fhir/Reference{:reference #fhir/string "Condition/0"}} {:fhir/type :fhir.Encounter/diagnosis :condition #fhir/Reference{:reference #fhir/string "Condition/1"}}]}] - [:put {:fhir/type :fhir/Encounter - :id "1" + [:put {:fhir/type :fhir/Encounter :id "1" :diagnosis [{:fhir/type :fhir.Encounter/diagnosis :condition #fhir/Reference{:reference #fhir/string "Condition/1"}} @@ -5769,7 +5765,28 @@ (testing "on pulling the second page" (given (pull-type-query node "Encounter" [["diagnosis" "Condition/0" "Condition/1" "Condition/2"]] "1") count := 1 - [0 :id] := "1"))))) + [0 :id] := "1")))) + + (testing "Encounter.length (Duration)" + (with-system-data [{:blaze.db/keys [node]} config] + [[[:put {:fhir/type :fhir/Encounter :id "0" + :length #fhir/Duration{:value #fhir/decimal 1M :code #fhir/code"s"}}] + [:put {:fhir/type :fhir/Encounter :id "1" + :length #fhir/Duration{:value #fhir/decimal 2M :code #fhir/code"s"}}] + [:put {:fhir/type :fhir/Encounter :id "2" + :length #fhir/Duration{:value #fhir/decimal 1M :code #fhir/code"s"}}]]] + + (let [clauses [["length" "1|s"]]] + (testing "on pulling all resource handles" + (given-type-query node "Encounter" clauses + count := 2 + [0 :id] := "0" + [1 :id] := "2")) + + (testing "it is possible to start with the second observation" + (given (pull-type-query node "Encounter" clauses "2") + count := 1 + [0 :id] := "2")))))) (deftest type-query-multiple-clauses-test (testing "with two token search params" @@ -5853,16 +5870,16 @@ (with-system-data [{:blaze.db/keys [node]} config] [[[:put {:fhir/type :fhir/Observation :id "0" :status #fhir/code "final" - :effective #fhir/dateTime "2025"}] + :effective #fhir/dateTime #system/date-time "2025"}] [:put {:fhir/type :fhir/Observation :id "1" :status #fhir/code "preliminary" - :effective #fhir/dateTime "2025"}] + :effective #fhir/dateTime #system/date-time "2025"}] [:put {:fhir/type :fhir/Observation :id "2" :status #fhir/code "final" - :effective #fhir/dateTime "2026"}] + :effective #fhir/dateTime #system/date-time "2026"}] [:put {:fhir/type :fhir/Observation :id "3" :status #fhir/code "preliminary" - :effective #fhir/dateTime "2026"}]]] + :effective #fhir/dateTime #system/date-time "2026"}]]] (let [clauses [["status" "final"] ["date" "2025"]]] (given-type-query node "Observation" clauses @@ -5910,19 +5927,19 @@ (with-system-data [{:blaze.db/keys [node]} config] [[[:put {:fhir/type :fhir/Observation :id "0" :status #fhir/code "final" - :effective #fhir/dateTime "1990-06-14T12:24:47Z"}] + :effective #fhir/dateTime #system/date-time "1990-06-14T12:24:47Z"}] [:put {:fhir/type :fhir/Observation :id "1" :status #fhir/code "final" - :effective #fhir/dateTime "1990-06-14T12:24:48Z"}] + :effective #fhir/dateTime #system/date-time "1990-06-14T12:24:48Z"}] [:put {:fhir/type :fhir/Observation :id "2" :status #fhir/code "final" - :effective #fhir/dateTime "1990-06-14T12:24:48Z"}] + :effective #fhir/dateTime #system/date-time "1990-06-14T12:24:48Z"}] [:put {:fhir/type :fhir/Observation :id "3" :status #fhir/code "final" - :effective #fhir/dateTime "1990-06-14T12:24:48Z"}] + :effective #fhir/dateTime #system/date-time "1990-06-14T12:24:48Z"}] [:put {:fhir/type :fhir/Observation :id "4" :status #fhir/code "final" - :effective #fhir/dateTime "1990-06-14T12:24:49Z"}]]] + :effective #fhir/dateTime #system/date-time "1990-06-14T12:24:49Z"}]]] (let [clauses [["date" "1990-06-14T12:24:48Z"]]] (given-type-query node "Observation" clauses @@ -5969,19 +5986,19 @@ (with-system-data [{:blaze.db/keys [node]} config] [[[:put {:fhir/type :fhir/Observation :id "0" :status #fhir/code "final" - :effective #fhir/dateTime "1990-06-14T12:24:47Z"}] + :effective #fhir/dateTime #system/date-time "1990-06-14T12:24:47Z"}] [:put {:fhir/type :fhir/Observation :id "1" :status #fhir/code "final" - :effective #fhir/dateTime "1990-06-14T12:24:48Z"}] + :effective #fhir/dateTime #system/date-time "1990-06-14T12:24:48Z"}] [:put {:fhir/type :fhir/Observation :id "2" :status #fhir/code "final" - :effective #fhir/dateTime "1990-06-14T12:24:48Z"}] + :effective #fhir/dateTime #system/date-time "1990-06-14T12:24:48Z"}] [:put {:fhir/type :fhir/Observation :id "3" :status #fhir/code "final" - :effective #fhir/dateTime "1990-06-14T12:24:48Z"}] + :effective #fhir/dateTime #system/date-time "1990-06-14T12:24:48Z"}] [:put {:fhir/type :fhir/Observation :id "4" :status #fhir/code "final" - :effective #fhir/dateTime "1990-06-14T12:24:49Z"}]]] + :effective #fhir/dateTime #system/date-time "1990-06-14T12:24:49Z"}]]] (given-type-query node "Observation" [["date" "ne1990-06-14T12:24:48Z"]] count := 2 @@ -6010,11 +6027,11 @@ (testing "year precision" (with-system-data [{:blaze.db/keys [node]} config] [[[:put {:fhir/type :fhir/Patient :id "0" - :birthDate #fhir/date "1990"}] + :birthDate #fhir/date #system/date "1990"}] [:put {:fhir/type :fhir/Patient :id "1" - :birthDate #fhir/date "1991"}] + :birthDate #fhir/date #system/date "1991"}] [:put {:fhir/type :fhir/Patient :id "2" - :birthDate #fhir/date "1992"}]]] + :birthDate #fhir/date #system/date "1992"}]]] (given-type-query node "Patient" [["birthdate" "gt1990"]] count := 2 @@ -6029,9 +6046,9 @@ (testing "day precision" (with-system-data [{:blaze.db/keys [node]} config] [[[:put {:fhir/type :fhir/Patient :id "0" - :birthDate #fhir/date "2022-12-14"}] + :birthDate #fhir/date #system/date "2022-12-14"}] [:put {:fhir/type :fhir/Patient :id "1" - :birthDate #fhir/date "2022-12-15"}]]] + :birthDate #fhir/date #system/date "2022-12-15"}]]] (given-type-query node "Patient" [["birthdate" "gt2022-12-14"]] count := 1 @@ -6041,10 +6058,10 @@ (with-system-data [{:blaze.db/keys [node]} config] [[[:put {:fhir/type :fhir/Patient :id "0" :gender #fhir/code "male" - :birthDate #fhir/date "2022-12-14"}] + :birthDate #fhir/date #system/date "2022-12-14"}] [:put {:fhir/type :fhir/Patient :id "1" :gender #fhir/code "male" - :birthDate #fhir/date "2022-12-15"}]]] + :birthDate #fhir/date #system/date "2022-12-15"}]]] (given-type-query node "Patient" [["gender" "male"] ["birthdate" "gt2022-12-14"]] @@ -6055,13 +6072,13 @@ (testing "year precision" (with-system-data [{:blaze.db/keys [node]} config] [[[:put {:fhir/type :fhir/Patient :id "0" - :birthDate #fhir/date "1970"}]] + :birthDate #fhir/date #system/date "1970"}]] [[:put {:fhir/type :fhir/Patient :id "0" - :birthDate #fhir/date "1990"}] + :birthDate #fhir/date #system/date "1990"}] [:put {:fhir/type :fhir/Patient :id "1" - :birthDate #fhir/date "1989"}] + :birthDate #fhir/date #system/date "1989"}] [:put {:fhir/type :fhir/Patient :id "2" - :birthDate #fhir/date "1988"}]]] + :birthDate #fhir/date #system/date "1988"}]]] (doseq [clauses [[["birthdate" "lt1990"]] [["birthdate" "lt1990" "le1989"]]]] @@ -6078,9 +6095,9 @@ (testing "day precision" (with-system-data [{:blaze.db/keys [node]} config] [[[:put {:fhir/type :fhir/Patient :id "0" - :birthDate #fhir/date "2022-12-14"}] + :birthDate #fhir/date #system/date "2022-12-14"}] [:put {:fhir/type :fhir/Patient :id "1" - :birthDate #fhir/date "2022-12-13"}]]] + :birthDate #fhir/date #system/date "2022-12-13"}]]] (given-type-query node "Patient" [["birthdate" "lt2022-12-14"]] count := 1 @@ -6090,10 +6107,10 @@ (with-system-data [{:blaze.db/keys [node]} config] [[[:put {:fhir/type :fhir/Patient :id "0" :gender #fhir/code "male" - :birthDate #fhir/date "2022-12-14"}] + :birthDate #fhir/date #system/date "2022-12-14"}] [:put {:fhir/type :fhir/Patient :id "1" :gender #fhir/code "male" - :birthDate #fhir/date "2022-12-13"}]]] + :birthDate #fhir/date #system/date "2022-12-13"}]]] (given-type-query node "Patient" [["gender" "male"] ["birthdate" "lt2022-12-14"]] @@ -6104,11 +6121,11 @@ (testing "year precision" (with-system-data [{:blaze.db/keys [node]} config] [[[:put {:fhir/type :fhir/Patient :id "0" - :birthDate #fhir/date "1990"}] + :birthDate #fhir/date #system/date "1990"}] [:put {:fhir/type :fhir/Patient :id "1" - :birthDate #fhir/date "1991"}] + :birthDate #fhir/date #system/date "1991"}] [:put {:fhir/type :fhir/Patient :id "2" - :birthDate #fhir/date "1992"}]]] + :birthDate #fhir/date #system/date "1992"}]]] (given-type-query node "Patient" [["birthdate" "ge1990"]] count := 3 @@ -6125,9 +6142,9 @@ (testing "day precision" (with-system-data [{:blaze.db/keys [node]} config] [[[:put {:fhir/type :fhir/Patient :id "0" - :birthDate #fhir/date "2022-12-14"}] + :birthDate #fhir/date #system/date "2022-12-14"}] [:put {:fhir/type :fhir/Patient :id "1" - :birthDate #fhir/date "2022-12-15"}]]] + :birthDate #fhir/date #system/date "2022-12-15"}]]] (given-type-query node "Patient" [["birthdate" "ge2022-12-14"]] count := 2 @@ -6138,10 +6155,10 @@ (with-system-data [{:blaze.db/keys [node]} config] [[[:put {:fhir/type :fhir/Patient :id "0" :gender #fhir/code "male" - :birthDate #fhir/date "2022-12-14"}] + :birthDate #fhir/date #system/date "2022-12-14"}] [:put {:fhir/type :fhir/Patient :id "1" :gender #fhir/code "male" - :birthDate #fhir/date "2022-12-15"}]]] + :birthDate #fhir/date #system/date "2022-12-15"}]]] (given-type-query node "Patient" [["gender" "male"] ["birthdate" "ge2022-12-14"]] @@ -6153,13 +6170,13 @@ (testing "year precision" (with-system-data [{:blaze.db/keys [node]} config] [[[:put {:fhir/type :fhir/Patient :id "0" - :birthDate #fhir/date "1970"}]] + :birthDate #fhir/date #system/date "1970"}]] [[:put {:fhir/type :fhir/Patient :id "0" - :birthDate #fhir/date "1990"}] + :birthDate #fhir/date #system/date "1990"}] [:put {:fhir/type :fhir/Patient :id "1" - :birthDate #fhir/date "1989"}] + :birthDate #fhir/date #system/date "1989"}] [:put {:fhir/type :fhir/Patient :id "2" - :birthDate #fhir/date "1988"}]]] + :birthDate #fhir/date #system/date "1988"}]]] (given-type-query node "Patient" [["birthdate" "le1990"]] count := 3 @@ -6176,9 +6193,9 @@ (testing "day precision" (with-system-data [{:blaze.db/keys [node]} config] [[[:put {:fhir/type :fhir/Patient :id "0" - :birthDate #fhir/date "2022-12-14"}] + :birthDate #fhir/date #system/date "2022-12-14"}] [:put {:fhir/type :fhir/Patient :id "1" - :birthDate #fhir/date "2022-12-13"}]]] + :birthDate #fhir/date #system/date "2022-12-13"}]]] (given-type-query node "Patient" [["birthdate" "le2022-12-14"]] count := 2 @@ -6189,10 +6206,10 @@ (with-system-data [{:blaze.db/keys [node]} config] [[[:put {:fhir/type :fhir/Patient :id "0" :gender #fhir/code "male" - :birthDate #fhir/date "2022-12-14"}] + :birthDate #fhir/date #system/date "2022-12-14"}] [:put {:fhir/type :fhir/Patient :id "1" :gender #fhir/code "male" - :birthDate #fhir/date "2022-12-13"}]]] + :birthDate #fhir/date #system/date "2022-12-13"}]]] (given-type-query node "Patient" [["gender" "male"] ["birthdate" "le2022-12-14"]] @@ -6204,11 +6221,11 @@ (testing "year precision" (with-system-data [{:blaze.db/keys [node]} config] [[[:put {:fhir/type :fhir/Patient :id "0" - :birthDate #fhir/date "1990"}] + :birthDate #fhir/date #system/date "1990"}] [:put {:fhir/type :fhir/Patient :id "1" - :birthDate #fhir/date "1991"}] + :birthDate #fhir/date #system/date "1991"}] [:put {:fhir/type :fhir/Patient :id "2" - :birthDate #fhir/date "1992"}]]] + :birthDate #fhir/date #system/date "1992"}]]] (given-type-query node "Patient" [["birthdate" "sa1990"]] count := 2 @@ -6223,9 +6240,9 @@ (testing "day precision" (with-system-data [{:blaze.db/keys [node]} config] [[[:put {:fhir/type :fhir/Patient :id "0" - :birthDate #fhir/date "2022-12-14"}] + :birthDate #fhir/date #system/date "2022-12-14"}] [:put {:fhir/type :fhir/Patient :id "1" - :birthDate #fhir/date "2022-12-15"}]]] + :birthDate #fhir/date #system/date "2022-12-15"}]]] (given-type-query node "Patient" [["birthdate" "sa2022-12-14"]] count := 1 @@ -6235,10 +6252,10 @@ (with-system-data [{:blaze.db/keys [node]} config] [[[:put {:fhir/type :fhir/Patient :id "0" :gender #fhir/code "male" - :birthDate #fhir/date "2022-12-14"}] + :birthDate #fhir/date #system/date "2022-12-14"}] [:put {:fhir/type :fhir/Patient :id "1" :gender #fhir/code "male" - :birthDate #fhir/date "2022-12-15"}]]] + :birthDate #fhir/date #system/date "2022-12-15"}]]] (given-type-query node "Patient" [["gender" "male"] ["birthdate" "sa2022-12-14"]] @@ -6249,13 +6266,13 @@ (testing "year precision" (with-system-data [{:blaze.db/keys [node]} config] [[[:put {:fhir/type :fhir/Patient :id "0" - :birthDate #fhir/date "1970"}]] + :birthDate #fhir/date #system/date "1970"}]] [[:put {:fhir/type :fhir/Patient :id "0" - :birthDate #fhir/date "1990"}] + :birthDate #fhir/date #system/date "1990"}] [:put {:fhir/type :fhir/Patient :id "1" - :birthDate #fhir/date "1989"}] + :birthDate #fhir/date #system/date "1989"}] [:put {:fhir/type :fhir/Patient :id "2" - :birthDate #fhir/date "1988"}]]] + :birthDate #fhir/date #system/date "1988"}]]] (given-type-query node "Patient" [["birthdate" "eb1990"]] count := 2 @@ -6270,9 +6287,9 @@ (testing "day precision" (with-system-data [{:blaze.db/keys [node]} config] [[[:put {:fhir/type :fhir/Patient :id "0" - :birthDate #fhir/date "2022-12-14"}] + :birthDate #fhir/date #system/date "2022-12-14"}] [:put {:fhir/type :fhir/Patient :id "1" - :birthDate #fhir/date "2022-12-13"}]]] + :birthDate #fhir/date #system/date "2022-12-13"}]]] (given-type-query node "Patient" [["birthdate" "eb2022-12-14"]] count := 1 @@ -6282,10 +6299,10 @@ (with-system-data [{:blaze.db/keys [node]} config] [[[:put {:fhir/type :fhir/Patient :id "0" :gender #fhir/code "male" - :birthDate #fhir/date "2022-12-14"}] + :birthDate #fhir/date #system/date "2022-12-14"}] [:put {:fhir/type :fhir/Patient :id "1" :gender #fhir/code "male" - :birthDate #fhir/date "2022-12-13"}]]] + :birthDate #fhir/date #system/date "2022-12-13"}]]] (given-type-query node "Patient" [["gender" "male"] ["birthdate" "eb2022-12-14"]] @@ -6295,17 +6312,17 @@ (deftest type-query-date-encounter-test (with-system-data [{:blaze.db/keys [node]} config] [[[:put {:fhir/type :fhir/Encounter :id "E1" - :period #fhir/Period{:start #fhir/dateTime "1999-08" - :end #fhir/dateTime "2000-04"}}]] + :period #fhir/Period{:start #fhir/dateTime #system/date-time "1999-08" + :end #fhir/dateTime #system/date-time "2000-04"}}]] [[:put {:fhir/type :fhir/Encounter :id "E2" - :period #fhir/Period{:start #fhir/dateTime "2000-03" - :end #fhir/dateTime "2000-10"}}]] + :period #fhir/Period{:start #fhir/dateTime #system/date-time "2000-03" + :end #fhir/dateTime #system/date-time "2000-10"}}]] [[:put {:fhir/type :fhir/Encounter :id "E3" - :period #fhir/Period{:start #fhir/dateTime "1999-11" - :end #fhir/dateTime "2001-04"}}]] + :period #fhir/Period{:start #fhir/dateTime #system/date-time "1999-11" + :end #fhir/dateTime #system/date-time "2001-04"}}]] [[:put {:fhir/type :fhir/Encounter :id "E4" - :period #fhir/Period{:start #fhir/dateTime "2000-09" - :end #fhir/dateTime "2001-07"}}]]] + :period #fhir/Period{:start #fhir/dateTime #system/date-time "2000-09" + :end #fhir/dateTime #system/date-time "2001-07"}}]]] (let [db (d/db node) num-encounter #(count (d/type-query db "Encounter" %))] @@ -6333,7 +6350,7 @@ (def encounter-gen (let [date-time (fg/dateTime :extension (gen/return nil) - :value (gen/fmap (partial apply format "%04d-%02d-%02d") + :value (gen/fmap (comp system/parse-date-time (partial apply format "%04d-%02d-%02d")) (gen/tuple (gen/choose 1999 2001) fg/month fg/day)))] (fg/encounter :id (gen/fmap str gen/uuid) @@ -6387,13 +6404,13 @@ (system/date-time-upper-bound date-time)]) (defn- fhir-date-time-range [x] - (condp = (type/type x) + (case (:fhir/type x) :fhir/Period - [(system/date-time-lower-bound (type/value (:start x))) - (system/date-time-upper-bound (type/value (:end x)))] - :fhir/dateTime - [(system/date-time-lower-bound (type/value x)) - (system/date-time-upper-bound (type/value x))])) + [(system/date-time-lower-bound (:value (:start x))) + (system/date-time-upper-bound (:value (:end x)))] + (:fhir/dateTime :fhir/instant) + [(system/date-time-lower-bound (:value x)) + (system/date-time-upper-bound (:value x))])) (defn- fully-contains? [[x1 x2] [y1 y2]] (<= x1 y1 y2 x2)) @@ -6489,7 +6506,7 @@ (defn- every-found-observation-matches? [pred node prefix date-time] (let [pull (partial pull-type-query node "Observation") - pred (comp (pred (system/parse-date-time date-time)) :effective) + pred (comp (pred date-time) :effective) observations (pull [["date" (str prefix date-time)]])] (and (every? pred observations) (or (< (count observations) 2) @@ -6505,12 +6522,28 @@ :id (gen/fmap str gen/uuid) :meta (gen/return nil) :identifier (gen/return nil) + :basedOn (gen/return nil) + :partOf (gen/return nil) :status (gen/return #fhir/code "final") :category (gen/return nil) :code (gen/return nil) :subject (gen/return nil) + :focus (gen/return nil) :encounter (gen/return nil) - :value (gen/return nil))) + :issued (gen/return nil) + :performer (gen/return nil) + :value (gen/return nil) + :dataAbsentReason (gen/return nil) + :interpretation (gen/return nil) + :note (gen/return nil) + :bodySite (gen/return nil) + :method (gen/return nil) + :specimen (gen/return nil) + :device (gen/return nil) + :referenceRange (gen/return nil) + :hasMember (gen/return nil) + :derivedFrom (gen/return nil) + :component (gen/return nil))) (deftest ^:slow type-query-date-equal-generative-test (log/set-min-level! :warn) @@ -6606,7 +6639,7 @@ (testing "Encounter" (with-system-data [{:blaze.db/keys [node]} config] [[[:put {:fhir/type :fhir/Encounter :id "0" - :period #fhir/Period{:start #fhir/dateTime "2016"} + :period #fhir/Period{:start #fhir/dateTime #system/date-time "2016"} :diagnosis [{:fhir/type :fhir.Encounter/diagnosis :condition @@ -6616,14 +6649,14 @@ #fhir/Reference{:reference #fhir/string "Condition/2"}}]}] [:put {:fhir/type :fhir/Encounter :id "1" :status #fhir/code "finished" - :period #fhir/Period{:start #fhir/dateTime "2016"} + :period #fhir/Period{:start #fhir/dateTime #system/date-time "2016"} :diagnosis [{:fhir/type :fhir.Encounter/diagnosis :condition #fhir/Reference{:reference #fhir/string "Condition/1"}}]}] [:put {:fhir/type :fhir/Encounter :id "2" :status #fhir/code "finished" - :period #fhir/Period{:start #fhir/dateTime "2016"} + :period #fhir/Period{:start #fhir/dateTime #system/date-time "2016"} :diagnosis [{:fhir/type :fhir.Encounter/diagnosis :condition @@ -6984,8 +7017,8 @@ :identifier [(type/identifier {:value (type/string (str i))})]}) (deftest type-query-identifier-non-matching-test - (st/unstrument) (log/set-min-level! :info) + (st/unstrument) (testing "doesn't return non-matching resources" (let [test-size 200000] (with-system-data [{:blaze.db/keys [node]} config] @@ -7195,41 +7228,41 @@ (testing "with unit" (given-type-query node "Location" [in-leipzig] count := 1 - [0 :name] := "Leipzig")) + [0 :name] := #fhir/string "Leipzig")) (testing "without unit" (given-type-query node "Location" [london-900km] count := 2 - [0 :name] := "London" - [1 :name] := "Leipzig")) + [0 :name] := #fhir/string "London" + [1 :name] := #fhir/string "Leipzig")) (testing "with more than one value" (given-type-query node "Location" [london-florence-900km] count := 2 - [0 :name] := "London" - [1 :name] := "Leipzig")) + [0 :name] := #fhir/string "London" + [1 :name] := #fhir/string "Leipzig")) (testing "it is possible to start with the second location" (given (pull-type-query node "Location" [london-900km] "1") count := 1 - [0 :name] := "Leipzig") + [0 :name] := #fhir/string "Leipzig") (testing "with more than one value" (given (pull-type-query node "Location" [london-florence-900km] "1") count := 1 - [0 :name] := "Leipzig")))) + [0 :name] := #fhir/string "Leipzig")))) (testing "status and near" (let [clauses [["status" "active"] langsa-1550km]] (given (pull-type-query node "Location" clauses) count := 1 - [0 :name] := "Jakarta")) + [0 :name] := #fhir/string "Jakarta")) (testing "with more than one value" (let [clauses [["status" "active"] london-florence-900km]] (given (pull-type-query node "Location" clauses) count := 1 - [0 :name] := "Leipzig"))) + [0 :name] := #fhir/string "Leipzig"))) (testing "with many values enforcing a seek for `near`" (with-system-data [{:blaze.db/keys [node]} config] @@ -7253,7 +7286,7 @@ (let [clauses [["status" "suspended"] london-900km]] (given (pull-type-query node "Location" clauses) count := 1 - [0 :name] := "London") + [0 :name] := #fhir/string "London") (given (explain-type-query node "Location" clauses) :scan-type := :ordered @@ -7578,9 +7611,9 @@ (testing "date search param" (with-system-data [{:blaze.db/keys [node]} config] [[[:put {:fhir/type :fhir/Patient :id "0" - :birthDate #fhir/date "2025"}] + :birthDate #fhir/date #system/date "2025"}] [:put {:fhir/type :fhir/Patient :id "1" - :birthDate #fhir/date "2023"}]]] + :birthDate #fhir/date #system/date "2023"}]]] (with-open-db [db node] (doseq [target [node db] @@ -8188,10 +8221,10 @@ (with-system-data [{:blaze.db/keys [node]} config] [[[:put {:fhir/type :fhir/Patient :id "0"}] [:put {:fhir/type :fhir/Condition :id "1" - :onset #fhir/dateTime "2025-07-25" + :onset #fhir/dateTime #system/date-time "2025-07-25" :subject #fhir/Reference{:reference #fhir/string "Patient/0"}}] [:put {:fhir/type :fhir/Condition :id "2" - :onset #fhir/dateTime "2025-07-26" + :onset #fhir/dateTime #system/date-time "2025-07-26" :subject #fhir/Reference{:reference #fhir/string "Patient/0"}}]]] (testing "only returns the condition with onset <= 2025-07-25" @@ -8368,7 +8401,7 @@ :fhir/type := :fhir/Patient :id := "0" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH)))) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")))) (testing "summary" (testing "CodeSystem" @@ -8389,7 +8422,7 @@ :fhir/type := :fhir/CodeSystem :id := "0" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z" [:meta :tag 0 :system] := #fhir/uri "http://terminology.hl7.org/CodeSystem/v3-ObservationValue" [:meta :tag 0 :code] := #fhir/code "SUBSETTED" :url := #fhir/uri "system-115910" @@ -8415,7 +8448,7 @@ :fhir/type := :fhir/ValueSet :id := "0" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z" [:meta :tag 0 :system] := #fhir/uri "http://terminology.hl7.org/CodeSystem/v3-ObservationValue" [:meta :tag 0 :code] := #fhir/code "SUBSETTED" :url := #fhir/uri "value-set-154043" @@ -8513,7 +8546,7 @@ [0 :fhir/type] := :fhir/CodeSystem [0 :id] := "0" [0 :meta :versionId] := #fhir/id "1" - [0 :meta :lastUpdated] := Instant/EPOCH + [0 :meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z" [0 :meta :tag 0 :system] := #fhir/uri "http://terminology.hl7.org/CodeSystem/v3-ObservationValue" [0 :meta :tag 0 :code] := #fhir/code "SUBSETTED" [0 :url] := #fhir/uri "system-115910" @@ -8523,7 +8556,7 @@ [1 :fhir/type] := :fhir/ValueSet [1 :id] := "0" [1 :meta :versionId] := #fhir/id "1" - [1 :meta :lastUpdated] := Instant/EPOCH + [1 :meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z" [1 :meta :tag 0 :system] := #fhir/uri "http://terminology.hl7.org/CodeSystem/v3-ObservationValue" [1 :meta :tag 0 :code] := #fhir/code "SUBSETTED" [1 :url] := #fhir/uri "value-set-154043" @@ -9386,7 +9419,7 @@ :subject #fhir/Reference{:reference #fhir/string "Patient/0"}}] [:put {:fhir/type :fhir/Observation :id "1" :subject #fhir/Reference{:reference #fhir/string "Patient/0"} - :effective #fhir/dateTime "2024-01-04T23:45:50Z"}]]] + :effective #fhir/dateTime #system/date-time "2024-01-04T23:45:50Z"}]]] (let [db (d/db node) patient (d/resource-handle db "Patient" "0")] @@ -9405,7 +9438,7 @@ :subject #fhir/Reference{:reference #fhir/string "Patient/0"}}] [:put {:fhir/type :fhir/Encounter :id "1" :subject #fhir/Reference{:reference #fhir/string "Patient/0"} - :period #fhir/Period{:start #fhir/dateTime "2024-01-04T23:45:50Z"}}]]] + :period #fhir/Period{:start #fhir/dateTime #system/date-time "2024-01-04T23:45:50Z"}}]]] (let [db (d/db node) patient (d/resource-handle db "Patient" "0")] @@ -9424,7 +9457,7 @@ :subject #fhir/Reference{:reference #fhir/string "Patient/0"}}] [:put {:fhir/type :fhir/Observation :id "1" :subject #fhir/Reference{:reference #fhir/string "Patient/0"} - :effective #fhir/dateTime "2024-01-04T23:45:50Z"}]]] + :effective #fhir/dateTime #system/date-time "2024-01-04T23:45:50Z"}]]] (let [db (d/db node) patient (d/resource-handle db "Patient" "0")] @@ -9443,10 +9476,10 @@ :subject #fhir/Reference{:reference #fhir/string "Patient/0"}}] [:put {:fhir/type :fhir/Observation :id "1" :subject #fhir/Reference{:reference #fhir/string "Patient/0"} - :effective #fhir/dateTime "2024-01-04T23:45:50Z"}] + :effective #fhir/dateTime #system/date-time "2024-01-04T23:45:50Z"}] [:put {:fhir/type :fhir/Observation :id "2" :subject #fhir/Reference{:reference #fhir/string "Patient/0"} - :effective #fhir/dateTime "2025-01-04T23:45:50Z"}]]] + :effective #fhir/dateTime #system/date-time "2025-01-04T23:45:50Z"}]]] (let [db (d/db node) patient (d/resource-handle db "Patient" "0")] @@ -9582,7 +9615,7 @@ [[[:create {:fhir/type :fhir/Patient :id "0"}]]] (with-open [batch-db (d/new-batch-db (d/db node))] - (given @(d/pull batch-db (d/resource-handle batch-db "Patient" "0")) + (given @(pull-resource batch-db "Patient" "0") :fhir/type := :fhir/Patient :id := "0" [:meta :versionId] := #fhir/id "1")))) diff --git a/modules/db/test/blaze/db/impl/codec/date_test.clj b/modules/db/test/blaze/db/impl/codec/date_test.clj index e0b6572c5..d54a04f2f 100644 --- a/modules/db/test/blaze/db/impl/codec/date_test.clj +++ b/modules/db/test/blaze/db/impl/codec/date_test.clj @@ -4,8 +4,8 @@ [blaze.db.impl.codec-spec] [blaze.db.impl.codec.date :as codec-date] [blaze.db.impl.index.search-param-value-resource-spec] + [blaze.fhir.spec.generators :as fg] [blaze.test-util :as tu :refer [satisfies-prop]] - [clojure.spec.alpha :as s] [clojure.spec.test.alpha :as st] [clojure.test :as test :refer [are deftest is testing]] [clojure.test.check.properties :as prop]) @@ -84,12 +84,12 @@ (deftest encode-range-test (testing "extract lower bound" (satisfies-prop 100 - (prop/for-all [date (s/gen :system/date)] + (prop/for-all [date fg/date-value] (= (codec-date/lower-bound-bytes (codec-date/encode-range date)) (codec-date/encode-lower-bound date))))) (testing "extract upper bound" (satisfies-prop 100 - (prop/for-all [date (s/gen :system/date)] + (prop/for-all [date fg/date-value] (= (codec-date/upper-bound-bytes (codec-date/encode-range date)) (codec-date/encode-upper-bound date)))))) diff --git a/modules/db/test/blaze/db/impl/search_param/chained_test.clj b/modules/db/test/blaze/db/impl/search_param/chained_test.clj index 7706ff923..c3a8d7fc6 100644 --- a/modules/db/test/blaze/db/impl/search_param/chained_test.clj +++ b/modules/db/test/blaze/db/impl/search_param/chained_test.clj @@ -10,7 +10,6 @@ [blaze.db.search-param-registry :as sr] [blaze.db.search-param-registry-spec] [blaze.fhir.hash-spec] - [blaze.fhir.spec.type] [blaze.fhir.test-util :refer [structure-definition-repo]] [blaze.module.test-util :refer [with-system]] [blaze.test-util :as tu] diff --git a/modules/db/test/blaze/db/impl/search_param/composite/token_quantity_test.clj b/modules/db/test/blaze/db/impl/search_param/composite/token_quantity_test.clj index 40ba4c85f..9fc26271c 100644 --- a/modules/db/test/blaze/db/impl/search_param/composite/token_quantity_test.clj +++ b/modules/db/test/blaze/db/impl/search_param/composite/token_quantity_test.clj @@ -10,7 +10,6 @@ [blaze.db.search-param-registry :as sr] [blaze.db.search-param-registry-spec] [blaze.fhir.hash-spec] - [blaze.fhir.spec.type] [blaze.fhir.test-util :refer [structure-definition-repo]] [blaze.module.test-util :refer [with-system]] [blaze.test-util :as tu] diff --git a/modules/db/test/blaze/db/impl/search_param/composite/token_token_test.clj b/modules/db/test/blaze/db/impl/search_param/composite/token_token_test.clj index d5cc33d3e..b8d22aa7b 100644 --- a/modules/db/test/blaze/db/impl/search_param/composite/token_token_test.clj +++ b/modules/db/test/blaze/db/impl/search_param/composite/token_token_test.clj @@ -10,7 +10,6 @@ [blaze.db.search-param-registry :as sr] [blaze.db.search-param-registry-spec] [blaze.fhir.hash-spec] - [blaze.fhir.spec.type] [blaze.fhir.test-util :refer [structure-definition-repo]] [blaze.module.test-util :refer [with-system]] [blaze.test-util :as tu] diff --git a/modules/db/test/blaze/db/impl/search_param/composite_test.clj b/modules/db/test/blaze/db/impl/search_param/composite_test.clj index c5f3147fd..9a391eb44 100644 --- a/modules/db/test/blaze/db/impl/search_param/composite_test.clj +++ b/modules/db/test/blaze/db/impl/search_param/composite_test.clj @@ -15,7 +15,6 @@ [blaze.fhir-path :as fhir-path] [blaze.fhir.hash :as hash] [blaze.fhir.hash-spec] - [blaze.fhir.spec.type] [blaze.fhir.test-util :refer [structure-definition-repo]] [blaze.module.test-util :refer [with-system]] [blaze.test-util :as tu] diff --git a/modules/db/test/blaze/db/impl/search_param/date_test.clj b/modules/db/test/blaze/db/impl/search_param/date_test.clj index 67d2f2bbd..b48b3d61b 100644 --- a/modules/db/test/blaze/db/impl/search_param/date_test.clj +++ b/modules/db/test/blaze/db/impl/search_param/date_test.clj @@ -116,7 +116,7 @@ (testing "birthDate" (let [patient {:fhir/type :fhir/Patient :id "id-142629" - :birthDate #fhir/date "2020-02-04"} + :birthDate #fhir/date #system/date "2020-02-04"} hash (hash/generate patient) [[_ k0]] (index-entries @@ -135,7 +135,7 @@ (let [patient {:fhir/type :fhir/Patient :id "id-142629" - :deceased #fhir/dateTime "2019-11-17T00:14:29+01:00"} + :deceased #fhir/dateTime #system/date-time "2019-11-17T00:14:29+01:00"} hash (hash/generate patient) [[_ k0]] (index-entries @@ -157,8 +157,8 @@ {:fhir/type :fhir/Encounter :id "id-160224" :period #fhir/Period - {:start #fhir/dateTime "2019-11-17T00:14:29+01:00" - :end #fhir/dateTime "2019-11-17T00:44:29+01:00"}} + {:start #fhir/dateTime #system/date-time "2019-11-17T00:14:29+01:00" + :end #fhir/dateTime #system/date-time "2019-11-17T00:44:29+01:00"}} hash (hash/generate encounter) [[_ k0]] (index-entries @@ -180,7 +180,7 @@ {:fhir/type :fhir/Encounter :id "id-160224" :period #fhir/Period - {:end #fhir/dateTime "2019-11-17"}} + {:end #fhir/dateTime #system/date-time "2019-11-17"}} hash (hash/generate encounter) [[_ k0]] (index-entries @@ -201,7 +201,7 @@ {:fhir/type :fhir/Encounter :id "id-160224" :period #fhir/Period - {:start #fhir/dateTime "2019-11-17T00:14:29+01:00"}} + {:start #fhir/dateTime #system/date-time "2019-11-17T00:14:29+01:00"}} hash (hash/generate encounter) [[_ k0]] (index-entries @@ -240,7 +240,7 @@ (testing "issued" (let [patient {:fhir/type :fhir/DiagnosticReport :id "id-155607" - :issued #fhir/instant "2019-11-17T00:14:29.917+01:00"} + :issued #fhir/instant #system/date-time "2019-11-17T00:14:29.917+01:00"} hash (hash/generate patient) [[_ k0]] (index-entries diff --git a/modules/db/test/blaze/db/impl/search_param/has_test.clj b/modules/db/test/blaze/db/impl/search_param/has_test.clj index cf44760a7..8ddb474e1 100644 --- a/modules/db/test/blaze/db/impl/search_param/has_test.clj +++ b/modules/db/test/blaze/db/impl/search_param/has_test.clj @@ -10,7 +10,6 @@ [blaze.db.search-param-registry :as sr] [blaze.db.search-param-registry-spec] [blaze.fhir.hash-spec] - [blaze.fhir.spec.type] [blaze.fhir.test-util :refer [structure-definition-repo]] [blaze.module.test-util :refer [with-system]] [blaze.test-util :as tu] diff --git a/modules/db/test/blaze/db/impl/search_param/number_test.clj b/modules/db/test/blaze/db/impl/search_param/number_test.clj index e084f8398..71fc3acd9 100644 --- a/modules/db/test/blaze/db/impl/search_param/number_test.clj +++ b/modules/db/test/blaze/db/impl/search_param/number_test.clj @@ -16,7 +16,6 @@ [blaze.fhir-path :as fhir-path] [blaze.fhir.hash :as hash] [blaze.fhir.hash-spec] - [blaze.fhir.spec.type] [blaze.fhir.test-util :refer [structure-definition-repo]] [blaze.module.test-util :refer [with-system]] [blaze.test-util :as tu] diff --git a/modules/db/test/blaze/db/impl/search_param/quantity_test.clj b/modules/db/test/blaze/db/impl/search_param/quantity_test.clj index ff9a52f19..6f12d51d2 100644 --- a/modules/db/test/blaze/db/impl/search_param/quantity_test.clj +++ b/modules/db/test/blaze/db/impl/search_param/quantity_test.clj @@ -16,7 +16,6 @@ [blaze.fhir-path :as fhir-path] [blaze.fhir.hash :as hash] [blaze.fhir.hash-spec] - [blaze.fhir.spec.type] [blaze.fhir.test-util :refer [structure-definition-repo]] [blaze.module.test-util :refer [with-system]] [blaze.test-util :as tu] diff --git a/modules/db/test/blaze/db/impl/search_param/token_test.clj b/modules/db/test/blaze/db/impl/search_param/token_test.clj index 9e5d8a827..038b755a2 100644 --- a/modules/db/test/blaze/db/impl/search_param/token_test.clj +++ b/modules/db/test/blaze/db/impl/search_param/token_test.clj @@ -519,7 +519,7 @@ (let [patient {:fhir/type :fhir/Patient :id "id-142629" - :deceased #fhir/dateTime "2019-11-17T00:14:29+01:00"} + :deceased #fhir/dateTime #system/date-time "2019-11-17T00:14:29+01:00"} hash (hash/generate patient) [[_ k0] [_ k1]] (index-entries diff --git a/modules/db/test/blaze/db/impl/search_param/util_test.clj b/modules/db/test/blaze/db/impl/search_param/util_test.clj index 50122a377..b8efe604b 100644 --- a/modules/db/test/blaze/db/impl/search_param/util_test.clj +++ b/modules/db/test/blaze/db/impl/search_param/util_test.clj @@ -34,7 +34,7 @@ (deftest format-skip-indexing-msg-test (is (= (u/format-skip-indexing-msg #fhir/string "value-132537" "url-132522" "type-132528") - "Skip indexing value `value-132537` of type `:fhir/string` for search parameter `url-132522` with type `type-132528` because the rule is missing."))) + "Skip indexing value `String{id=null, extension=[], value='value-132537'}` of type `:fhir/string` for search parameter `url-132522` with type `type-132528` because the rule is missing."))) (deftest soundex-test (testing "question mark from issue #903" diff --git a/modules/db/test/blaze/db/impl/search_param_test.clj b/modules/db/test/blaze/db/impl/search_param_test.clj index 87f96d8e2..d1b3f72b4 100644 --- a/modules/db/test/blaze/db/impl/search_param_test.clj +++ b/modules/db/test/blaze/db/impl/search_param_test.clj @@ -12,7 +12,6 @@ [blaze.db.search-param-registry :as sr] [blaze.fhir.hash :as hash] [blaze.fhir.hash-spec] - [blaze.fhir.spec.type] [blaze.fhir.test-util :refer [structure-definition-repo]] [blaze.module.test-util :refer [with-system]] [blaze.test-util :as tu] diff --git a/modules/db/test/blaze/db/node/resource_indexer_test.clj b/modules/db/test/blaze/db/node/resource_indexer_test.clj index fd8069fd4..4e9817832 100644 --- a/modules/db/test/blaze/db/node/resource_indexer_test.clj +++ b/modules/db/test/blaze/db/node/resource_indexer_test.clj @@ -24,7 +24,6 @@ [blaze.fhir.hash :as hash] [blaze.fhir.hash-spec] [blaze.fhir.parsing-context] - [blaze.fhir.spec.type] [blaze.fhir.test-util :refer [structure-definition-repo]] [blaze.fhir.writing-context] [blaze.metrics.spec] @@ -268,7 +267,7 @@ [#fhir/Coding {:system #fhir/uri "system-204435" :code #fhir/code "code-204441"}]} - :onset #fhir/dateTime "2020-01-30" + :onset #fhir/dateTime #system/date-time "2020-01-30" :subject #fhir/Reference{:reference #fhir/string "Patient/id-145552"} :meta #fhir/Meta @@ -363,7 +362,7 @@ {:system #fhir/uri "system-193821" :code #fhir/code "code-193824"}]} :subject #fhir/Reference{:reference #fhir/string "Patient/id-180857"} - :effective #fhir/dateTime "2005-06-17" + :effective #fhir/dateTime #system/date-time "2005-06-17" :value #fhir/Quantity {:code #fhir/code "kg/m2" diff --git a/modules/db/test/blaze/db/node/transaction_test.clj b/modules/db/test/blaze/db/node/transaction_test.clj index 6294e8777..9d7ce5e8e 100644 --- a/modules/db/test/blaze/db/node/transaction_test.clj +++ b/modules/db/test/blaze/db/node/transaction_test.clj @@ -4,7 +4,6 @@ [blaze.db.impl.index.tx-success :as tx-success] [blaze.db.node.transaction :as tx] [blaze.db.node.transaction-spec] - [blaze.fhir.spec.type] [blaze.fhir.test-util] [blaze.test-util :as tu :refer [satisfies-prop]] [clojure.spec.alpha :as s] diff --git a/modules/db/test/blaze/db/node/tx_indexer/expand_test.clj b/modules/db/test/blaze/db/node/tx_indexer/expand_test.clj index 4bb54064c..5b3570eff 100644 --- a/modules/db/test/blaze/db/node/tx_indexer/expand_test.clj +++ b/modules/db/test/blaze/db/node/tx_indexer/expand_test.clj @@ -13,7 +13,6 @@ [blaze.fhir.hash :as hash] [blaze.fhir.hash-spec] [blaze.fhir.spec.spec] - [blaze.fhir.spec.type] [blaze.module.test-util :refer [with-system]] [blaze.test-util :as tu] [clojure.spec.test.alpha :as st] @@ -38,9 +37,9 @@ (testing "conflict" (with-system-data [{:blaze.db/keys [node]} config] [[[:put {:fhir/type :fhir/Patient :id "0" - :birthDate #fhir/date "2020"}] + :birthDate #fhir/date #system/date "2020"}] [:put {:fhir/type :fhir/Patient :id "1" - :birthDate #fhir/date "2020"}]]] + :birthDate #fhir/date #system/date "2020"}]]] (given (expand-tx-cmds node diff --git a/modules/db/test/blaze/db/node/tx_indexer/verify_test.clj b/modules/db/test/blaze/db/node/tx_indexer/verify_test.clj index 03d70bd4e..2c51a34b9 100644 --- a/modules/db/test/blaze/db/node/tx_indexer/verify_test.clj +++ b/modules/db/test/blaze/db/node/tx_indexer/verify_test.clj @@ -21,7 +21,6 @@ [blaze.db.tx-log.spec] [blaze.fhir.hash :as hash] [blaze.fhir.hash-spec] - [blaze.fhir.spec.type] [blaze.module.test-util :refer [with-system]] [blaze.test-util :as tu :refer [satisfies-prop]] [clojure.spec.alpha :as s] diff --git a/modules/db/test/blaze/db/resource_cache_test.clj b/modules/db/test/blaze/db/resource_cache_test.clj index 97113d076..4201b0f02 100644 --- a/modules/db/test/blaze/db/resource_cache_test.clj +++ b/modules/db/test/blaze/db/resource_cache_test.clj @@ -47,10 +47,9 @@ (def patient-2-hash (hash/generate patient-2)) (def code-system-0-hash (hash/generate code-system-0)) -(def config +(def ^:private config {:blaze.db/resource-cache - {:resource-store (ig/ref ::rs/kv) - :max-size 100} + {:resource-store (ig/ref ::rs/kv)} ::rs/kv {:kv-store (ig/ref ::kv/mem) :parsing-context (ig/ref :blaze.fhir.parsing-context/resource-store) @@ -66,6 +65,14 @@ :blaze.fhir/writing-context {:structure-definition-repo structure-definition-repo}}) +(def ^:private zero-config + "Creates a special version of a no-op cache." + (assoc-in config [:blaze.db/resource-cache :max-size-ratio] 0)) + +(def ^:private one-config + "Constraints the max-size-ratio to 0.8." + (assoc-in config [:blaze.db/resource-cache :max-size-ratio] 1)) + (deftest init-test (testing "nil config" (given-failed-system {:blaze.db/resource-cache nil} @@ -86,53 +93,57 @@ [:cause-data ::s/problems 0 :via] := [:blaze.db/resource-store] [:cause-data ::s/problems 0 :val] := ::invalid)) - (testing "invalid max-size" - (given-failed-system (assoc-in config [:blaze.db/resource-cache :max-size] ::invalid) + (testing "invalid max-size-ratio" + (given-failed-system (assoc-in config [:blaze.db/resource-cache :max-size-ratio] ::invalid) :key := :blaze.db/resource-cache :reason := ::ig/build-failed-spec - [:cause-data ::s/problems 0 :via] := [::rc/max-size] + [:cause-data ::s/problems 0 :via] := [::rc/max-size-ratio] [:cause-data ::s/problems 0 :val] := ::invalid))) (deftest get-test (testing "success" - (with-system [{cache :blaze.db/resource-cache store ::rs/kv} config] - @(rs/put! store {patient-0-hash patient-0 - patient-1-hash patient-1 - code-system-0-hash code-system-0}) - - (are [key resource] (= resource @(rc/get cache key)) - [:fhir/Patient patient-0-hash :complete] patient-0 - [:fhir/Patient patient-1-hash :complete] patient-1 - [:fhir/CodeSystem code-system-0-hash :complete] code-system-0 - [:fhir/CodeSystem code-system-0-hash :summary] {:fhir/type :fhir/CodeSystem :id "0" - :meta (type/meta {:tag [fu/subsetted]})} - [:fhir/CodeSystem code-system-0-hash :complete] code-system-0))) + (doseq [config [config zero-config one-config]] + (with-system [{cache :blaze.db/resource-cache store ::rs/kv} config] + @(rs/put! store {patient-0-hash patient-0 + patient-1-hash patient-1 + code-system-0-hash code-system-0}) + + (are [key resource] (= resource @(rc/get cache key)) + [:fhir/Patient patient-0-hash :complete] patient-0 + [:fhir/Patient patient-1-hash :complete] patient-1 + [:fhir/CodeSystem code-system-0-hash :complete] code-system-0 + [:fhir/CodeSystem code-system-0-hash :summary] {:fhir/type :fhir/CodeSystem :id "0" + :meta (type/meta {:tag [fu/subsetted]})} + [:fhir/CodeSystem code-system-0-hash :complete] code-system-0)))) (testing "not-found" - (with-system [{cache :blaze.db/resource-cache} config] + (doseq [config [config zero-config one-config]] + (with-system [{cache :blaze.db/resource-cache} config] - (is (nil? @(rc/get cache [:fhir/Patient patient-0-hash :complete])))))) + (is (nil? @(rc/get cache [:fhir/Patient patient-0-hash :complete]))))))) (deftest multi-get-test (testing "found both" - (with-system [{cache :blaze.db/resource-cache store ::rs/kv} config] - @(rs/put! store {patient-0-hash patient-0 - patient-1-hash patient-1}) + (doseq [config [config zero-config one-config]] + (with-system [{cache :blaze.db/resource-cache store ::rs/kv} config] + @(rs/put! store {patient-0-hash patient-0 + patient-1-hash patient-1}) - (is (= {[:fhir/Patient patient-0-hash :complete] patient-0 - [:fhir/Patient patient-1-hash :complete] patient-1} - @(st/with-instrument-disabled - (rc/multi-get cache [[:fhir/Patient patient-0-hash :complete] - [:fhir/Patient patient-1-hash :complete]])))))) + (is (= {[:fhir/Patient patient-0-hash :complete] patient-0 + [:fhir/Patient patient-1-hash :complete] patient-1} + @(st/with-instrument-disabled + (rc/multi-get cache [[:fhir/Patient patient-0-hash :complete] + [:fhir/Patient patient-1-hash :complete]]))))))) (testing "found one" - (with-system [{cache :blaze.db/resource-cache store ::rs/kv} config] - @(rs/put! store {patient-0-hash patient-0}) + (doseq [config [config zero-config one-config]] + (with-system [{cache :blaze.db/resource-cache store ::rs/kv} config] + @(rs/put! store {patient-0-hash patient-0}) - (is (= {[:fhir/Patient patient-0-hash :complete] patient-0} - @(st/with-instrument-disabled - (rc/multi-get cache [[:fhir/Patient patient-0-hash :complete] - [:fhir/Patient patient-1-hash :complete]]))))))) + (is (= {[:fhir/Patient patient-0-hash :complete] patient-0} + @(st/with-instrument-disabled + (rc/multi-get cache [[:fhir/Patient patient-0-hash :complete] + [:fhir/Patient patient-1-hash :complete]])))))))) (defn- generate-patients [n] (into @@ -143,39 +154,45 @@ [(hash/generate patient) patient]))) (range n))) +(defn- contains-key? [cache key] + (when (instance? DefaultResourceCache cache) + (some? (.getIfPresent ^AsyncCache (.cache ^DefaultResourceCache cache) key)))) + (defn- cache-size [cache] (.size (.asMap ^AsyncCache (.cache ^DefaultResourceCache cache)))) (deftest multi-get-skip-cache-insertion-test (testing "just returns two existing patients" - (with-system [{cache :blaze.db/resource-cache store ::rs/kv} config] - @(rs/put! store {patient-0-hash patient-0 - patient-1-hash patient-1}) + (doseq [config [config zero-config one-config]] + (with-system [{cache :blaze.db/resource-cache store ::rs/kv} config] + @(rs/put! store {patient-0-hash patient-0 + patient-1-hash patient-1}) - @(rc/get cache [:fhir/Patient patient-0-hash :complete]) - @(rc/get cache [:fhir/Patient patient-1-hash :complete]) + @(rc/get cache [:fhir/Patient patient-0-hash :complete]) + @(rc/get cache [:fhir/Patient patient-1-hash :complete]) - (is (= {[:fhir/Patient patient-0-hash :complete] patient-0 - [:fhir/Patient patient-1-hash :complete] patient-1} - @(st/with-instrument-disabled - (rc/multi-get-skip-cache-insertion - cache [[:fhir/Patient patient-0-hash :complete] - [:fhir/Patient patient-1-hash :complete]])))))) + (is (= {[:fhir/Patient patient-0-hash :complete] patient-0 + [:fhir/Patient patient-1-hash :complete] patient-1} + @(st/with-instrument-disabled + (rc/multi-get-skip-cache-insertion + cache [[:fhir/Patient patient-0-hash :complete] + [:fhir/Patient patient-1-hash :complete]]))))))) (testing "not inserting both patients" - (with-system [{cache :blaze.db/resource-cache store ::rs/kv} config] - @(rs/put! store {patient-0-hash patient-0 - patient-1-hash patient-1}) + (doseq [config [config zero-config one-config]] + (with-system [{cache :blaze.db/resource-cache store ::rs/kv} config] + @(rs/put! store {patient-0-hash patient-0 + patient-1-hash patient-1}) - (is (= {[:fhir/Patient patient-0-hash :complete] patient-0 - [:fhir/Patient patient-1-hash :complete] patient-1} - @(st/with-instrument-disabled - (rc/multi-get-skip-cache-insertion - cache [[:fhir/Patient patient-0-hash :complete] - [:fhir/Patient patient-1-hash :complete]])))) + (is (= {[:fhir/Patient patient-0-hash :complete] patient-0 + [:fhir/Patient patient-1-hash :complete] patient-1} + @(st/with-instrument-disabled + (rc/multi-get-skip-cache-insertion + cache [[:fhir/Patient patient-0-hash :complete] + [:fhir/Patient patient-1-hash :complete]])))) - (is (not (rc/contains? cache [:fhir/Patient patient-0-hash :complete]))) - (is (not (rc/contains? cache [:fhir/Patient patient-1-hash :complete]))))) + (is (not (contains-key? cache [:fhir/Patient patient-0-hash :complete]))) + (is (not (contains-key? cache [:fhir/Patient patient-1-hash :complete])))))) (testing "not inserting second patient" (with-system [{cache :blaze.db/resource-cache store ::rs/kv} config] @@ -184,7 +201,7 @@ @(rc/get cache [:fhir/Patient patient-0-hash :complete]) - (is (rc/contains? cache [:fhir/Patient patient-0-hash :complete])) + (is (contains-key? cache [:fhir/Patient patient-0-hash :complete])) (is (= {[:fhir/Patient patient-0-hash :complete] patient-0 [:fhir/Patient patient-1-hash :complete] patient-1} @@ -193,7 +210,7 @@ cache [[:fhir/Patient patient-0-hash :complete] [:fhir/Patient patient-1-hash :complete]])))) - (is (not (rc/contains? cache [:fhir/Patient patient-1-hash :complete]))))) + (is (not (contains-key? cache [:fhir/Patient patient-1-hash :complete]))))) (testing "one contained, one non-contained and one not-found patient" (with-system [{cache :blaze.db/resource-cache store ::rs/kv} config] @@ -202,7 +219,7 @@ @(rc/get cache [:fhir/Patient patient-0-hash :complete]) - (is (rc/contains? cache [:fhir/Patient patient-0-hash :complete])) + (is (contains-key? cache [:fhir/Patient patient-0-hash :complete])) (is (= {[:fhir/Patient patient-0-hash :complete] patient-0 [:fhir/Patient patient-1-hash :complete] patient-1} @@ -212,7 +229,7 @@ [:fhir/Patient patient-1-hash :complete] [:fhir/Patient patient-2-hash :complete]])))) - (is (not (rc/contains? cache [:fhir/Patient patient-1-hash :complete]))))) + (is (not (contains-key? cache [:fhir/Patient patient-1-hash :complete]))))) (testing "100 patients" (with-system [{cache :blaze.db/resource-cache store ::rs/kv} config] @@ -253,8 +270,7 @@ (is (= 1 (ccp/-estimated-size cache))))) (testing "with zero max size" - (with-system [{cache :blaze.db/resource-cache store ::rs/kv} - (assoc-in config [:blaze.db/resource-cache :max-size] 0)] + (with-system [{cache :blaze.db/resource-cache store ::rs/kv} zero-config] (is (zero? (.hitCount ^CacheStats (ccp/-stats cache)))) (is (zero? (ccp/-estimated-size cache))) diff --git a/modules/fhir-client/src/blaze/fhir_client/impl.clj b/modules/fhir-client/src/blaze/fhir_client/impl.clj index 1338d48c5..6756e40a1 100644 --- a/modules/fhir-client/src/blaze/fhir_client/impl.clj +++ b/modules/fhir-client/src/blaze/fhir_client/impl.clj @@ -6,7 +6,6 @@ [blaze.async.flow :as flow] [blaze.byte-buffer :as bb] [blaze.fhir.spec :as fhir-spec] - [blaze.fhir.spec.type :as type] [blaze.util :refer [str]] [clojure.java.io :as io] [cognitect.anomalies :as anom] @@ -82,7 +81,7 @@ handle-error)) (defn- etag [resource] - (when-let [version-id (-> resource :meta :versionId type/value)] + (when-let [version-id (-> resource :meta :versionId :value)] (str "W/\"" version-id "\""))) (defn- generate-body [{:keys [writing-context]} resource] @@ -145,7 +144,7 @@ handle-error)) (defn- next-url [page] - (type/value (:url (first (filter (comp #{"next"} type/value :relation) (:link page)))))) + (:value (:url (first (filter (comp #{"next"} :value :relation) (:link page)))))) (deftype PagingSubscription [^Flow$Subscriber subscriber volatile-uri opts] diff --git a/modules/fhir-path/src/blaze/fhir_path.clj b/modules/fhir-path/src/blaze/fhir_path.clj index d177cbf25..46de9ae5a 100644 --- a/modules/fhir-path/src/blaze/fhir_path.clj +++ b/modules/fhir-path/src/blaze/fhir_path.clj @@ -4,7 +4,6 @@ [blaze.anomaly :as ba :refer [throw-anom]] [blaze.coll.core :as coll] [blaze.fhir.spec :as fhir-spec] - [blaze.fhir.spec.type :as type] [blaze.fhir.spec.type.system :as system] [blaze.util :refer [str]] [clojure.string :as str] @@ -73,21 +72,21 @@ (defn- convertible? "See: http://hl7.org/fhirpath/index.html#conversion" [type item] - (if (identical? type (fhir-spec/fhir-type item)) + (if (identical? type (system/type item)) true - (case [(fhir-spec/fhir-type item) type] - ([:fhir/integer :fhir/decimal] - [:fhir/date :fhir/dateTime]) + (case [(system/type item) type] + ([:system/integer :system/decimal] + [:system/date :system/date-time]) true false))) (defn- convert "See: http://hl7.org/fhirpath/index.html#conversion" [type item] - (if (identical? type (fhir-spec/fhir-type item)) + (if (identical? type (system/type item)) item - (case [(fhir-spec/fhir-type item) type] - [:fhir/integer :fhir/decimal] + (case [(system/type item) type] + [:system/integer :system/decimal] (BigDecimal/valueOf (long item))))) (defn- convert-fhir-primitive @@ -95,7 +94,7 @@ See: https://build.fhir.org/fhirpath.html#types" [x] - (cond-> x (fhir-spec/primitive-val? x) (type/value))) + (cond-> x (fhir-spec/primitive-val? x) :value)) ;; See: http://hl7.org/fhirpath/index.html#singleton-evaluation-of-collections (defn- singleton-evaluation-msg [coll] @@ -103,10 +102,10 @@ (defn- singleton [type coll] (case (coll/count coll) - 1 (let [first (coll/nth coll 0)] + 1 (let [value (convert-fhir-primitive (coll/nth coll 0))] (cond - (convertible? type first) (convert type first) - (identical? :fhir/boolean type) true + (convertible? type value) (convert type value) + (identical? :system/boolean type) true :else (throw-anom (ba/incorrect (singleton-evaluation-msg coll))))) 0 coll @@ -125,16 +124,17 @@ (defn- typed-start-expression [type-name] (let [fhir-type (keyword "fhir" type-name) - pred #(identical? fhir-type (fhir-spec/fhir-type %))] + pred #(identical? fhir-type (:fhir/type %))] (->TypedStartExpression ((filter pred) conj)))) -(deftype GetChildrenExpression [f] +(deftype GetChildrenExpression [key f] Expression (-eval [_ _ coll] (.reduce ^IReduceInit coll f []))) (defn- get-children-expression [key] (->GetChildrenExpression + key (fn [res item] (let [val (get item key)] (cond @@ -147,14 +147,16 @@ (-eval [_ context coll] (-eval invocation context (-eval expression context coll)))) +;; 6.6.3. + (addition) + (deftype PlusExpression [left-expr right-expr] Expression (-eval [_ context coll] - (let [left (singleton :fhir/string (-eval left-expr context coll)) - right (singleton :fhir/string (-eval right-expr context coll))] + (let [left (singleton :system/string (-eval left-expr context coll)) + right (singleton :system/string (-eval right-expr context coll))] (cond - (empty? left) [right] - (empty? right) [left] + (empty? left) [] + (empty? right) [] :else [(str left right)])))) (defn- is-type-specifier-msg [coll] @@ -168,7 +170,7 @@ (case (coll/count coll) 0 coll - 1 [(identical? type-specifier (fhir-spec/fhir-type (coll/nth coll 0)))] + 1 [(identical? type-specifier (:fhir/type (coll/nth coll 0)))] (throw-anom (ba/incorrect (is-type-specifier-msg coll))))))) @@ -179,14 +181,14 @@ (case (coll/count coll) 0 [] - 1 (if (identical? type-specifier (fhir-spec/fhir-type (coll/nth coll 0))) + 1 (if (identical? type-specifier (:fhir/type (coll/nth coll 0))) coll []) ;; HACK: normally multiple items should throw an error. However in R4 many ;; FHIRPath expressions of search parameters use the as type specifier wrongly. ;; Please remove that hack for R5. - (filterv #(identical? type-specifier (fhir-spec/fhir-type %)) coll))))) + (filterv #(identical? type-specifier (:fhir/type %)) coll))))) (deftype UnionExpression [e1 e2] Expression @@ -239,11 +241,11 @@ (deftype AndExpression [expr-a expr-b] Expression (-eval [_ context coll] - (let [a (singleton :fhir/boolean (-eval expr-a context coll))] + (let [a (singleton :system/boolean (-eval expr-a context coll))] (if (false? a) [false] - (let [b (singleton :fhir/boolean (-eval expr-b context coll))] + (let [b (singleton :system/boolean (-eval expr-b context coll))] (cond (false? b) [false] (and (true? a) (true? b)) [true] @@ -255,19 +257,19 @@ (case (coll/count coll) 0 coll - 1 (if (identical? type-specifier (fhir-spec/fhir-type (coll/nth coll 0))) + 1 (if (identical? type-specifier (:fhir/type (coll/nth coll 0))) coll []) ;; HACK: normally multiple items should throw an error. However in R4 many ;; FHIRPath expressions of search parameters use the as type specifier wrongly. ;; Please remove that hack for R5. - (filterv #(identical? type-specifier (fhir-spec/fhir-type %)) coll)))) + (filterv #(identical? type-specifier (:fhir/type %)) coll)))) (deftype OfTypeFunctionExpression [type-specifier] Expression (-eval [_ _ coll] - (filterv #(identical? type-specifier (fhir-spec/fhir-type %)) coll))) + (filterv #(identical? type-specifier (:fhir/type %)) coll))) (deftype ExistsFunctionExpression [] Expression @@ -277,24 +279,23 @@ (deftype ExistsWithCriteriaFunctionExpression [criteria] Expression (-eval [_ _ _] - (throw-anom (ba/unsupported "unsupported `exists` function")))) + (throw-anom (ba/unsupported "unsupported `exists` function with criteria")))) -(defmulti ^IReduceInit resolve (fn [_ item] (fhir-spec/fhir-type item))) +(defmulti ^IReduceInit resolve (fn [_ item] (or (:fhir/type item) (system/type item)))) (defn- resolve* [resolver uri] (if-let [resource (-resolve resolver uri)] [resource] [])) -(defmethod resolve :fhir/string [{:keys [resolver]} uri] - (resolve* resolver (type/value uri))) +(defmethod resolve :system/string [{:keys [resolver]} uri] + (resolve* resolver uri)) -(defmethod resolve :fhir/Reference [{:keys [resolver]} {:keys [reference]}] - (resolve* resolver (type/value reference))) +(defmethod resolve :fhir/Reference [{:keys [resolver]} reference] + (resolve* resolver (-> reference :reference :value))) -(defmethod resolve :default [_ item] - (log/warn (format "Skip resolving %s `%s`." (name (fhir-spec/fhir-type item)) - (pr-str item))) +(defmethod resolve :default [_ {:fhir/keys [type] :as item}] + (log/warn (format "Skip resolving %s `%s`." (name type) (pr-str item))) []) (deftype ResolveFunctionExpression [] @@ -319,9 +320,9 @@ ;; 5.2.1. where(criteria : expression) : collection ;; See: http://hl7.org/fhirpath/#wherecriteria-expression-collection -(defn- non-boolean-result-msg [x] +(defn- non-boolean-result-msg [{:fhir/keys [type] :as x}] (format "non-boolean result `%s` of type `%s` while evaluating where function criteria" - (pr-str x) (fhir-spec/fhir-type x))) + (pr-str x) type)) (defn- multiple-result-msg [x] (format "multiple result items `%s` while evaluating where function criteria" @@ -375,7 +376,7 @@ Expression (-eval [_ context coll] (let [coll (-eval expression context coll) - idx (singleton :fhir/integer (-eval index context coll)) + idx (singleton :system/integer (-eval index context coll)) res (coll/nth coll idx nil)] (if (nil? res) [] [res])))) diff --git a/modules/fhir-path/src/blaze/fhir_path_spec.clj b/modules/fhir-path/src/blaze/fhir_path_spec.clj index d7647180a..4c619796c 100644 --- a/modules/fhir-path/src/blaze/fhir_path_spec.clj +++ b/modules/fhir-path/src/blaze/fhir_path_spec.clj @@ -2,7 +2,6 @@ (:require [blaze.anomaly-spec] [blaze.fhir-path :as fhir-path] - [blaze.fhir.spec :as fhir-spec] [blaze.fhir.spec-spec] [blaze.fhir.spec.type.system-spec] [blaze.util-spec] @@ -18,7 +17,7 @@ (s/fdef fhir-path/eval :args (s/cat :resolver :blaze.fhir-path/resolver :expr :blaze.fhir-path/expression - :value #(some? (fhir-spec/fhir-type %))) + :value #(keyword? (:fhir/type %))) :ret (s/or :coll (s/coll-of some?) :anomaly ::anom/anomaly)) (s/fdef fhir-path/compile diff --git a/modules/fhir-path/test/blaze/fhir_path_test.clj b/modules/fhir-path/test/blaze/fhir_path_test.clj index 3c48529c7..ecffb7acb 100644 --- a/modules/fhir-path/test/blaze/fhir_path_test.clj +++ b/modules/fhir-path/test/blaze/fhir_path_test.clj @@ -5,8 +5,6 @@ [blaze.anomaly :as ba] [blaze.fhir-path :as fhir-path] [blaze.fhir-path-spec] - [blaze.fhir.spec :as fhir-spec] - [blaze.fhir.spec.type] [blaze.fhir.test-util] [blaze.test-util :as tu :refer [satisfies-prop]] [clojure.spec.test.alpha :as st] @@ -57,54 +55,45 @@ "id-161537")) (testing "Patient.active" - (testing "value" - (are [x pred] (pred (first (eval "Patient.active" - {:fhir/type :fhir/Patient - :id "foo" - :active x}))) - true true? - false false?)) - (testing "type" - (are [x] (= :fhir/boolean - (fhir-spec/fhir-type (first (eval "Patient.active" - {:fhir/type :fhir/Patient - :id "foo" - :active x})))) - true - false))) + (are [x] (= x (first (eval "Patient.active" + {:fhir/type :fhir/Patient + :id "foo" + :active x}))) + #fhir/boolean true + #fhir/boolean false)) (testing "(Observation.value as boolean)" - (are [x pred] (pred (first (eval "(Observation.value as boolean)" - {:fhir/type :fhir/Observation - :id "foo" - :value x}))) - true true? - false false?))) + (are [x] (= x (first (eval "(Observation.value as boolean)" + {:fhir/type :fhir/Observation + :id "foo" + :value x}))) + #fhir/boolean true + #fhir/boolean false))) ;; 4. Expressions ;; 4.1 Literals (deftest boolean-test - (is (true? (first (eval "true" "foo")))) - (is (false? (first (eval "false" "foo"))))) + (is (true? (first (eval "true" #fhir/string "foo")))) + (is (false? (first (eval "false" #fhir/string "foo"))))) (deftest string-test - (is (= "bar" (first (eval "'bar'" "foo"))))) + (is (= "bar" (first (eval "'bar'" #fhir/string "foo"))))) (deftest integer-test - (is (zero? (first (eval "0" "foo"))))) + (is (zero? (first (eval "0" #fhir/string "foo"))))) (deftest decimal-test - (is (= 0.1M (first (eval "0.1" "foo"))))) + (is (= 0.1M (first (eval "0.1" #fhir/string "foo"))))) (deftest date-test - (are [expr date] (= date (first (eval expr "foo"))) + (are [expr date] (= date (first (eval expr #fhir/string "foo"))) "@2020" #system/date"2020" "@2020-01" #system/date"2020-01" "@2020-01-02" #system/date"2020-01-02")) (deftest date-time-test - (are [expr date-time] (= date-time (first (eval expr "foo"))) + (are [expr date-time] (= date-time (first (eval expr #fhir/string "foo"))) "@2020T" #system/date-time"2020" "@2020-01T" #system/date-time"2020-01" "@2020-01-02T" #system/date-time"2020-01-02" @@ -114,26 +103,25 @@ (deftest singleton-test (testing "string concatenation" (testing "with no given name" - (given (eval "Patient.name.family + ', ' + Patient.name.given" - {:fhir/type :fhir/Patient - :id "foo" - :name [#fhir/HumanName{:family #fhir/string "Doe"}]}) - identity := ["Doe, "])) + (is (empty? (eval "Patient.name.family + ', ' + Patient.name.given" + {:fhir/type :fhir/Patient + :id "foo" + :name [#fhir/HumanName{:family #fhir/string "Doe"}]})))) (testing "with one given name" - (given (eval "Patient.name.family + ', ' + Patient.name.given" + (is (= (eval "Patient.name.family + ', ' + Patient.name.given" {:fhir/type :fhir/Patient :id "foo" :name [#fhir/HumanName{:family #fhir/string "Doe" :given [#fhir/string "John"]}]}) - identity := ["Doe, John"])) + ["Doe, John"]))) (testing "with two given names" (given (eval "Patient.name.family + ', ' + Patient.name.given" {:fhir/type :fhir/Patient :id "foo" - :name [#fhir/HumanName{:family #fhir/string "Doe" :given [#fhir/string "John" "Foo"]}]}) + :name [#fhir/HumanName{:family #fhir/string "Doe" :given [#fhir/string "John" #fhir/string "Foo"]}]}) ::anom/category := ::anom/incorrect - ::anom/message := "unable to evaluate `[\"John\" \"Foo\"]` as singleton")) + ::anom/message := "unable to evaluate `[#fhir/string-interned \"John\" #fhir/string-interned \"Foo\"]` as singleton")) (testing "with non-convertible type" (given (eval "Patient.name.family + ', ' + Patient.name" @@ -141,7 +129,7 @@ :id "foo" :name [#fhir/HumanName{:family #fhir/string "Doe"}]}) ::anom/category := ::anom/incorrect - ::anom/message := "unable to evaluate `[#fhir/HumanName{:family \"Doe\"}]` as singleton"))) + ::anom/message := "unable to evaluate `[#fhir/HumanName{:family #fhir/string-interned \"Doe\"}]` as singleton"))) (testing "and expression" (testing "with one telecom" @@ -151,9 +139,9 @@ :active #fhir/boolean true :gender #fhir/code "female" :telecom - [{:fhir/type :fhir/ContactPoint - :use #fhir/code "home" - :value #fhir/string "foo"}]}) + [#fhir/ContactPoint + {:value #fhir/string "foo" + :use #fhir/code "home"}]}) identity := [true])) (testing "with two telecoms" @@ -163,14 +151,14 @@ :active #fhir/boolean true :gender #fhir/code "female" :telecom - [{:fhir/type :fhir/ContactPoint - :use #fhir/code "home" - :value #fhir/string "foo"} - {:fhir/type :fhir/ContactPoint - :use #fhir/code "work" - :value #fhir/string "bar"}]}) + [#fhir/ContactPoint + {:value #fhir/string "foo" + :use #fhir/code "home"} + #fhir/ContactPoint + {:value #fhir/string "bar" + :use #fhir/code "work"}]}) ::anom/category := ::anom/incorrect - ::anom/message := "unable to evaluate `[{:fhir/type :fhir/ContactPoint, :use #fhir/code\"home\", :value \"foo\"} {:fhir/type :fhir/ContactPoint, :use #fhir/code\"work\", :value \"bar\"}]` as singleton")))) + ::anom/message := "unable to evaluate `[#fhir/ContactPoint{:value #fhir/string-interned \"foo\" :use #fhir/code \"home\"} #fhir/ContactPoint{:value #fhir/string-interned \"bar\" :use #fhir/code \"work\"}]` as singleton")))) ;; 5. Functions @@ -217,18 +205,14 @@ ;; 5.1.2. exists([criteria : expression]) : Boolean (deftest exists-function-test - (given (eval - "Patient.deceased.exists()" - {:fhir/type :fhir/Patient - :id "id-182007"}) - identity := [false]) + (are [patient res] (= [res] (eval "Patient.deceased.exists()" patient)) + {:fhir/type :fhir/Patient} false + {:fhir/type :fhir/Patient :deceased #fhir/boolean true} true + {:fhir/type :fhir/Patient :deceased #fhir/boolean false} true) - (given (eval - "Patient.deceased.exists()" - {:fhir/type :fhir/Patient - :id "id-182007" - :deceased #fhir/boolean true}) - identity := [true])) + (given (eval "Patient.identifier.exists(use = 'official')" {:fhir/type :fhir/Patient}) + ::anom/category := ::anom/unsupported + ::anom/message := "unsupported `exists` function with criteria")) ;; 5.2. Filtering and projection @@ -245,9 +229,8 @@ {:fhir/type :fhir/Patient :id "id-162953" :telecom - [{:fhir/type :fhir/ContactPoint - :use #fhir/code "home" - :value #fhir/string "value-170758"}]}) + [#fhir/ContactPoint{:value #fhir/string "value-170758" + :use #fhir/code "home"}]}) [0 :use] := #fhir/code "home" [0 :value] := #fhir/string "value-170758")) @@ -257,12 +240,10 @@ {:fhir/type :fhir/Patient :id "id-162953" :telecom - [{:fhir/type :fhir/ContactPoint - :use #fhir/code "home" - :value #fhir/string "value-170758"} - {:fhir/type :fhir/ContactPoint - :use #fhir/code "home" - :value #fhir/string "value-145928"}]}) + [#fhir/ContactPoint{:value #fhir/string "value-170758" + :use #fhir/code "home"} + #fhir/ContactPoint{:value #fhir/string "value-145928" + :use #fhir/code "home"}]}) [0 :use] := #fhir/code "home" [0 :value] := #fhir/string "value-170758" [1 :use] := #fhir/code "home" @@ -274,9 +255,8 @@ {:fhir/type :fhir/Patient :id "id-162953" :telecom - [{:fhir/type :fhir/ContactPoint - :use #fhir/code "home" - :value #fhir/string "value-170758"}]}) + [#fhir/ContactPoint{:value #fhir/string "value-170758" + :use #fhir/code "home"}]}) count := 0)) (testing "returns empty collection on empty criteria result" @@ -285,9 +265,8 @@ {:fhir/type :fhir/Patient :id "id-162953" :telecom - [{:fhir/type :fhir/ContactPoint - :use #fhir/code "home" - :value #fhir/string "value-170758"}]}) + [#fhir/ContactPoint{:value #fhir/string "value-170758" + :use #fhir/code "home"}]}) count := 0)) (testing "returns empty collection on empty input" @@ -304,7 +283,7 @@ :id "id-162953" :address [#fhir/Address{:line [#fhir/string "a" #fhir/string "b"]}]}) ::anom/category := ::anom/incorrect - ::anom/message := "multiple result items `[\"a\" \"b\"]` while evaluating where function criteria")) + ::anom/message := "multiple result items `[#fhir/string-interned \"a\" #fhir/string-interned \"b\"]` while evaluating where function criteria")) (testing "return error on non-boolean criteria result" (given (eval @@ -312,11 +291,10 @@ {:fhir/type :fhir/Patient :id "id-162953" :telecom - [{:fhir/type :fhir/ContactPoint - :use #fhir/code "home" - :value #fhir/string "value-170758"}]}) + [#fhir/ContactPoint{:value #fhir/string "value-170758" + :use #fhir/code "home"}]}) ::anom/category := ::anom/incorrect - ::anom/message := "non-boolean result `#fhir/code\"home\"` of type `:fhir/code` while evaluating where function criteria"))) + ::anom/message := "non-boolean result `#fhir/code \"home\"` of type `:fhir/code` while evaluating where function criteria"))) ;; 5.2.4. ofType(type : type specifier) : collection (deftest of-type-function-test @@ -401,8 +379,8 @@ {:fhir/type :fhir/Patient :id "id-162953" :gender #fhir/code "female" - :birthDate #fhir/date "2020"}) - identity := [#fhir/code "female" #fhir/date "2020"])) + :birthDate #fhir/date #system/date "2020"}) + identity := [#fhir/code "female" #fhir/date #system/date "2020"])) ;; 6. Operations @@ -486,14 +464,14 @@ (given (eval "Patient.birthDate is date" {:fhir/type :fhir/Patient :id "foo" - :birthDate #fhir/date "2020"}) + :birthDate #fhir/date #system/date "2020"}) identity := [true])) (testing "single item with non-matching type returns false" (given (eval "Patient.birthDate is string" {:fhir/type :fhir/Patient :id "foo" - :birthDate #fhir/date "2020"}) + :birthDate #fhir/date #system/date "2020"}) identity := [false])) (testing "empty collection returns empty collection" @@ -510,7 +488,7 @@ [#fhir/Identifier{:value #fhir/string "value-163922"} #fhir/Identifier{:value #fhir/string "value-163928"}]}) ::anom/category := ::anom/incorrect - ::anom/message := "is type specifier with more than one item at the left side `[#fhir/Identifier{:value \"value-163922\"} #fhir/Identifier{:value \"value-163928\"}]`"))) + ::anom/message := "is type specifier with more than one item at the left side `[#fhir/Identifier{:value #fhir/string \"value-163922\"} #fhir/Identifier{:value #fhir/string \"value-163928\"}]`"))) ;; 6.3.3 as type specifier (deftest as-type-specifier-test @@ -518,14 +496,14 @@ (given (eval "Patient.birthDate as date" {:fhir/type :fhir/Patient :id "foo" - :birthDate #fhir/date "2020"}) - identity := [#fhir/date "2020"])) + :birthDate #fhir/date #system/date "2020"}) + identity := [#fhir/date #system/date "2020"])) (testing "single item with non-matching type returns an empty collection" (given (eval "Patient.birthDate as string" {:fhir/type :fhir/Patient :id "foo" - :birthDate #fhir/date "2020"}) + :birthDate #fhir/date #system/date "2020"}) count := 0)) (testing "empty collection returns empty collection" @@ -554,14 +532,14 @@ (given (eval "Patient.birthDate.as(date)" {:fhir/type :fhir/Patient :id "foo" - :birthDate #fhir/date "2020"}) - identity := [#fhir/date "2020"])) + :birthDate #fhir/date #system/date "2020"}) + identity := [#fhir/date #system/date "2020"])) (testing "single item with non-matching type returns an empty collection" (given (eval "Patient.birthDate.as(string)" {:fhir/type :fhir/Patient :id "foo" - :birthDate #fhir/date "2020"}) + :birthDate #fhir/date #system/date "2020"}) count := 0)) (testing "empty collection returns empty collection" @@ -588,14 +566,14 @@ ;; 6.5.1. and (deftest and-test - (are [expr pred] (pred (first (eval expr {:fhir/type :fhir/Patient - :id "id-162953" - :gender #fhir/code "male" - :birthDate #fhir/date "2020"}))) - "Patient.gender = 'male' and Patient.birthDate = @2020" true? - "Patient.gender = 'male' and Patient.birthDate = @2021" false? - "Patient.gender = 'female' and Patient.birthDate = @2020" false? - "Patient.gender = 'female' and Patient.birthDate = @2021" false?)) + (let [patient {:fhir/type :fhir/Patient + :gender #fhir/code "male" + :birthDate #fhir/date #system/date "2020"}] + (are [expr pred] (pred (first (eval expr patient))) + "Patient.gender = 'male' and Patient.birthDate = @2020" true? + "Patient.gender = 'male' and Patient.birthDate = @2021" false? + "Patient.gender = 'female' and Patient.birthDate = @2020" false? + "Patient.gender = 'female' and Patient.birthDate = @2021" false?))) ;; Additional functions (https://www.hl7.org/fhir/fhirpath.html#functions) @@ -611,4 +589,16 @@ "Patient.extension('url-145553').value" {:fhir/type :fhir/Patient :id "foo" :extension [#fhir/Extension{:url "url-145553" :value #fhir/string "value-145600"}]}) - identity := ["value-145600"])) + identity := [#fhir/string "value-145600"])) + +;; 6.6. Math + +;; 6.6.3. + (addition) + +(deftest plus-test + (testing "string" + (are [expr result] (= result (eval expr {:fhir/type :fhir/Patient :id "0"})) + "Patient.id + '1'" ["01"] + "'1' + Patient.id" ["10"] + "Patient.id + {}" [] + "{} + Patient.id" []))) diff --git a/modules/fhir-structure/Makefile b/modules/fhir-structure/Makefile index 4e962ca16..9f8fc3dfc 100644 --- a/modules/fhir-structure/Makefile +++ b/modules/fhir-structure/Makefile @@ -11,10 +11,13 @@ build: prep clojure -T:build compile test: build - clojure -M:test:kaocha --profile :ci + clojure -M:test:kaocha --profile :ci --skip-meta :mem-size + +test-mem-size: build + clojure -J-XX:+UseCompactObjectHeaders -J--sun-misc-unsafe-memory-access=allow -M:test:kaocha --profile :ci --focus-meta :mem-size test-coverage: build - clojure -M:test:coverage + clojure -M:test:coverage --skip-meta :mem-size deps-tree: clojure -X:deps tree diff --git a/modules/fhir-structure/build.clj b/modules/fhir-structure/build.clj index d1465ac5a..796754344 100644 --- a/modules/fhir-structure/build.clj +++ b/modules/fhir-structure/build.clj @@ -7,4 +7,4 @@ {:basis (b/create-basis {:project "deps.edn"}) :src-dirs ["java"] :class-dir "target/classes" - :javac-opts ["-Xlint:all" "-proc:none" "--release" "17"]})) + :javac-opts ["-Xlint:all" "-proc:none" "--release" "21"]})) diff --git a/modules/fhir-structure/deps.edn b/modules/fhir-structure/deps.edn index bd16f81d2..d37ba79b5 100644 --- a/modules/fhir-structure/deps.edn +++ b/modules/fhir-structure/deps.edn @@ -49,7 +49,10 @@ {:local/root "../executor"} blaze/fhir-test-util - {:local/root "../fhir-test-util"}}} + {:local/root "../fhir-test-util"} + + org.openjdk.jol/jol-core + {:mvn/version "0.17"}}} :kaocha {:extra-deps diff --git a/modules/fhir-structure/java/blaze/Interner.java b/modules/fhir-structure/java/blaze/Interner.java new file mode 100644 index 000000000..11ad9b179 --- /dev/null +++ b/modules/fhir-structure/java/blaze/Interner.java @@ -0,0 +1,6 @@ +package blaze; + +public interface Interner { + + V intern(K key); +} diff --git a/modules/fhir-structure/java/blaze/Interners.java b/modules/fhir-structure/java/blaze/Interners.java new file mode 100644 index 000000000..4a8876d45 --- /dev/null +++ b/modules/fhir-structure/java/blaze/Interners.java @@ -0,0 +1,76 @@ +package blaze; + +import clojure.lang.Util; + +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; + +import static java.util.Objects.requireNonNull; + +public final class Interners { + + private Interners() { + } + + public static Interner strongInterner(Function creator) { + return new StrongInterner<>(creator); + } + + public static Interner weakInterner(Function creator) { + return new WeakInterner<>(creator); + } + + private static final class StrongInterner implements Interner { + + private final ConcurrentHashMap table = new ConcurrentHashMap<>(); + private final Function creator; + + private StrongInterner(Function creator) { + this.creator = requireNonNull(creator); + } + + @Override + public V intern(K key) { + V existingVal = table.get(key); + if (existingVal != null) return existingVal; + V newVal = creator.apply(key); + existingVal = table.putIfAbsent(key, newVal); + return existingVal == null ? newVal : existingVal; + } + } + + private static final class WeakInterner implements Interner { + + private final ConcurrentHashMap> table = new ConcurrentHashMap<>(); + private final ReferenceQueue rq = new ReferenceQueue<>(); + private final Function creator; + + private WeakInterner(Function creator) { + this.creator = requireNonNull(creator); + } + + @Override + public V intern(K key) { + while (true) { + Reference existingRef = table.get(key); + if (existingRef == null) { + Util.clearCache(rq, table); + V newVal = creator.apply(key); + existingRef = table.putIfAbsent(key, new WeakReference<>(newVal, rq)); + if (existingRef == null) { + return newVal; + } + } + V existingVal = existingRef.get(); + if (existingVal != null) { + return existingVal; + } + //entry died in the interim, do over + table.remove(key, existingRef); + } + } + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/Hash.java b/modules/fhir-structure/java/blaze/fhir/Hash.java index e18bc3c92..5bf8bc890 100644 --- a/modules/fhir-structure/java/blaze/fhir/Hash.java +++ b/modules/fhir-structure/java/blaze/fhir/Hash.java @@ -44,9 +44,7 @@ public void copyTo(ByteBuffer target) { @Override public boolean equals(Object o) { if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Hash hash = (Hash) o; - return l0 == hash.l0 && l1 == hash.l1 && l2 == hash.l2 && l3 == hash.l3; + return o instanceof Hash that && l0 == that.l0 && l1 == that.l1 && l2 == that.l2 && l3 == that.l3; } @Override diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/AbstractBackboneElement.java b/modules/fhir-structure/java/blaze/fhir/spec/type/AbstractBackboneElement.java new file mode 100644 index 000000000..e64767891 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/AbstractBackboneElement.java @@ -0,0 +1,42 @@ +package blaze.fhir.spec.type; + +import clojure.lang.Keyword; +import clojure.lang.RT; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.io.SerializedString; + +import java.io.IOException; +import java.util.List; + +import static blaze.fhir.spec.type.Complex.serializeJsonComplexList; +import static java.util.Objects.requireNonNull; + +abstract class AbstractBackboneElement extends AbstractElement { + + protected final static Keyword MODIFIER_EXTENSION = RT.keyword(null, "modifierExtension"); + private static final SerializedString FIELD_NAME_MODIFIER_EXTENSION = new SerializedString("modifierExtension"); + + protected final List modifierExtension; + + protected AbstractBackboneElement(ExtensionData extensionData, List modifierExtension) { + super(extensionData); + this.modifierExtension = requireNonNull(modifierExtension); + } + + public final List modifierExtension() { + return modifierExtension; + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == MODIFIER_EXTENSION) return modifierExtension; + return super.valAt(key, notFound); + } + + protected void serializeJsonBase(JsonGenerator generator) throws IOException { + super.serializeJsonBase(generator); + if (!modifierExtension.isEmpty()) { + serializeJsonComplexList(modifierExtension, generator, FIELD_NAME_MODIFIER_EXTENSION); + } + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/AbstractElement.java b/modules/fhir-structure/java/blaze/fhir/spec/type/AbstractElement.java new file mode 100644 index 000000000..3b07d78c1 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/AbstractElement.java @@ -0,0 +1,59 @@ +package blaze.fhir.spec.type; + +import clojure.lang.IPersistentMap; +import clojure.lang.PersistentVector; +import com.fasterxml.jackson.core.JsonGenerator; + +import java.io.IOException; +import java.lang.String; +import java.util.List; +import java.util.stream.Stream; + +import static java.util.Objects.requireNonNull; + +abstract class AbstractElement implements Element { + + protected final ExtensionData extensionData; + + protected AbstractElement(ExtensionData extensionData) { + this.extensionData = requireNonNull(extensionData); + } + + public final String id() { + return extensionData.id; + } + + public final List extension() { + return extensionData.extension; + } + + @Override + public Object valAt(Object key, Object notFound) { + return extensionData.valAt(key, notFound); + } + + @Override + public Stream references() { + return extensionData.references(); + } + + @Override + public IPersistentMap meta() { + return extensionData.meta; + } + + protected void serializeJsonBase(JsonGenerator generator) throws IOException { + extensionData.serializeJson(generator); + } + + @Override + public void serializeJsonPrimitiveExtension(JsonGenerator generator) throws IOException { + if (extensionData.isNotEmpty()) { + generator.writeStartObject(); + serializeJsonBase(generator); + generator.writeEndObject(); + } else { + generator.writeNull(); + } + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/AbstractQuantity.java b/modules/fhir-structure/java/blaze/fhir/spec/type/AbstractQuantity.java new file mode 100644 index 000000000..a87377eae --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/AbstractQuantity.java @@ -0,0 +1,210 @@ +package blaze.fhir.spec.type; + +import clojure.lang.ISeq; +import clojure.lang.Keyword; +import clojure.lang.PersistentList; +import clojure.lang.RT; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.util.Iterator; +import java.util.Objects; + +import static blaze.fhir.spec.type.Base.appendElement; +import static java.util.Objects.requireNonNull; + +public sealed abstract class AbstractQuantity extends AbstractElement implements Complex, ExtensionValue permits + Age, Count, Distance, Duration, Quantity { + + protected static final Keyword VALUE = RT.keyword(null, "value"); + protected static final Keyword COMPARATOR = RT.keyword(null, "comparator"); + protected static final Keyword UNIT = RT.keyword(null, "unit"); + protected static final Keyword SYSTEM = RT.keyword(null, "system"); + protected static final Keyword CODE = RT.keyword(null, "code"); + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - extension data reference + * 4 byte - value reference + * 4 byte - comparator reference + * 4 byte - unit reference + * 4 byte - system reference + * 4 byte - code reference + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 24; + private static final Keyword[] FIELDS = {ID, EXTENSION, VALUE, COMPARATOR, UNIT, SYSTEM, CODE}; + + private static final FieldName FIELD_NAME_VALUE = FieldName.of("value"); + private static final FieldName FIELD_NAME_COMPARATOR = FieldName.of("comparator"); + private static final FieldName FIELD_NAME_UNIT = FieldName.of("unit"); + private static final FieldName FIELD_NAME_SYSTEM = FieldName.of("system"); + private static final FieldName FIELD_NAME_CODE = FieldName.of("code"); + + private static final byte HASH_MARKER = 40; + + protected final Decimal value; + protected final Code comparator; + protected final String unit; + protected final Uri system; + protected final Code code; + + protected AbstractQuantity(ExtensionData extensionData, Decimal value, Code comparator, String unit, Uri system, Code code) { + super(extensionData); + this.value = value; + this.comparator = comparator; + this.unit = unit; + this.system = system; + this.code = code; + } + + @Override + public boolean isInterned() { + return extensionData.isInterned() && Base.isInterned(value) && Base.isInterned(comparator) && + Base.isInterned(unit) && Base.isInterned(system) && Base.isInterned(code); + } + + public Decimal value() { + return value; + } + + public Code comparator() { + return comparator; + } + + public String unit() { + return unit; + } + + public Uri system() { + return system; + } + + public Code code() { + return code; + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == VALUE) return value; + if (key == CODE) return code; + if (key == SYSTEM) return system; + if (key == UNIT) return unit; + if (key == COMPARATOR) return comparator; + return super.valAt(key, notFound); + } + + @Override + public ISeq seq() { + ISeq seq = PersistentList.EMPTY; + seq = appendElement(seq, CODE, code); + seq = appendElement(seq, SYSTEM, system); + seq = appendElement(seq, UNIT, unit); + seq = appendElement(seq, COMPARATOR, comparator); + seq = appendElement(seq, VALUE, value); + return extensionData.append(seq); + } + + @Override + public Iterator> iterator() { + return new BaseIterator(this, FIELDS); + } + + @Override + public void serializeAsJsonValue(JsonGenerator generator) throws IOException { + generator.writeStartObject(); + serializeJsonBase(generator); + if (value != null) { + value.serializeAsJsonProperty(generator, FIELD_NAME_VALUE); + } + if (comparator != null) { + comparator.serializeAsJsonProperty(generator, FIELD_NAME_COMPARATOR); + } + if (unit != null) { + unit.serializeAsJsonProperty(generator, FIELD_NAME_UNIT); + } + if (system != null) { + system.serializeAsJsonProperty(generator, FIELD_NAME_SYSTEM); + } + if (code != null) { + code.serializeAsJsonProperty(generator, FIELD_NAME_CODE); + } + generator.writeEndObject(); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (value != null) { + sink.putByte((byte) 2); + value.hashInto(sink); + } + if (comparator != null) { + sink.putByte((byte) 3); + comparator.hashInto(sink); + } + if (unit != null) { + sink.putByte((byte) 4); + unit.hashInto(sink); + } + if (system != null) { + sink.putByte((byte) 5); + system.hashInto(sink); + } + if (code != null) { + sink.putByte((byte) 6); + code.hashInto(sink); + } + } + + @Override + public int memSize() { + return isInterned() ? 0 : MEM_SIZE_OBJECT + extensionData.memSize() + Base.memSize(value) + + Base.memSize(comparator) + Base.memSize(unit) + Base.memSize(system) + Base.memSize(code); + } + + @Override + public final boolean equals(Object o) { + if (this == o) return true; + return o instanceof AbstractQuantity that && + extensionData.equals(that.extensionData) && + Objects.equals(value, that.value) && + Objects.equals(comparator, that.comparator) && + Objects.equals(unit, that.unit) && + Objects.equals(system, that.system) && + Objects.equals(code, that.code); + } + + @Override + public final int hashCode() { + int result = extensionData.hashCode(); + result = 31 * result + Objects.hashCode(value); + result = 31 * result + Objects.hashCode(comparator); + result = 31 * result + Objects.hashCode(unit); + result = 31 * result + Objects.hashCode(system); + result = 31 * result + Objects.hashCode(code); + return result; + } + + @Override + public java.lang.String toString() { + return "Quantity{" + + extensionData + + ", value=" + value + + ", comparator=" + comparator + + ", unit=" + unit + + ", system=" + system + + ", code=" + code + + '}'; + } + + protected record InternerKey(ExtensionData extensionData, Decimal value, Code comparator, String unit, Uri system, + Code code) { + protected InternerKey { + requireNonNull(extensionData); + } + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Address.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Address.java new file mode 100644 index 000000000..fa1eb0f7d --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Address.java @@ -0,0 +1,367 @@ +package blaze.fhir.spec.type; + +import clojure.lang.*; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.io.SerializedString; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; + +import static blaze.fhir.spec.type.Base.appendElement; +import static java.util.Objects.requireNonNull; + +@SuppressWarnings("DuplicatedCode") +public final class Address extends AbstractElement implements Complex, ExtensionValue { + + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - extension data reference + * 4 byte - use reference + * 4 byte - type reference + * 4 byte - text reference + * 4 byte - line reference + * 4 byte - city reference + * 4 byte - district reference + * 4 byte - state reference + * 4 byte - postalCode reference + * 4 byte - country reference + * 4 byte - period reference + * 4 byte - padding + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 48; + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "Address"); + + private static final Keyword USE = RT.keyword(null, "use"); + private static final Keyword TYPE = RT.keyword(null, "type"); + private static final Keyword TEXT = RT.keyword(null, "text"); + private static final Keyword LINE = RT.keyword(null, "line"); + private static final Keyword CITY = RT.keyword(null, "city"); + private static final Keyword DISTRICT = RT.keyword(null, "district"); + private static final Keyword STATE = RT.keyword(null, "state"); + private static final Keyword POSTAL_CODE = RT.keyword(null, "postalCode"); + private static final Keyword COUNTRY = RT.keyword(null, "country"); + private static final Keyword PERIOD = RT.keyword(null, "period"); + + private static final Keyword[] FIELDS = {ID, EXTENSION, USE, TYPE, TEXT, LINE, CITY, DISTRICT, STATE, POSTAL_CODE, COUNTRY, PERIOD}; + + private static final FieldName FIELD_NAME_USE = FieldName.of("use"); + private static final FieldName FIELD_NAME_TYPE = FieldName.of("type"); + private static final FieldName FIELD_NAME_TEXT = FieldName.of("text"); + private static final FieldName FIELD_NAME_LINE = FieldName.of("line"); + private static final FieldName FIELD_NAME_CITY = FieldName.of("city"); + private static final FieldName FIELD_NAME_DISTRICT = FieldName.of("district"); + private static final FieldName FIELD_NAME_STATE = FieldName.of("state"); + private static final FieldName FIELD_NAME_POSTAL_CODE = FieldName.of("postalCode"); + private static final FieldName FIELD_NAME_COUNTRY = FieldName.of("country"); + private static final FieldName FIELD_NAME_PERIOD = FieldName.of("period"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueAddress"); + + private static final byte HASH_MARKER = 47; + + @SuppressWarnings("unchecked") + private static final Address EMPTY = new Address(ExtensionData.EMPTY, null, null, null, PersistentVector.EMPTY, + null, null, null, null, null, null); + + private final Code use; + private final Code type; + private final String text; + private final List line; + private final String city; + private final String district; + private final String state; + private final String postalCode; + private final String country; + private final Period period; + + private Address(ExtensionData extensionData, Code use, Code type, String text, List line, + String city, String district, String state, String postalCode, String country, Period period) { + super(extensionData); + this.use = use; + this.type = type; + this.text = text; + this.line = requireNonNull(line); + this.city = city; + this.district = district; + this.state = state; + this.postalCode = postalCode; + this.country = country; + this.period = period; + } + + public static Address create(IPersistentMap m) { + return new Address(ExtensionData.fromMap(m), (Code) m.valAt(USE), (Code) m.valAt(TYPE), (String) m.valAt(TEXT), + Base.listFrom(m, LINE), (String) m.valAt(CITY), (String) m.valAt(DISTRICT), (String) m.valAt(STATE), + (String) m.valAt(POSTAL_CODE), (String) m.valAt(COUNTRY), (Period) m.valAt(PERIOD)); + } + + public Code use() { + return use; + } + + public Code type() { + return type; + } + + public String text() { + return text; + } + + public List line() { + return line; + } + + public String city() { + return city; + } + + public String district() { + return district; + } + + public String state() { + return state; + } + + public String postalCode() { + return postalCode; + } + + public String country() { + return country; + } + + public Period period() { + return period; + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE; + if (key == USE) return use; + if (key == TYPE) return type; + if (key == TEXT) return text; + if (key == LINE) return line; + if (key == CITY) return city; + if (key == DISTRICT) return district; + if (key == STATE) return state; + if (key == POSTAL_CODE) return postalCode; + if (key == COUNTRY) return country; + if (key == PERIOD) return period; + return super.valAt(key, notFound); + } + + @Override + public ISeq seq() { + ISeq seq = PersistentList.EMPTY; + seq = appendElement(seq, PERIOD, period); + seq = appendElement(seq, COUNTRY, country); + seq = appendElement(seq, POSTAL_CODE, postalCode); + seq = appendElement(seq, STATE, state); + seq = appendElement(seq, DISTRICT, district); + seq = appendElement(seq, CITY, city); + if (!line.isEmpty()) { + seq = appendElement(seq, LINE, line); + } + seq = appendElement(seq, TEXT, text); + seq = appendElement(seq, TYPE, type); + seq = appendElement(seq, USE, use); + return extensionData.append(seq); + } + + @Override + public Address empty() { + return EMPTY; + } + + @Override + public Iterator> iterator() { + return new BaseIterator(this, FIELDS); + } + + @Override + public Address assoc(Object key, Object val) { + if (key == USE) + return new Address(extensionData, (Code) val, type, text, line, city, district, state, postalCode, country, period); + if (key == TYPE) + return new Address(extensionData, use, (Code) val, text, line, city, district, state, postalCode, country, period); + if (key == TEXT) + return new Address(extensionData, use, type, (String) val, line, city, district, state, postalCode, country, period); + if (key == LINE) + return new Address(extensionData, use, type, text, Lists.nullToEmpty(val), city, district, state, postalCode, country, period); + if (key == CITY) + return new Address(extensionData, use, type, text, line, (String) val, district, state, postalCode, country, period); + if (key == DISTRICT) + return new Address(extensionData, use, type, text, line, city, (String) val, state, postalCode, country, period); + if (key == STATE) + return new Address(extensionData, use, type, text, line, city, district, (String) val, postalCode, country, period); + if (key == POSTAL_CODE) + return new Address(extensionData, use, type, text, line, city, district, state, (String) val, country, period); + if (key == COUNTRY) + return new Address(extensionData, use, type, text, line, city, district, state, postalCode, (String) val, period); + if (key == PERIOD) + return new Address(extensionData, use, type, text, line, city, district, state, postalCode, country, (Period) val); + if (key == EXTENSION) + return new Address(extensionData.withExtension(val), use, type, text, line, city, district, state, postalCode, country, period); + if (key == ID) + return new Address(extensionData.withId(val), use, type, text, line, city, district, state, postalCode, country, period); + return this; + } + + @Override + public Address withMeta(IPersistentMap meta) { + return new Address(extensionData.withMeta(meta), use, type, text, line, city, district, state, postalCode, country, period); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeAsJsonValue(JsonGenerator generator) throws IOException { + generator.writeStartObject(); + serializeJsonBase(generator); + if (use != null) { + use.serializeAsJsonProperty(generator, FIELD_NAME_USE); + } + if (type != null) { + type.serializeAsJsonProperty(generator, FIELD_NAME_TYPE); + } + if (text != null) { + text.serializeAsJsonProperty(generator, FIELD_NAME_TEXT); + } + if (!line.isEmpty()) { + Primitive.serializeJsonPrimitiveList(line, generator, FIELD_NAME_LINE); + } + if (city != null) { + city.serializeAsJsonProperty(generator, FIELD_NAME_CITY); + } + if (district != null) { + district.serializeAsJsonProperty(generator, FIELD_NAME_DISTRICT); + } + if (state != null) { + state.serializeAsJsonProperty(generator, FIELD_NAME_STATE); + } + if (postalCode != null) { + postalCode.serializeAsJsonProperty(generator, FIELD_NAME_POSTAL_CODE); + } + if (country != null) { + country.serializeAsJsonProperty(generator, FIELD_NAME_COUNTRY); + } + if (period != null) { + period.serializeJsonField(generator, FIELD_NAME_PERIOD); + } + generator.writeEndObject(); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (use != null) { + sink.putByte((byte) 2); + use.hashInto(sink); + } + if (type != null) { + sink.putByte((byte) 3); + type.hashInto(sink); + } + if (text != null) { + sink.putByte((byte) 4); + text.hashInto(sink); + } + if (!line.isEmpty()) { + sink.putByte((byte) 5); + Base.hashIntoList(line, sink); + } + if (city != null) { + sink.putByte((byte) 6); + city.hashInto(sink); + } + if (district != null) { + sink.putByte((byte) 7); + district.hashInto(sink); + } + if (state != null) { + sink.putByte((byte) 8); + state.hashInto(sink); + } + if (postalCode != null) { + sink.putByte((byte) 9); + postalCode.hashInto(sink); + } + if (country != null) { + sink.putByte((byte) 10); + country.hashInto(sink); + } + if (period != null) { + sink.putByte((byte) 11); + period.hashInto(sink); + } + } + + @Override + public int memSize() { + return MEM_SIZE_OBJECT + extensionData.memSize() + Base.memSize(use) + Base.memSize(type) + Base.memSize(text) + + Base.memSize(line) + Base.memSize(city) + Base.memSize(district) + Base.memSize(state) + + Base.memSize(postalCode) + Base.memSize(country) + Base.memSize(period); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof Address that && + extensionData.equals(that.extensionData) && + Objects.equals(use, that.use) && + Objects.equals(type, that.type) && + Objects.equals(text, that.text) && + Objects.equals(line, that.line) && + Objects.equals(city, that.city) && + Objects.equals(district, that.district) && + Objects.equals(state, that.state) && + Objects.equals(postalCode, that.postalCode) && + Objects.equals(country, that.country) && + Objects.equals(period, that.period); + } + + @Override + public int hashCode() { + int result = extensionData.hashCode(); + result = 31 * result + Objects.hashCode(use); + result = 31 * result + Objects.hashCode(type); + result = 31 * result + Objects.hashCode(text); + result = 31 * result + Objects.hashCode(line); + result = 31 * result + Objects.hashCode(city); + result = 31 * result + Objects.hashCode(district); + result = 31 * result + Objects.hashCode(state); + result = 31 * result + Objects.hashCode(postalCode); + result = 31 * result + Objects.hashCode(country); + result = 31 * result + Objects.hashCode(period); + return result; + } + + @Override + public java.lang.String toString() { + return "Address{" + + extensionData + + ", use=" + use + + ", type=" + type + + ", text=" + text + + ", line=" + line + + ", city=" + city + + ", district=" + district + + ", state=" + state + + ", postalCode=" + postalCode + + ", country=" + country + + ", period=" + period + + '}'; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Age.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Age.java new file mode 100644 index 000000000..cd1cc6b49 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Age.java @@ -0,0 +1,71 @@ +package blaze.fhir.spec.type; + +import blaze.Interner; +import blaze.Interners; +import clojure.lang.IPersistentMap; +import clojure.lang.Keyword; +import clojure.lang.RT; + +@SuppressWarnings("DuplicatedCode") +public final class Age extends AbstractQuantity { + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "Age"); + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueAge"); + + private static final Age EMPTY = new Age(ExtensionData.EMPTY, null, null, null, null, null); + + private static final Interner INTERNER = Interners.weakInterner( + k -> new Age(k.extensionData(), k.value(), k.comparator(), k.unit(), k.system(), k.code()) + ); + + private Age(ExtensionData extensionData, Decimal value, Code comparator, String unit, Uri system, Code code) { + super(extensionData, value, comparator, unit, system, code); + } + + private static Age maybeIntern(ExtensionData extensionData, Decimal value, Code comparator, String unit, + Uri system, Code code) { + return extensionData.isInterned() && Base.isInterned(value) && Base.isInterned(comparator) && + Base.isInterned(unit) && Base.isInterned(system) && Base.isInterned(code) + ? INTERNER.intern(new InternerKey(extensionData, value, comparator, unit, system, code)) + : new Age(extensionData, value, comparator, unit, system, code); + } + + public static Age create(IPersistentMap m) { + return maybeIntern(ExtensionData.fromMap(m), (Decimal) m.valAt(VALUE), (Code) m.valAt(COMPARATOR), + (String) m.valAt(UNIT), (Uri) m.valAt(SYSTEM), (Code) m.valAt(CODE)); + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE; + return super.valAt(key, notFound); + } + + @Override + public Age empty() { + return EMPTY; + } + + @Override + public Age assoc(Object key, Object val) { + if (key == VALUE) return maybeIntern(extensionData, (Decimal) val, comparator, unit, system, code); + if (key == COMPARATOR) return maybeIntern(extensionData, value, (Code) val, unit, system, code); + if (key == UNIT) return maybeIntern(extensionData, value, comparator, (String) val, system, code); + if (key == SYSTEM) return maybeIntern(extensionData, value, comparator, unit, (Uri) val, code); + if (key == CODE) return maybeIntern(extensionData, value, comparator, unit, system, (Code) val); + if (key == EXTENSION) + return maybeIntern(extensionData.withExtension(val), value, comparator, unit, system, code); + if (key == ID) return maybeIntern(extensionData.withId(val), value, comparator, unit, system, code); + return this; + } + + @Override + public Age withMeta(IPersistentMap meta) { + return maybeIntern(extensionData.withMeta(meta), value, comparator, unit, system, code); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Annotation.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Annotation.java new file mode 100644 index 000000000..034723abc --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Annotation.java @@ -0,0 +1,197 @@ +package blaze.fhir.spec.type; + +import clojure.lang.*; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.util.Iterator; +import java.util.Objects; + +import static blaze.fhir.spec.type.Base.appendElement; + +@SuppressWarnings("DuplicatedCode") +public final class Annotation extends AbstractElement implements Complex, ExtensionValue { + + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - extension data reference + * 4 byte - author reference + * 4 byte - time reference + * 4 byte - text reference + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 16; + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "Annotation"); + + private static final Keyword AUTHOR = RT.keyword(null, "author"); + private static final Keyword TIME = RT.keyword(null, "time"); + private static final Keyword TEXT = RT.keyword(null, "text"); + + private static final Keyword[] FIELDS = {ID, EXTENSION, AUTHOR, TIME, TEXT}; + + private static final FieldName FIELD_NAME_AUTHOR_REFERENCE = FieldName.of("authorReference"); + private static final FieldName FIELD_NAME_AUTHOR_STRING = FieldName.of("authorString"); + private static final FieldName FIELD_NAME_TIME = FieldName.of("time"); + private static final FieldName FIELD_NAME_TEXT = FieldName.of("text"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueAnnotation"); + + private static final byte HASH_MARKER = 49; + + private static final Annotation EMPTY = new Annotation(ExtensionData.EMPTY, null, null, null); + + private final Element author; + private final DateTime time; + private final Markdown text; + + private Annotation(ExtensionData extensionData, Element author, DateTime time, Markdown text) { + super(extensionData); + this.author = author; + this.time = time; + this.text = text; + } + + public static Annotation create(IPersistentMap m) { + return new Annotation(ExtensionData.fromMap(m), (Element) m.valAt(AUTHOR), (DateTime) m.valAt(TIME), + (Markdown) m.valAt(TEXT)); + } + + public Base author() { + return author; + } + + public DateTime time() { + return time; + } + + public Markdown text() { + return text; + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE; + if (key == AUTHOR) return author; + if (key == TIME) return time; + if (key == TEXT) return text; + return super.valAt(key, notFound); + } + + @Override + public ISeq seq() { + ISeq seq = PersistentList.EMPTY; + seq = appendElement(seq, TEXT, text); + seq = appendElement(seq, TIME, time); + seq = appendElement(seq, AUTHOR, author); + return extensionData.append(seq); + } + + @Override + public Annotation empty() { + return EMPTY; + } + + @Override + public Iterator> iterator() { + return new BaseIterator(this, FIELDS); + } + + @Override + public Annotation assoc(Object key, Object val) { + if (key == AUTHOR) return new Annotation(extensionData, (Element) val, time, text); + if (key == TIME) return new Annotation(extensionData, author, (DateTime) val, text); + if (key == TEXT) return new Annotation(extensionData, author, time, (Markdown) val); + if (key == EXTENSION) return new Annotation(extensionData.withExtension(val), author, time, text); + if (key == ID) return new Annotation(extensionData.withId(val), author, time, text); + return this; + } + + @Override + public Annotation withMeta(IPersistentMap meta) { + return new Annotation(extensionData.withMeta(meta), author, time, text); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeAsJsonValue(JsonGenerator generator) throws IOException { + generator.writeStartObject(); + serializeJsonBase(generator); + if (author != null) { + switch (author) { + case Reference authorReference -> + authorReference.serializeJsonField(generator, FIELD_NAME_AUTHOR_REFERENCE); + case String authorString -> + authorString.serializeAsJsonProperty(generator, FIELD_NAME_AUTHOR_STRING); + default -> { + } + } + } + if (time != null) { + time.serializeAsJsonProperty(generator, FIELD_NAME_TIME); + } + if (text != null) { + text.serializeAsJsonProperty(generator, FIELD_NAME_TEXT); + } + generator.writeEndObject(); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (author != null) { + sink.putByte((byte) 2); + author.hashInto(sink); + } + if (time != null) { + sink.putByte((byte) 3); + time.hashInto(sink); + } + if (text != null) { + sink.putByte((byte) 4); + text.hashInto(sink); + } + } + + @Override + public int memSize() { + return MEM_SIZE_OBJECT + extensionData.memSize() + Base.memSize(author) + Base.memSize(time) + Base.memSize(text); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof Annotation that && + extensionData.equals(that.extensionData) && + Objects.equals(author, that.author) && + Objects.equals(time, that.time) && + Objects.equals(text, that.text); + } + + @Override + public int hashCode() { + int result = extensionData.hashCode(); + result = 31 * result + Objects.hashCode(author); + result = 31 * result + Objects.hashCode(time); + result = 31 * result + Objects.hashCode(text); + return result; + } + + @Override + public java.lang.String toString() { + return "Annotation{" + + extensionData + + ", author=" + author + + ", time=" + time + + ", text=" + text + + '}'; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/AsciiByteArrayAppendable.java b/modules/fhir-structure/java/blaze/fhir/spec/type/AsciiByteArrayAppendable.java new file mode 100644 index 000000000..209bfcf64 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/AsciiByteArrayAppendable.java @@ -0,0 +1,52 @@ +package blaze.fhir.spec.type; + +import java.io.IOException; + +/** + * An appendable that only allows 7-bit ASCII chars to append. + *

+ * Useful in situations like date/time formatting where only 7-bit ASCII chars + * are created. + *

+ * The class is not thread safe and mutable. Please use with care. + */ +final class AsciiByteArrayAppendable implements Appendable { + + private final byte[] buffer; + private int i = 0; + + AsciiByteArrayAppendable(int size) { + buffer = new byte[size]; + } + + @Override + public Appendable append(CharSequence csq) throws IOException { + return append(csq, 0, csq.length()); + } + + @Override + public Appendable append(CharSequence csq, int start, int end) throws IOException { + for (int i = start; i < end; i++) { + append(csq.charAt(i)); + } + return this; + } + + @Override + public Appendable append(char c) throws IOException { + if (c <= 127) { + buffer[i++] = (byte) c; + } else { + throw new IOException("Illegal char: " + c); + } + return this; + } + + byte[] toByteArray() { + return buffer; + } + + int length() { + return i; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Attachment.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Attachment.java new file mode 100644 index 000000000..d4d47fbd7 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Attachment.java @@ -0,0 +1,314 @@ +package blaze.fhir.spec.type; + +import clojure.lang.*; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.util.Iterator; +import java.util.Objects; + +import static blaze.fhir.spec.type.Base.appendElement; + +@SuppressWarnings("DuplicatedCode") +public final class Attachment extends AbstractElement implements Complex, ExtensionValue { + + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - extension data reference + * 4 byte - contentType reference + * 4 byte - language reference + * 4 byte - data reference + * 4 byte - url reference + * 4 byte - size reference + * 4 byte - hash reference + * 4 byte - title reference + * 4 byte - creation reference + * 4 byte - padding + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 40; + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "Attachment"); + + private static final Keyword CONTENT_TYPE = RT.keyword(null, "contentType"); + private static final Keyword LANGUAGE = RT.keyword(null, "language"); + private static final Keyword DATA = RT.keyword(null, "data"); + private static final Keyword URL = RT.keyword(null, "url"); + private static final Keyword SIZE = RT.keyword(null, "size"); + private static final Keyword HASH = RT.keyword(null, "hash"); + private static final Keyword TITLE = RT.keyword(null, "title"); + private static final Keyword CREATION = RT.keyword(null, "creation"); + + private static final Keyword[] FIELDS = {ID, EXTENSION, CONTENT_TYPE, LANGUAGE, DATA, URL, SIZE, HASH, TITLE, CREATION}; + + private static final FieldName FIELD_NAME_CONTENT_TYPE = FieldName.of("contentType"); + private static final FieldName FIELD_NAME_LANGUAGE = FieldName.of("language"); + private static final FieldName FIELD_NAME_DATA = FieldName.of("data"); + private static final FieldName FIELD_NAME_URL = FieldName.of("url"); + private static final FieldName FIELD_NAME_SIZE = FieldName.of("size"); + private static final FieldName FIELD_NAME_HASH = FieldName.of("hash"); + private static final FieldName FIELD_NAME_TITLE = FieldName.of("title"); + private static final FieldName FIELD_NAME_CREATION = FieldName.of("creation"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueAttachment"); + + private static final byte HASH_MARKER = 46; + + private static final Attachment EMPTY = new Attachment(ExtensionData.EMPTY, null, null, null, null, null, null, null, null); + + private final Code contentType; + private final Code language; + private final Base64Binary data; + private final Url url; + private final UnsignedInt size; + private final Base64Binary hash; + private final String title; + private final DateTime creation; + + private Attachment(ExtensionData extensionData, Code contentType, Code language, Base64Binary data, Url url, + UnsignedInt size, Base64Binary hash, String title, DateTime creation) { + super(extensionData); + this.contentType = contentType; + this.language = language; + this.data = data; + this.url = url; + this.size = size; + this.hash = hash; + this.title = title; + this.creation = creation; + } + + public static Attachment create(IPersistentMap m) { + return new Attachment(ExtensionData.fromMap(m), (Code) m.valAt(CONTENT_TYPE), (Code) m.valAt(LANGUAGE), + (Base64Binary) m.valAt(DATA), (Url) m.valAt(URL), (UnsignedInt) m.valAt(SIZE), + (Base64Binary) m.valAt(HASH), (String) m.valAt(TITLE), (DateTime) m.valAt(CREATION)); + } + + public Code contentType() { + return contentType; + } + + public Code language() { + return language; + } + + public Base64Binary data() { + return data; + } + + public Url url() { + return url; + } + + public UnsignedInt sizeValue() { + return size; + } + + public Base64Binary hash() { + return hash; + } + + public String title() { + return title; + } + + public DateTime creation() { + return creation; + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE; + if (key == CONTENT_TYPE) return contentType; + if (key == LANGUAGE) return language; + if (key == DATA) return data; + if (key == URL) return url; + if (key == SIZE) return size; + if (key == HASH) return hash; + if (key == TITLE) return title; + if (key == CREATION) return creation; + return super.valAt(key, notFound); + } + + @Override + public ISeq seq() { + ISeq seq = PersistentList.EMPTY; + seq = appendElement(seq, CREATION, creation); + seq = appendElement(seq, TITLE, title); + seq = appendElement(seq, HASH, hash); + seq = appendElement(seq, SIZE, size); + seq = appendElement(seq, URL, url); + seq = appendElement(seq, DATA, data); + seq = appendElement(seq, LANGUAGE, language); + seq = appendElement(seq, CONTENT_TYPE, contentType); + return extensionData.append(seq); + } + + @Override + public Attachment empty() { + return EMPTY; + } + + @Override + public Iterator> iterator() { + return new BaseIterator(this, FIELDS); + } + + @Override + public Attachment assoc(Object key, Object val) { + if (key == CONTENT_TYPE) + return new Attachment(extensionData, (Code) val, language, data, url, size, hash, title, creation); + if (key == LANGUAGE) + return new Attachment(extensionData, contentType, (Code) val, data, url, size, hash, title, creation); + if (key == DATA) + return new Attachment(extensionData, contentType, language, (Base64Binary) val, url, size, hash, title, creation); + if (key == URL) + return new Attachment(extensionData, contentType, language, data, (Url) val, size, hash, title, creation); + if (key == SIZE) + return new Attachment(extensionData, contentType, language, data, url, (UnsignedInt) val, hash, title, creation); + if (key == HASH) + return new Attachment(extensionData, contentType, language, data, url, size, (Base64Binary) val, title, creation); + if (key == TITLE) + return new Attachment(extensionData, contentType, language, data, url, size, hash, (String) val, creation); + if (key == CREATION) + return new Attachment(extensionData, contentType, language, data, url, size, hash, title, (DateTime) val); + if (key == EXTENSION) + return new Attachment(extensionData.withExtension(val), contentType, language, data, url, size, hash, title, creation); + if (key == ID) + return new Attachment(extensionData.withId(val), contentType, language, data, url, size, hash, title, creation); + return this; + } + + @Override + public Attachment withMeta(IPersistentMap meta) { + return new Attachment(extensionData.withMeta(meta), contentType, language, data, url, size, hash, title, creation); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeAsJsonValue(JsonGenerator generator) throws IOException { + generator.writeStartObject(); + serializeJsonBase(generator); + if (contentType != null) { + contentType.serializeAsJsonProperty(generator, FIELD_NAME_CONTENT_TYPE); + } + if (language != null) { + language.serializeAsJsonProperty(generator, FIELD_NAME_LANGUAGE); + } + if (data != null) { + data.serializeAsJsonProperty(generator, FIELD_NAME_DATA); + } + if (url != null) { + url.serializeAsJsonProperty(generator, FIELD_NAME_URL); + } + if (size != null) { + size.serializeAsJsonProperty(generator, FIELD_NAME_SIZE); + } + if (hash != null) { + hash.serializeAsJsonProperty(generator, FIELD_NAME_HASH); + } + if (title != null) { + title.serializeAsJsonProperty(generator, FIELD_NAME_TITLE); + } + if (creation != null) { + creation.serializeAsJsonProperty(generator, FIELD_NAME_CREATION); + } + generator.writeEndObject(); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (contentType != null) { + sink.putByte((byte) 2); + contentType.hashInto(sink); + } + if (language != null) { + sink.putByte((byte) 3); + language.hashInto(sink); + } + if (data != null) { + sink.putByte((byte) 4); + data.hashInto(sink); + } + if (url != null) { + sink.putByte((byte) 5); + url.hashInto(sink); + } + if (size != null) { + sink.putByte((byte) 6); + size.hashInto(sink); + } + if (hash != null) { + sink.putByte((byte) 7); + hash.hashInto(sink); + } + if (title != null) { + sink.putByte((byte) 8); + title.hashInto(sink); + } + if (creation != null) { + sink.putByte((byte) 9); + creation.hashInto(sink); + } + } + + @Override + public int memSize() { + return MEM_SIZE_OBJECT + extensionData.memSize() + Base.memSize(contentType) + Base.memSize(language) + + Base.memSize(data) + Base.memSize(url) + Base.memSize(size) + Base.memSize(hash) + Base.memSize(title) + + Base.memSize(creation); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof Attachment that && + extensionData.equals(that.extensionData) && + Objects.equals(contentType, that.contentType) && + Objects.equals(language, that.language) && + Objects.equals(data, that.data) && + Objects.equals(url, that.url) && + Objects.equals(size, that.size) && + Objects.equals(hash, that.hash) && + Objects.equals(title, that.title) && + Objects.equals(creation, that.creation); + } + + @Override + public int hashCode() { + int result = extensionData.hashCode(); + result = 31 * result + Objects.hashCode(contentType); + result = 31 * result + Objects.hashCode(language); + result = 31 * result + Objects.hashCode(data); + result = 31 * result + Objects.hashCode(url); + result = 31 * result + Objects.hashCode(size); + result = 31 * result + Objects.hashCode(hash); + result = 31 * result + Objects.hashCode(title); + result = 31 * result + Objects.hashCode(creation); + return result; + } + + @Override + public java.lang.String toString() { + return "Attachment{" + + extensionData + + ", contentType=" + contentType + + ", language=" + language + + ", data=" + data + + ", url=" + url + + ", size=" + size + + ", hash=" + hash + + ", title=" + title + + ", creation=" + creation + + '}'; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Base.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Base.java new file mode 100644 index 000000000..bb23f5016 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Base.java @@ -0,0 +1,332 @@ +package blaze.fhir.spec.type; + +import blaze.fhir.spec.type.system.Strings; +import clojure.lang.*; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.lang.String; +import java.util.*; +import java.util.stream.Stream; + +public interface Base extends IPersistentMap, IKeywordLookup, Map, IRecord, IObj, IHashEq { + + Keyword ID = RT.keyword(null, "id"); + Keyword EXTENSION = RT.keyword(null, "extension"); + Keyword VALUE = RT.keyword(null, "value"); + Keyword FHIR_TYPE_KEY = RT.keyword("fhir", "type"); + Keyword FHIR_TYPE_BUNDLE_ENTRY = RT.keyword("fhir.Bundle", "entry"); + + int MEM_SIZE_OBJECT_HEADER = 8; + int MEM_SIZE_PERSISTENT_VECTOR_OBJECT = 40; + int MEM_SIZE_PERSISTENT_SUB_VECTOR_OBJECT = 32; + int MEM_SIZE_PERSISTENT_ARRAY_MAP_OBJECT = 24; + int MEM_SIZE_PERSISTENT_HASH_MAP_OBJECT = 40; + int MEM_SIZE_PERSISTENT_HASH_MAP_ENTRY = 64; + int MEM_SIZE_ATOMIC_REFERENCE = 16; + int MEM_SIZE_REFERENCE = 4; + + IFn imapCons = RT.var("clojure.core", "imap-cons"); + + static ISeq appendElement(ISeq base, Keyword name, Object value) { + return value == null ? base : base.cons(MapEntry.create(name, value)); + } + + static ISeq appendElement(ISeq base, Keyword name, List value) { + return value.isEmpty() ? base : base.cons(MapEntry.create(name, value)); + } + + static List listFrom(IPersistentMap m, Keyword key) { + return Lists.nullToEmpty(m.valAt(key)); + } + + static boolean isInterned(Base x) { + return x == null || x.isInterned(); + } + + static boolean areAllInterned(List x) { + if (x == null) return true; + for (Base e : x) { + if (!Base.isInterned(e)) return false; + } + return true; + } + + static boolean isInternedExt(Object x) { + return x instanceof Base b && b.isInterned(); + } + + static boolean areAllInternedExt(List x) { + if (x == null) return true; + for (Object e : x) { + if (!Base.isInternedExt(e)) return false; + } + return true; + } + + @SuppressWarnings({"UnstableApiUsage"}) + static void hashInto(Object x, PrimitiveSink sink) { + if (x == null) return; + switch (x) { + case Base b: + b.hashInto(sink); + break; + case Map m: + hashIntoMap(m, sink); + break; + case List l: + sink.putByte((byte) 36); + for (Object i : l) { + hashInto(i, sink); + } + break; + default: + throw new IllegalStateException("Unexpected value of class: " + x.getClass()); + } + } + + @SuppressWarnings("UnstableApiUsage") + static void hashIntoList(List l, PrimitiveSink sink) { + sink.putByte((byte) 36); + for (Base b : l) { + b.hashInto(sink); + } + } + + @SuppressWarnings({"UnstableApiUsage", "unchecked"}) + static void hashIntoMap(Map m, PrimitiveSink sink) { + sink.putByte((byte) 37); + List> toSort = new ArrayList<>(m.size()); + for (Entry e : m.entrySet()) { + if (e.getKey() instanceof Keyword) toSort.add((Entry) e); + } + toSort.sort(Entry.comparingByKey()); + for (Entry e : toSort) { + sink.putInt(e.getKey().hasheq()); + switch (e.getValue()) { + case Keyword k: + sink.putInt(k.hasheq()); + break; + case Base b: + b.hashInto(sink); + break; + // for compatibility reasons, we use the hash signature of a FHIR.String instead of a System.String + case String s: + blaze.fhir.spec.type.String.hashIntoValue(sink, s); + break; + default: + hashInto(e.getValue(), sink); + } + } + } + + static Stream references(Object x) { + return switch (x) { + case Base b -> b.references(); + case Map m -> referencesMap(m); + case List v -> v.stream().flatMap(Base::references); + default -> Stream.empty(); + }; + } + + static Stream referencesMap(Map m) { + if (m.get(FHIR_TYPE_KEY) == FHIR_TYPE_BUNDLE_ENTRY) return Stream.empty(); + return m.values().stream() + .filter(v -> !(v instanceof Keyword)) + .filter(v -> !(v instanceof String)) + .flatMap(Base::references); + } + + static int memSize(Object x) { + if (x == null) return 0; + return switch (x) { + case Base b -> b.memSize(); + case PersistentArrayMap m -> m.isEmpty() ? 0 : memSizeArrayMap(m); + case PersistentHashMap m -> m.isEmpty() ? 0 : memSizeHashMap(m); + case IPersistentVector v -> v.count() == 0 ? 0 : memSizeVector(v); + case String s -> Strings.memSize(s); + default -> 0; + }; + } + + @SuppressWarnings("unchecked") + static int memSizeArrayMap(PersistentArrayMap m) { + return MEM_SIZE_PERSISTENT_ARRAY_MAP_OBJECT + 16 + + ((m.size() * MEM_SIZE_REFERENCE * 2 + 3) & ~7) + + m.values().stream().mapToInt(Base::memSize).sum(); + } + + @SuppressWarnings("unchecked") + static int memSizeHashMap(PersistentHashMap m) { + return MEM_SIZE_PERSISTENT_HASH_MAP_OBJECT + MEM_SIZE_ATOMIC_REFERENCE + + m.size() * MEM_SIZE_PERSISTENT_HASH_MAP_ENTRY + + m.values().stream().mapToInt(Base::memSize).sum(); + } + + static int memSizeVector(IPersistentVector list) { + return switch (list) { + case PersistentVector v -> memSizeNormalVector(v); + case APersistentVector.SubVector v -> memSizeSubVector(v); + default -> 0; + }; + } + + @SuppressWarnings("unchecked") + static int memSizeNormalVector(PersistentVector list) { + // TODO: improve calculation for lists with more than 32 elements + return MEM_SIZE_PERSISTENT_VECTOR_OBJECT + (list.size() <= 32 + ? 16 + ((list.size() * MEM_SIZE_REFERENCE + 3) & ~7) + : list.size() * MEM_SIZE_REFERENCE * 3) + + list.stream().mapToInt(Base::memSize).sum(); + } + + static int memSizeSubVector(APersistentVector.SubVector list) { + return MEM_SIZE_PERSISTENT_SUB_VECTOR_OBJECT + memSizeVector(list.v); + } + + static int memSize(Base value) { + return value == null ? 0 : value.memSize(); + } + + default ILookupThunk getLookupThunk(Keyword key) { + return null; + } + + boolean isInterned(); + + void serializeJsonPrimitiveExtension(JsonGenerator generator) throws IOException; + + void serializeJsonField(JsonGenerator generator, FieldName fieldName) throws IOException; + + @SuppressWarnings("UnstableApiUsage") + void hashInto(PrimitiveSink sink); + + default Stream references() { + return Stream.empty(); + } + + int memSize(); + + @Override + default int hasheq() { + return APersistentMap.mapHasheq(this); + } + + @Override + default boolean containsKey(Object key) { + return valAt(key) != null; + } + + @Override + default Object valAt(Object key) { + return valAt(key, null); + } + + @Override + default IMapEntry entryAt(Object key) { + var val = valAt(key); + return val == null ? null : MapEntry.create(key, val); + } + + @Override + default int count() { + var seq = seq(); + return seq == null ? 0 : seq.count(); + } + + @Override + default IPersistentCollection cons(Object o) { + return (IPersistentCollection) imapCons.invoke(this, o); + } + + @Override + default IPersistentMap assocEx(Object key, Object val) { + throw new UnsupportedOperationException("AssocEx isn't supported in FHIR types."); + } + + @Override + default IPersistentMap without(Object key) { + return assoc(key, null); + } + + @Override + default boolean equiv(Object o) { + return equals(o); + } + + @Override + default int size() { + return count(); + } + + @Override + default boolean isEmpty() { + return size() == 0; + } + + @Override + default boolean containsValue(Object value) { + return false; + } + + @Override + default Object get(Object key) { + return valAt(key); + } + + @Override + default Object put(Object key, Object value) { + throw new UnsupportedOperationException("Put isn't supported in FHIR types."); + } + + @Override + default Object remove(Object key) { + throw new UnsupportedOperationException("Remove isn't supported in FHIR types."); + } + + @Override + default void putAll(Map m) { + throw new UnsupportedOperationException("PutAll isn't supported in FHIR types."); + } + + @Override + default void clear() { + throw new UnsupportedOperationException("Clear isn't supported in FHIR types."); + } + + @Override + @SuppressWarnings("unchecked") + default Set keySet() { + return PersistentHashSet.create(RT.keys(this)); + } + + @Override + @SuppressWarnings("unchecked") + default Set> entrySet() { + return new AbstractSet<>() { + + public Iterator> iterator() { + return Base.this.iterator(); + } + + public int size() { + return count(); + } + + public int hashCode() { + return Base.this.hashCode(); + } + + public boolean contains(Object o) { + return o instanceof Entry e && Objects.equals(valAt(e.getKey()), e.getValue()); + } + }; + } + + @Override + @SuppressWarnings("unchecked") + default Collection values() { + return (Collection) RT.vals(this); + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Base64Binary.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Base64Binary.java new file mode 100644 index 000000000..46afe1e1e --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Base64Binary.java @@ -0,0 +1,122 @@ +package blaze.fhir.spec.type; + +import blaze.Interner; +import blaze.Interners; +import blaze.fhir.spec.type.system.Strings; +import clojure.lang.IObj; +import clojure.lang.IPersistentMap; +import clojure.lang.Keyword; +import clojure.lang.RT; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.lang.String; +import java.util.Objects; + +@SuppressWarnings("DuplicatedCode") +public final class Base64Binary extends PrimitiveElement implements IObj { + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "base64Binary"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueBase64Binary"); + + private static final byte HASH_MARKER = 8; + + private static final Interner INTERNER = Interners.weakInterner(k -> new Base64Binary(k, null)); + private static final Base64Binary EMPTY = new Base64Binary(ExtensionData.EMPTY, null); + + private final String value; + + private Base64Binary(ExtensionData extensionData, String value) { + super(extensionData); + this.value = value; + } + + private static Base64Binary maybeIntern(ExtensionData extensionData, String value) { + return extensionData.isInterned() && value == null ? INTERNER.intern(extensionData) : new Base64Binary(extensionData, value); + } + + public static Base64Binary create(String value) { + return value == null ? EMPTY : new Base64Binary(ExtensionData.EMPTY, value); + } + + public static Base64Binary create(IPersistentMap m) { + return maybeIntern(ExtensionData.fromMap(m), (String) m.valAt(VALUE)); + } + + public String value() { + return value; + } + + @Override + public Object valAt(Object key, Object notFound) { + return key == FHIR_TYPE_KEY ? FHIR_TYPE : super.valAt(key, notFound); + } + + @Override + public Base64Binary empty() { + return EMPTY; + } + + @Override + public Base64Binary assoc(Object key, Object val) { + if (key == VALUE) return maybeIntern(extensionData, (String) val); + if (key == EXTENSION) return maybeIntern(extensionData.withExtension(val), value); + if (key == ID) return maybeIntern(extensionData.withId(val), value); + return this; + } + + @Override + public Base64Binary withMeta(IPersistentMap meta) { + return maybeIntern(extensionData.withMeta(meta), value); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeJsonPrimitiveValue(JsonGenerator generator) throws IOException { + if (hasValue()) { + generator.writeString(value); + } else { + generator.writeNull(); + } + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (value != null) { + sink.putByte((byte) 2); + Strings.hashInto(value, sink); + } + } + + @Override + public int memSize() { + return super.memSize() + Strings.memSize(value); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof Base64Binary that && + extensionData.equals(that.extensionData) && + Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return 31 * extensionData.hashCode() + Objects.hashCode(value); + } + + @Override + public String toString() { + return "Base64Binary{" + extensionData + ", value=" + (value == null ? null : '\'' + value + '\'') + '}'; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/BaseIterator.java b/modules/fhir-structure/java/blaze/fhir/spec/type/BaseIterator.java new file mode 100644 index 000000000..d1ab60d63 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/BaseIterator.java @@ -0,0 +1,49 @@ +package blaze.fhir.spec.type; + +import clojure.lang.ILookup; +import clojure.lang.Keyword; +import clojure.lang.MapEntry; + +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; + +import static java.util.Objects.requireNonNull; + +public class BaseIterator implements Iterator> { + + private final ILookup lookup; + private final Keyword[] fields; + private int i = 0; + + public BaseIterator(ILookup lookup, Keyword[] fields) { + this.lookup = requireNonNull(lookup); + this.fields = requireNonNull(fields); + } + + private static boolean isNullOrEmpty(Object x) { + return x == null || x instanceof List l && l.isEmpty(); + } + + public boolean hasNext() { + if (i < fields.length) { + if (!isNullOrEmpty(lookup.valAt(fields[i]))) { + return true; + } + i++; + return hasNext(); + } + return false; + } + + @SuppressWarnings("unchecked") + public Map.Entry next() { + if (hasNext()) { + Object k = fields[i]; + i++; + return MapEntry.create(k, lookup.valAt(k)); + } + throw new NoSuchElementException(); + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Boolean.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Boolean.java new file mode 100644 index 000000000..52ba53330 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Boolean.java @@ -0,0 +1,140 @@ +package blaze.fhir.spec.type; + +import blaze.Interner; +import blaze.Interners; +import blaze.fhir.spec.type.system.Booleans; +import clojure.lang.IPersistentMap; +import clojure.lang.Keyword; +import clojure.lang.RT; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.lang.String; +import java.util.Objects; + +import static java.util.Objects.requireNonNull; + +@SuppressWarnings("DuplicatedCode") +public final class Boolean extends PrimitiveElement { + + public static final Boolean TRUE = new Boolean(ExtensionData.EMPTY, java.lang.Boolean.TRUE); + public static final Boolean FALSE = new Boolean(ExtensionData.EMPTY, java.lang.Boolean.FALSE); + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "boolean"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueBoolean"); + + private static final byte HASH_MARKER = 0; + + private static final Interner INTERNER = Interners.weakInterner(k -> new Boolean(k.extensionData, k.value)); + private static final Boolean EMPTY = new Boolean(ExtensionData.EMPTY, null); + + private final java.lang.Boolean value; + + private Boolean(ExtensionData extensionData, java.lang.Boolean value) { + super(extensionData); + this.value = value; + } + + private static Boolean intern(ExtensionData extensionData, java.lang.Boolean value) { + return INTERNER.intern(new InternerKey(extensionData, value)); + } + + private static Boolean maybeIntern(ExtensionData extensionData, java.lang.Boolean value) { + return extensionData.isInterned() ? intern(extensionData, value) : new Boolean(extensionData, value); + } + + public static Boolean create(IPersistentMap m) { + return maybeIntern(ExtensionData.fromMap(m), (java.lang.Boolean) m.valAt(VALUE)); + } + + @Override + public boolean isInterned() { + return extensionData.isInterned(); + } + + public java.lang.Boolean value() { + return value; + } + + @Override + public Object valAt(Object key, Object notFound) { + return key == FHIR_TYPE_KEY ? FHIR_TYPE : super.valAt(key, notFound); + } + + @Override + public Boolean empty() { + return EMPTY; + } + + @Override + public Boolean assoc(Object key, Object val) { + if (key == VALUE) return maybeIntern(extensionData, (java.lang.Boolean) val); + if (key == EXTENSION) return maybeIntern(extensionData.withExtension(val), value); + if (key == ID) return maybeIntern(extensionData.withId(val), value); + return this; + } + + @Override + public Boolean withMeta(IPersistentMap meta) { + return maybeIntern(extensionData.withMeta(meta), value); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeJsonPrimitiveValue(JsonGenerator generator) throws IOException { + if (hasValue()) { + generator.writeBoolean(value); + } else { + generator.writeNull(); + } + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (value != null) { + sink.putByte((byte) 2); + Booleans.hashInto(value, sink); + } + } + + @Override + public int memSize() { + return isInterned() ? 0 : MEM_SIZE_OBJECT + extensionData.memSize(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof Boolean that && + extensionData.equals(that.extensionData) && + Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return 31 * extensionData.hashCode() + Objects.hashCode(value); + } + + @Override + public String toString() { + return "Boolean{" + + extensionData + + ", value=" + value + + '}'; + } + + private record InternerKey(ExtensionData extensionData, java.lang.Boolean value) { + private InternerKey { + requireNonNull(extensionData); + } + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/BundleEntrySearch.java b/modules/fhir-structure/java/blaze/fhir/spec/type/BundleEntrySearch.java new file mode 100644 index 000000000..fdad22ac3 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/BundleEntrySearch.java @@ -0,0 +1,160 @@ +package blaze.fhir.spec.type; + +import clojure.lang.*; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.util.Iterator; +import java.util.Objects; + +import static blaze.fhir.spec.type.Base.appendElement; + +@SuppressWarnings("DuplicatedCode") +public final class BundleEntrySearch extends AbstractElement implements Complex { + + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - extension data reference + * 4 byte - mode reference + * 4 byte - score reference + * 4 byte - padding + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 16; + + private static final Keyword FHIR_TYPE = RT.keyword("fhir.Bundle.entry", "search"); + + private static final Keyword MODE = RT.keyword(null, "mode"); + private static final Keyword SCORE = RT.keyword(null, "score"); + + private static final Keyword[] FIELDS = {ID, EXTENSION, MODE, SCORE}; + + private static final FieldName FIELD_NAME_MODE = FieldName.of("mode"); + private static final FieldName FIELD_NAME_SCORE = FieldName.of("score"); + + private static final byte HASH_MARKER = 45; + + private static final BundleEntrySearch EMPTY = new BundleEntrySearch(ExtensionData.EMPTY, null, null); + + private final Code mode; + private final Decimal score; + + private BundleEntrySearch(ExtensionData extensionData, Code mode, Decimal score) { + super(extensionData); + this.mode = mode; + this.score = score; + } + + public static BundleEntrySearch create(IPersistentMap m) { + return new BundleEntrySearch(ExtensionData.fromMap(m), (Code) m.valAt(MODE), (Decimal) m.valAt(SCORE)); + } + + public Code mode() { + return mode; + } + + public Decimal score() { + return score; + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE; + if (key == MODE) return mode; + if (key == SCORE) return score; + return super.valAt(key, notFound); + } + + @Override + public ISeq seq() { + ISeq seq = PersistentList.EMPTY; + seq = appendElement(seq, SCORE, score); + seq = appendElement(seq, MODE, mode); + return extensionData.append(seq); + } + + @Override + public BundleEntrySearch empty() { + return EMPTY; + } + + @Override + public Iterator> iterator() { + return new BaseIterator(this, FIELDS); + } + + @Override + public BundleEntrySearch assoc(Object key, Object val) { + if (key == MODE) return new BundleEntrySearch(extensionData, (Code) val, score); + if (key == SCORE) return new BundleEntrySearch(extensionData, mode, (Decimal) val); + if (key == EXTENSION) return new BundleEntrySearch(extensionData.withExtension(val), mode, score); + if (key == ID) return new BundleEntrySearch(extensionData.withId(val), mode, score); + return this; + } + + @Override + public BundleEntrySearch withMeta(IPersistentMap meta) { + return new BundleEntrySearch(extensionData.withMeta(meta), mode, score); + } + + @Override + public void serializeAsJsonValue(JsonGenerator generator) throws IOException { + generator.writeStartObject(); + serializeJsonBase(generator); + if (mode != null) { + mode.serializeAsJsonProperty(generator, FIELD_NAME_MODE); + } + if (score != null) { + score.serializeAsJsonProperty(generator, FIELD_NAME_SCORE); + } + generator.writeEndObject(); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (mode != null) { + sink.putByte((byte) 2); + mode.hashInto(sink); + } + if (score != null) { + sink.putByte((byte) 3); + score.hashInto(sink); + } + } + + @Override + public int memSize() { + return MEM_SIZE_OBJECT + extensionData.memSize() + Base.memSize(mode) + Base.memSize(score); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof BundleEntrySearch that && + extensionData.equals(that.extensionData) && + Objects.equals(mode, that.mode) && + Objects.equals(score, that.score); + } + + @Override + public int hashCode() { + int result = extensionData.hashCode(); + result = 31 * result + Objects.hashCode(mode); + result = 31 * result + Objects.hashCode(score); + return result; + } + + @Override + public java.lang.String toString() { + return "BundleEntrySearch{" + + extensionData + + ", mode=" + mode + + ", score=" + score + + '}'; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Canonical.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Canonical.java new file mode 100644 index 000000000..145be43f7 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Canonical.java @@ -0,0 +1,147 @@ +package blaze.fhir.spec.type; + +import blaze.Interner; +import blaze.Interners; +import blaze.fhir.spec.type.system.Strings; +import clojure.lang.IPersistentMap; +import clojure.lang.Keyword; +import clojure.lang.RT; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.io.SerializedString; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.lang.String; +import java.util.Objects; + +import static java.util.Objects.requireNonNull; + +@SuppressWarnings("DuplicatedCode") +public final class Canonical extends PrimitiveElement { + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "canonical"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueCanonical"); + + private static final byte HASH_MARKER = 7; + + private static final Interner INTERNER = Interners.weakInterner(k -> create(k.extensionData, k.value)); + private static final Canonical EMPTY = new Canonical(ExtensionData.EMPTY, null); + + private final SerializedString value; + + private Canonical(ExtensionData extensionData, SerializedString value) { + super(extensionData); + this.value = value; + } + + private static Canonical create(ExtensionData extensionData, String value) { + return new Canonical(extensionData, value == null ? null : new SerializedString(value)); + } + + private static Canonical intern(ExtensionData extensionData, String value) { + return INTERNER.intern(new InternerKey(extensionData, value)); + } + + private static Canonical maybeIntern(ExtensionData extensionData, String value) { + return extensionData.isInterned() ? intern(extensionData, value) : create(extensionData, value); + } + + public static Canonical create(String value) { + return intern(ExtensionData.EMPTY, requireNonNull(value)); + } + + public static Canonical create(IPersistentMap m) { + return maybeIntern(ExtensionData.fromMap(m), (String) m.valAt(VALUE)); + } + + @Override + public boolean isInterned() { + return extensionData.isInterned(); + } + + @Override + public String value() { + return value == null ? null : value.getValue(); + } + + @Override + public Object valAt(Object key, Object notFound) { + return key == FHIR_TYPE_KEY ? FHIR_TYPE : super.valAt(key, notFound); + } + + @Override + public Canonical empty() { + return EMPTY; + } + + @Override + public Canonical assoc(Object key, Object val) { + if (key == VALUE) return maybeIntern(extensionData, (String) val); + if (key == EXTENSION) return maybeIntern(extensionData.withExtension(val), value()); + if (key == ID) return maybeIntern(extensionData.withId(val), value()); + return this; + } + + @Override + public Canonical withMeta(IPersistentMap meta) { + return maybeIntern(extensionData.withMeta(meta), value()); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeJsonPrimitiveValue(JsonGenerator generator) throws IOException { + if (hasValue()) { + generator.writeString(value); + } else { + generator.writeNull(); + } + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (hasValue()) { + sink.putByte((byte) 2); + Strings.hashInto(value.getValue(), sink); + } + } + + @Override + public int memSize() { + return isInterned() ? 0 : MEM_SIZE_OBJECT + extensionData.memSize() + Strings.memSize(value); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof Canonical that && + extensionData.equals(that.extensionData) && + Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return 31 * extensionData.hashCode() + Objects.hashCode(value); + } + + @Override + public String toString() { + return "Canonical{" + + extensionData + + ", value=" + (value == null ? null : '\'' + value() + '\'') + + '}'; + } + + private record InternerKey(ExtensionData extensionData, String value) { + private InternerKey { + requireNonNull(extensionData); + } + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Code.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Code.java new file mode 100644 index 000000000..15a389dbf --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Code.java @@ -0,0 +1,160 @@ +package blaze.fhir.spec.type; + +import blaze.Interner; +import blaze.Interners; +import blaze.fhir.spec.type.system.Strings; +import clojure.lang.ILookupThunk; +import clojure.lang.IPersistentMap; +import clojure.lang.Keyword; +import clojure.lang.RT; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.io.SerializedString; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.lang.String; +import java.util.Objects; + +import static java.util.Objects.requireNonNull; + +@SuppressWarnings("DuplicatedCode") +public final class Code extends PrimitiveElement { + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "code"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueCode"); + + private static final byte HASH_MARKER = 13; + + private static final Interner INTERNER = Interners.weakInterner(k -> create(k.extensionData, k.value)); + private static final Code EMPTY = new Code(ExtensionData.EMPTY, null); + + private static final ILookupThunk FHIR_TYPE_LOOKUP_THUNK = new ILookupThunk() { + @Override + public Object get(Object target) { + return target instanceof Code ? FHIR_TYPE : this; + } + }; + + private final SerializedString value; + + private Code(ExtensionData extensionData, SerializedString value) { + super(extensionData); + this.value = value; + } + + private static Code create(ExtensionData extensionData, String value) { + return new Code(extensionData, value == null ? null : new SerializedString(value)); + } + + private static Code intern(ExtensionData extensionData, String value) { + return INTERNER.intern(new InternerKey(extensionData, value)); + } + + private static Code maybeIntern(ExtensionData extensionData, String value) { + return extensionData.isInterned() ? intern(extensionData, value) : create(extensionData, value); + } + + public static Code create(String value) { + return intern(ExtensionData.EMPTY, requireNonNull(value)); + } + + public static Code create(IPersistentMap m) { + return maybeIntern(ExtensionData.fromMap(m), (String) m.valAt(VALUE)); + } + + @Override + public boolean isInterned() { + return extensionData.isInterned(); + } + + @Override + public String value() { + return value == null ? null : value.getValue(); + } + + @Override + public ILookupThunk getLookupThunk(Keyword key) { + return (key == FHIR_TYPE_KEY) ? FHIR_TYPE_LOOKUP_THUNK : super.getLookupThunk(key); + } + + @Override + public Object valAt(Object key, Object notFound) { + return key == FHIR_TYPE_KEY ? FHIR_TYPE : super.valAt(key, notFound); + } + + @Override + public Code empty() { + return EMPTY; + } + + @Override + public Code assoc(Object key, Object val) { + if (key == VALUE) return maybeIntern(extensionData, (String) val); + if (key == EXTENSION) return maybeIntern(extensionData.withExtension(val), value()); + if (key == ID) return maybeIntern(extensionData.withId(val), value()); + return this; + } + + @Override + public Code withMeta(IPersistentMap meta) { + return maybeIntern(extensionData.withMeta(meta), value()); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeJsonPrimitiveValue(JsonGenerator generator) throws IOException { + if (hasValue()) { + generator.writeString(value); + } else { + generator.writeNull(); + } + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (hasValue()) { + sink.putByte((byte) 2); + Strings.hashInto(value.getValue(), sink); + } + } + + @Override + public int memSize() { + return isInterned() ? 0 : MEM_SIZE_OBJECT + extensionData.memSize() + Strings.memSize(value); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof Code that && + extensionData.equals(that.extensionData) && + Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return 31 * extensionData.hashCode() + Objects.hashCode(value); + } + + @Override + public String toString() { + return "Code{" + + extensionData + + ", value=" + (value == null ? null : '\'' + value() + '\'') + + '}'; + } + + private record InternerKey(ExtensionData extensionData, String value) { + private InternerKey { + requireNonNull(extensionData); + } + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/CodeableConcept.java b/modules/fhir-structure/java/blaze/fhir/spec/type/CodeableConcept.java new file mode 100644 index 000000000..e60759ed2 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/CodeableConcept.java @@ -0,0 +1,200 @@ +package blaze.fhir.spec.type; + +import blaze.Interner; +import blaze.Interners; +import clojure.lang.*; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.io.SerializedString; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; + +import static blaze.fhir.spec.type.Base.appendElement; +import static blaze.fhir.spec.type.Complex.serializeJsonComplexList; +import static java.util.Objects.requireNonNull; + +@SuppressWarnings("DuplicatedCode") +public final class CodeableConcept extends AbstractElement implements Complex, ExtensionValue { + + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - extension data reference + * 4 byte - coding reference + * 4 byte - text reference + * 1 byte - interned boolean + * 3 byte - padding + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 16; + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "CodeableConcept"); + + private static final Keyword CODING = RT.keyword(null, "coding"); + private static final Keyword TEXT = RT.keyword(null, "text"); + + private static final Keyword[] FIELDS = {ID, EXTENSION, CODING, TEXT}; + + private static final SerializedString FIELD_NAME_CODING = new SerializedString("coding"); + private static final FieldName FIELD_NAME_TEXT = FieldName.of("text"); + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueCodeableConcept"); + + private static final byte HASH_MARKER = 39; + + private static final Interner INTERNER = Interners.weakInterner( + k -> new CodeableConcept(k.extensionData, k.coding, k.text, true) + ); + @SuppressWarnings("unchecked") + private static final CodeableConcept EMPTY = new CodeableConcept(ExtensionData.EMPTY, PersistentVector.EMPTY, null, true); + + private final List coding; + private final String text; + private final boolean interned; + + private CodeableConcept(ExtensionData extensionData, List coding, String text, boolean interned) { + super(extensionData); + this.coding = requireNonNull(coding); + this.text = text; + this.interned = interned; + } + + private static CodeableConcept maybeIntern(ExtensionData extensionData, List coding, String text) { + return extensionData.isInterned() && Base.areAllInterned(coding) && Base.isInterned(text) + ? INTERNER.intern(new InternerKey(extensionData, coding, text)) + : new CodeableConcept(extensionData, coding, text, false); + } + + public static CodeableConcept create(IPersistentMap m) { + return maybeIntern(ExtensionData.fromMap(m), Base.listFrom(m, CODING), (String) m.valAt(TEXT)); + } + + @Override + public boolean isInterned() { + return interned; + } + + public List coding() { + return coding; + } + + public String text() { + return text; + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE; + if (key == CODING) return coding; + if (key == TEXT) return text; + return super.valAt(key, notFound); + } + + @Override + public ISeq seq() { + ISeq seq = PersistentList.EMPTY; + seq = appendElement(seq, TEXT, text); + if (!coding.isEmpty()) { + seq = appendElement(seq, CODING, coding); + } + return extensionData.append(seq); + } + + @Override + public CodeableConcept empty() { + return EMPTY; + } + + @Override + public Iterator> iterator() { + return new BaseIterator(this, FIELDS); + } + + @Override + public CodeableConcept assoc(Object key, Object val) { + if (key == CODING) return maybeIntern(extensionData, Lists.nullToEmpty(val), text); + if (key == TEXT) return maybeIntern(extensionData, coding, (String) val); + if (key == EXTENSION) return maybeIntern(extensionData.withExtension(val), coding, text); + if (key == ID) return maybeIntern(extensionData.withId(val), coding, text); + return this; + } + + @Override + public CodeableConcept withMeta(IPersistentMap meta) { + return maybeIntern(extensionData.withMeta(meta), coding, text); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeAsJsonValue(JsonGenerator generator) throws IOException { + generator.writeStartObject(); + serializeJsonBase(generator); + if (!coding.isEmpty()) { + serializeJsonComplexList(coding, generator, FIELD_NAME_CODING); + } + if (text != null) { + text.serializeAsJsonProperty(generator, FIELD_NAME_TEXT); + } + generator.writeEndObject(); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (!coding.isEmpty()) { + sink.putByte((byte) 2); + Base.hashIntoList(coding, sink); + } + if (text != null) { + sink.putByte((byte) 3); + text.hashInto(sink); + } + } + + @Override + public int memSize() { + return isInterned() ? 0 : MEM_SIZE_OBJECT + extensionData.memSize() + Base.memSize(coding) + + Base.memSize(text); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof CodeableConcept that && + extensionData.equals(that.extensionData) && + Objects.equals(coding, that.coding) && + Objects.equals(text, that.text); + } + + @Override + public int hashCode() { + int result = extensionData.hashCode(); + result = 31 * result + Objects.hashCode(coding); + result = 31 * result + Objects.hashCode(text); + return result; + } + + @Override + public java.lang.String toString() { + return "CodeableConcept{" + + extensionData + + ", coding=" + coding + + ", text=" + text + + '}'; + } + + private record InternerKey(ExtensionData extensionData, List coding, String text) { + private InternerKey { + requireNonNull(extensionData); + requireNonNull(coding); + } + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Coding.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Coding.java new file mode 100644 index 000000000..216c94d9c --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Coding.java @@ -0,0 +1,265 @@ +package blaze.fhir.spec.type; + +import blaze.Interner; +import blaze.Interners; +import clojure.lang.*; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.util.Iterator; +import java.util.Objects; + +import static blaze.fhir.spec.type.Base.appendElement; +import static java.util.Objects.requireNonNull; + +@SuppressWarnings("DuplicatedCode") +public final class Coding extends AbstractElement implements Complex, ExtensionValue { + + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - extension data reference + * 4 byte - system reference + * 4 byte - version reference + * 4 byte - code reference + * 4 byte - display reference + * 4 byte - userSelected reference + * 1 byte - interned boolean + * 7 byte - padding + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 32; + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "Coding"); + private static final Keyword SYSTEM = RT.keyword(null, "system"); + private static final Keyword VERSION = RT.keyword(null, "version"); + private static final Keyword CODE = RT.keyword(null, "code"); + private static final Keyword DISPLAY = RT.keyword(null, "display"); + private static final Keyword USER_SELECTED = RT.keyword(null, "userSelected"); + + private static final Keyword[] FIELDS = {ID, EXTENSION, SYSTEM, VERSION, CODE, DISPLAY, USER_SELECTED}; + + private static final FieldName FIELD_NAME_SYSTEM = FieldName.of("system"); + private static final FieldName FIELD_NAME_VERSION = FieldName.of("version"); + private static final FieldName FIELD_NAME_CODE = FieldName.of("code"); + private static final FieldName FIELD_NAME_DISPLAY = FieldName.of("display"); + private static final FieldName FIELD_NAME_USER_SELECTED = FieldName.of("userSelected"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueCoding"); + + private static final byte HASH_MARKER = 38; + + private static final Interner INTERNER = Interners.weakInterner( + k -> new Coding(k.extensionData, k.system, k.version, k.code, k.display, k.userSelected, true) + ); + public static final Coding EMPTY = new Coding(ExtensionData.EMPTY, null, null, null, null, null, true); + + private final Uri system; + private final String version; + private final Code code; + private final String display; + private final Boolean userSelected; + private final boolean interned; + + private Coding(ExtensionData extensionData, Uri system, String version, Code code, String display, + Boolean userSelected, boolean interned) { + super(extensionData); + this.system = system; + this.version = version; + this.code = code; + this.display = display; + this.userSelected = userSelected; + this.interned = interned; + } + + private static Coding maybeIntern(ExtensionData extensionData, Uri system, String version, Code code, String display, + Boolean userSelected) { + return extensionData.isInterned() && Base.isInterned(system) && Base.isInterned(version) && + Base.isInterned(code) && Base.isInterned(display) && Base.isInterned(userSelected) + ? INTERNER.intern(new InternerKey(extensionData, system, version, code, display, userSelected)) + : new Coding(extensionData, system, version, code, display, userSelected, false); + } + + public static Coding create(IPersistentMap m) { + return maybeIntern(ExtensionData.fromMap(m), (Uri) m.valAt(SYSTEM), (String) m.valAt(VERSION), + (Code) m.valAt(CODE), (String) m.valAt(DISPLAY), (Boolean) m.valAt(USER_SELECTED)); + } + + @Override + public boolean isInterned() { + return interned; + } + + public Uri system() { + return system; + } + + public String version() { + return version; + } + + public Code code() { + return code; + } + + public String display() { + return display; + } + + public Boolean userSelected() { + return userSelected; + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE; + if (key == CODE) return code; + if (key == SYSTEM) return system; + if (key == VERSION) return version; + if (key == DISPLAY) return display; + if (key == USER_SELECTED) return userSelected; + return super.valAt(key, notFound); + } + + @Override + public ISeq seq() { + ISeq seq = PersistentList.EMPTY; + seq = appendElement(seq, USER_SELECTED, userSelected); + seq = appendElement(seq, DISPLAY, display); + seq = appendElement(seq, CODE, code); + seq = appendElement(seq, VERSION, version); + seq = appendElement(seq, SYSTEM, system); + return extensionData.append(seq); + } + + @Override + public Coding empty() { + return EMPTY; + } + + @Override + public Iterator> iterator() { + return new BaseIterator(this, FIELDS); + } + + @Override + public Coding assoc(Object key, Object val) { + if (key == CODE) return maybeIntern(extensionData, system, version, (Code) val, display, userSelected); + if (key == SYSTEM) return maybeIntern(extensionData, (Uri) val, version, code, display, userSelected); + if (key == VERSION) return maybeIntern(extensionData, system, (String) val, code, display, userSelected); + if (key == DISPLAY) return maybeIntern(extensionData, system, version, code, (String) val, userSelected); + if (key == USER_SELECTED) return maybeIntern(extensionData, system, version, code, display, (Boolean) val); + if (key == EXTENSION) + return maybeIntern(extensionData.withExtension(val), system, version, code, display, userSelected); + if (key == ID) return maybeIntern(extensionData.withId(val), system, version, code, display, userSelected); + return this; + } + + @Override + public Coding withMeta(IPersistentMap meta) { + return maybeIntern(extensionData.withMeta(meta), system, version, code, display, userSelected); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeAsJsonValue(JsonGenerator generator) throws IOException { + generator.writeStartObject(); + serializeJsonBase(generator); + if (system != null) { + system.serializeAsJsonProperty(generator, FIELD_NAME_SYSTEM); + } + if (version != null) { + version.serializeAsJsonProperty(generator, FIELD_NAME_VERSION); + } + if (code != null) { + code.serializeAsJsonProperty(generator, FIELD_NAME_CODE); + } + if (display != null) { + display.serializeAsJsonProperty(generator, FIELD_NAME_DISPLAY); + } + if (userSelected != null) { + userSelected.serializeAsJsonProperty(generator, FIELD_NAME_USER_SELECTED); + } + generator.writeEndObject(); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (system != null) { + sink.putByte((byte) 2); + system.hashInto(sink); + } + if (version != null) { + sink.putByte((byte) 3); + version.hashInto(sink); + } + if (code != null) { + sink.putByte((byte) 4); + code.hashInto(sink); + } + if (display != null) { + sink.putByte((byte) 5); + display.hashInto(sink); + } + if (userSelected != null) { + sink.putByte((byte) 6); + userSelected.hashInto(sink); + } + } + + @Override + public int memSize() { + return isInterned() ? 0 : MEM_SIZE_OBJECT + extensionData.memSize() + Base.memSize(system) + + Base.memSize(version) + Base.memSize(code) + Base.memSize(display) + Base.memSize(userSelected); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof Coding that && + extensionData.equals(that.extensionData) && + Objects.equals(system, that.system) && + Objects.equals(version, that.version) && + Objects.equals(code, that.code) && + Objects.equals(display, that.display) && + Objects.equals(userSelected, that.userSelected); + } + + @Override + public int hashCode() { + int result = extensionData.hashCode(); + result = 31 * result + Objects.hashCode(system); + result = 31 * result + Objects.hashCode(version); + result = 31 * result + Objects.hashCode(code); + result = 31 * result + Objects.hashCode(display); + result = 31 * result + Objects.hashCode(userSelected); + return result; + } + + @Override + public java.lang.String toString() { + return "Coding{" + + extensionData + + ", system=" + system + + ", version=" + version + + ", code=" + code + + ", display=" + display + + ", userSelected=" + userSelected + + '}'; + } + + private record InternerKey(ExtensionData extensionData, Uri system, String version, Code code, String display, + Boolean userSelected) { + private InternerKey { + requireNonNull(extensionData); + } + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Complex.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Complex.java new file mode 100644 index 000000000..d32a94e4e --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Complex.java @@ -0,0 +1,32 @@ +package blaze.fhir.spec.type; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.SerializableString; + +import java.io.IOException; +import java.util.List; + +public interface Complex extends Base { + + static void serializeJsonComplexList(List values, JsonGenerator generator, SerializableString fieldName) throws IOException { + generator.writeFieldName(fieldName); + generator.writeStartArray(); + for (Complex element : values) { + element.serializeAsJsonValue(generator); + } + generator.writeEndArray(); + } + + @Override + default boolean isInterned() { + return false; + } + + void serializeAsJsonValue(JsonGenerator generator) throws IOException; + + @Override + default void serializeJsonField(JsonGenerator generator, FieldName fieldName) throws IOException { + generator.writeFieldName(fieldName.normal()); + serializeAsJsonValue(generator); + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/ContactDetail.java b/modules/fhir-structure/java/blaze/fhir/spec/type/ContactDetail.java new file mode 100644 index 000000000..294069029 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/ContactDetail.java @@ -0,0 +1,172 @@ +package blaze.fhir.spec.type; + +import clojure.lang.*; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.io.SerializedString; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; + +import static blaze.fhir.spec.type.Base.appendElement; +import static blaze.fhir.spec.type.Complex.serializeJsonComplexList; +import static java.util.Objects.requireNonNull; + +@SuppressWarnings("DuplicatedCode") +public final class ContactDetail extends AbstractElement implements Complex, ExtensionValue { + + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - extension data reference + * 4 byte - name reference + * 4 byte - telecom reference + * 4 byte - padding + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 16; + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "ContactDetail"); + + private static final Keyword NAME = RT.keyword(null, "name"); + private static final Keyword TELECOM = RT.keyword(null, "telecom"); + + private static final Keyword[] FIELDS = {ID, EXTENSION, NAME, TELECOM}; + + private static final FieldName FIELD_NAME_NAME = FieldName.of("name"); + private static final SerializedString FIELD_NAME_TELECOM = new SerializedString("telecom"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueContactDetail"); + + private static final byte HASH_MARKER = 52; + + @SuppressWarnings("unchecked") + private static final ContactDetail EMPTY = new ContactDetail(ExtensionData.EMPTY, null, PersistentVector.EMPTY); + + private final String name; + private final List telecom; + + private ContactDetail(ExtensionData extensionData, String name, List telecom) { + super(extensionData); + this.name = name; + this.telecom = requireNonNull(telecom); + } + + public static ContactDetail create(IPersistentMap m) { + return new ContactDetail(ExtensionData.fromMap(m), (String) m.valAt(NAME), Base.listFrom(m, TELECOM)); + } + + public String name() { + return name; + } + + public List telecom() { + return telecom; + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE; + if (key == NAME) return name; + if (key == TELECOM) return telecom; + return super.valAt(key, notFound); + } + + @Override + public ISeq seq() { + ISeq seq = PersistentList.EMPTY; + seq = appendElement(seq, TELECOM, telecom); + seq = appendElement(seq, NAME, name); + return extensionData.append(seq); + } + + @Override + public ContactDetail empty() { + return EMPTY; + } + + @Override + public Iterator> iterator() { + return new BaseIterator(this, FIELDS); + } + + @Override + public ContactDetail assoc(Object key, Object val) { + if (key == NAME) return new ContactDetail(extensionData, (String) val, telecom); + if (key == TELECOM) return new ContactDetail(extensionData, name, Lists.nullToEmpty(val)); + if (key == EXTENSION) return new ContactDetail(extensionData.withExtension(val), name, telecom); + if (key == ID) return new ContactDetail(extensionData.withId(val), name, telecom); + return this; + } + + @Override + public ContactDetail withMeta(IPersistentMap meta) { + return new ContactDetail(extensionData.withMeta(meta), name, telecom); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeAsJsonValue(JsonGenerator generator) throws IOException { + generator.writeStartObject(); + serializeJsonBase(generator); + if (name != null) { + name.serializeAsJsonProperty(generator, FIELD_NAME_NAME); + } + if (!telecom.isEmpty()) { + serializeJsonComplexList(telecom, generator, FIELD_NAME_TELECOM); + } + generator.writeEndObject(); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (name != null) { + sink.putByte((byte) 2); + name.hashInto(sink); + } + if (!telecom.isEmpty()) { + sink.putByte((byte) 3); + Base.hashIntoList(telecom, sink); + } + } + + @Override + public int memSize() { + return MEM_SIZE_OBJECT + extensionData.memSize() + Base.memSize(name) + Base.memSize(telecom); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof ContactDetail that && + extensionData.equals(that.extensionData) && + Objects.equals(name, that.name) && + Objects.equals(telecom, that.telecom); + } + + @Override + public int hashCode() { + int result = extensionData.hashCode(); + result = 31 * result + Objects.hashCode(name); + result = 31 * result + Objects.hashCode(telecom); + return result; + } + + @Override + public java.lang.String toString() { + return "ContactDetail{" + + extensionData + + ", name=" + name + + ", telecom=" + telecom + + '}'; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/ContactPoint.java b/modules/fhir-structure/java/blaze/fhir/spec/type/ContactPoint.java new file mode 100644 index 000000000..4a462265d --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/ContactPoint.java @@ -0,0 +1,236 @@ +package blaze.fhir.spec.type; + +import clojure.lang.*; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.util.Iterator; +import java.util.Objects; + +import static blaze.fhir.spec.type.Base.appendElement; + +@SuppressWarnings("DuplicatedCode") +public final class ContactPoint extends AbstractElement implements Complex, ExtensionValue { + + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - extension data reference + * 4 byte - system reference + * 4 byte - value reference + * 4 byte - use reference + * 4 byte - rank reference + * 4 byte - period reference + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 24; + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "ContactPoint"); + + private static final Keyword SYSTEM = RT.keyword(null, "system"); + private static final Keyword VALUE = RT.keyword(null, "value"); + private static final Keyword USE = RT.keyword(null, "use"); + private static final Keyword RANK = RT.keyword(null, "rank"); + private static final Keyword PERIOD = RT.keyword(null, "period"); + + private static final Keyword[] FIELDS = {ID, EXTENSION, SYSTEM, VALUE, USE, RANK, PERIOD}; + + private static final FieldName FIELD_NAME_SYSTEM = FieldName.of("system"); + private static final FieldName FIELD_NAME_VALUE = FieldName.of("value"); + private static final FieldName FIELD_NAME_USE = FieldName.of("use"); + private static final FieldName FIELD_NAME_RANK = FieldName.of("rank"); + private static final FieldName FIELD_NAME_PERIOD = FieldName.of("period"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueContactPoint"); + + private static final byte HASH_MARKER = 53; + + private static final ContactPoint EMPTY = new ContactPoint(ExtensionData.EMPTY, null, null, null, null, null); + + private final Code system; + private final String value; + private final Code use; + private final PositiveInt rank; + private final Period period; + + private ContactPoint(ExtensionData extensionData, Code system, String value, Code use, PositiveInt rank, + Period period) { + super(extensionData); + this.system = system; + this.value = value; + this.use = use; + this.rank = rank; + this.period = period; + } + + public static ContactPoint create(IPersistentMap m) { + return new ContactPoint(ExtensionData.fromMap(m), (Code) m.valAt(SYSTEM), (String) m.valAt(VALUE), + (Code) m.valAt(USE), (PositiveInt) m.valAt(RANK), (Period) m.valAt(PERIOD)); + } + + public Code system() { + return system; + } + + public String value() { + return value; + } + + public Code use() { + return use; + } + + public PositiveInt rank() { + return rank; + } + + public Period period() { + return period; + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE; + if (key == SYSTEM) return system; + if (key == VALUE) return value; + if (key == USE) return use; + if (key == RANK) return rank; + if (key == PERIOD) return period; + return super.valAt(key, notFound); + } + + @Override + public ISeq seq() { + ISeq seq = PersistentList.EMPTY; + seq = appendElement(seq, PERIOD, period); + seq = appendElement(seq, RANK, rank); + seq = appendElement(seq, USE, use); + seq = appendElement(seq, VALUE, value); + seq = appendElement(seq, SYSTEM, system); + return extensionData.append(seq); + } + + @Override + public ContactPoint empty() { + return EMPTY; + } + + @Override + public Iterator> iterator() { + return new BaseIterator(this, FIELDS); + } + + @Override + public ContactPoint assoc(Object key, Object val) { + if (key == SYSTEM) return new ContactPoint(extensionData, (Code) val, value, use, rank, period); + if (key == VALUE) return new ContactPoint(extensionData, system, (String) val, use, rank, period); + if (key == USE) return new ContactPoint(extensionData, system, value, (Code) val, rank, period); + if (key == RANK) return new ContactPoint(extensionData, system, value, use, (PositiveInt) val, period); + if (key == PERIOD) return new ContactPoint(extensionData, system, value, use, rank, (Period) val); + if (key == EXTENSION) + return new ContactPoint(extensionData.withExtension(val), system, value, use, rank, period); + if (key == ID) return new ContactPoint(extensionData.withId(val), system, value, use, rank, period); + return this; + } + + @Override + public ContactPoint withMeta(IPersistentMap meta) { + return new ContactPoint(extensionData.withMeta(meta), system, value, use, rank, period); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeAsJsonValue(JsonGenerator generator) throws IOException { + generator.writeStartObject(); + serializeJsonBase(generator); + if (system != null) { + system.serializeAsJsonProperty(generator, FIELD_NAME_SYSTEM); + } + if (value != null) { + value.serializeAsJsonProperty(generator, FIELD_NAME_VALUE); + } + if (use != null) { + use.serializeAsJsonProperty(generator, FIELD_NAME_USE); + } + if (rank != null) { + rank.serializeAsJsonProperty(generator, FIELD_NAME_RANK); + } + if (period != null) { + period.serializeJsonField(generator, FIELD_NAME_PERIOD); + } + generator.writeEndObject(); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (system != null) { + sink.putByte((byte) 2); + system.hashInto(sink); + } + if (value != null) { + sink.putByte((byte) 3); + value.hashInto(sink); + } + if (use != null) { + sink.putByte((byte) 4); + use.hashInto(sink); + } + if (rank != null) { + sink.putByte((byte) 5); + rank.hashInto(sink); + } + if (period != null) { + sink.putByte((byte) 6); + period.hashInto(sink); + } + } + + @Override + public int memSize() { + return MEM_SIZE_OBJECT + extensionData.memSize() + Base.memSize(system) + Base.memSize(value) + + Base.memSize(use) + Base.memSize(rank) + Base.memSize(period); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof ContactPoint that && + extensionData.equals(that.extensionData) && + Objects.equals(system, that.system) && + Objects.equals(value, that.value) && + Objects.equals(use, that.use) && + Objects.equals(rank, that.rank) && + Objects.equals(period, that.period); + } + + @Override + public int hashCode() { + int result = extensionData.hashCode(); + result = 31 * result + Objects.hashCode(system); + result = 31 * result + Objects.hashCode(value); + result = 31 * result + Objects.hashCode(use); + result = 31 * result + Objects.hashCode(rank); + result = 31 * result + Objects.hashCode(period); + return result; + } + + @Override + public java.lang.String toString() { + return "ContactPoint{" + + extensionData + + ", system=" + system + + ", value=" + value + + ", use=" + use + + ", rank=" + rank + + ", period=" + period + + '}'; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Contributor.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Contributor.java new file mode 100644 index 000000000..f45d50337 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Contributor.java @@ -0,0 +1,192 @@ +package blaze.fhir.spec.type; + +import clojure.lang.*; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.io.SerializedString; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; + +import static blaze.fhir.spec.type.Base.appendElement; +import static blaze.fhir.spec.type.Complex.serializeJsonComplexList; + +@SuppressWarnings("DuplicatedCode") +public final class Contributor extends AbstractElement implements Complex, ExtensionValue { + + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - extension data reference + * 4 byte - type reference + * 4 byte - name reference + * 4 byte - contact reference + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 16; + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "Contributor"); + + private static final Keyword TYPE = RT.keyword(null, "type"); + private static final Keyword NAME = RT.keyword(null, "name"); + private static final Keyword CONTACT = RT.keyword(null, "contact"); + + private static final Keyword[] FIELDS = {ID, EXTENSION, TYPE, NAME, CONTACT}; + + private static final FieldName FIELD_NAME_TYPE = FieldName.of("type"); + private static final FieldName FIELD_NAME_NAME = FieldName.of("name"); + private static final SerializedString FIELD_NAME_CONTACT = new SerializedString("contact"); + + private static final byte HASH_MARKER = 60; + + private static final Contributor EMPTY = new Contributor(ExtensionData.EMPTY, null, null, null); + + private final Code type; + private final String name; + private final List contact; + + private Contributor(ExtensionData extensionData, Code type, String name, List contact) { + super(extensionData); + this.type = type; + this.name = name; + this.contact = contact; + } + + public static Contributor create(IPersistentMap m) { + return new Contributor(ExtensionData.fromMap(m), (Code) m.valAt(TYPE), (String) m.valAt(NAME), + Base.listFrom(m, CONTACT)); + } + + public Code type() { + return type; + } + + public String name() { + return name; + } + + public List contact() { + return contact; + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE; + if (key == TYPE) return type; + if (key == NAME) return name; + if (key == CONTACT) return contact; + return super.valAt(key, notFound); + } + + @Override + public ISeq seq() { + ISeq seq = PersistentList.EMPTY; + seq = appendElement(seq, CONTACT, contact); + seq = appendElement(seq, NAME, name); + seq = appendElement(seq, TYPE, type); + return extensionData.append(seq); + } + + @Override + public Contributor empty() { + return EMPTY; + } + + @Override + public Iterator> iterator() { + return new BaseIterator(this, FIELDS); + } + + @Override + public Contributor assoc(Object key, Object val) { + if (key == TYPE) return new Contributor(extensionData, (Code) val, name, contact); + if (key == NAME) return new Contributor(extensionData, type, (String) val, contact); + if (key == CONTACT) return new Contributor(extensionData, type, name, Lists.nullToEmpty(val)); + if (key == EXTENSION) + return new Contributor(extensionData.withExtension(val), type, name, contact); + if (key == ID) return new Contributor(extensionData.withId(val), type, name, contact); + return this; + } + + @Override + public Contributor withMeta(IPersistentMap meta) { + return new Contributor(extensionData.withMeta(meta), type, name, contact); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FieldName.of("valueContributor"); + } + + @Override + public void serializeAsJsonValue(JsonGenerator generator) throws IOException { + generator.writeStartObject(); + serializeJsonBase(generator); + if (type != null) { + type.serializeAsJsonProperty(generator, FIELD_NAME_TYPE); + } + if (name != null) { + name.serializeAsJsonProperty(generator, FIELD_NAME_NAME); + } + if (!contact.isEmpty()) { + serializeJsonComplexList(contact, generator, FIELD_NAME_CONTACT); + } + generator.writeEndObject(); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (type != null) { + sink.putByte((byte) 2); + type.hashInto(sink); + } + if (name != null) { + sink.putByte((byte) 3); + name.hashInto(sink); + } + if (contact != null) { + sink.putByte((byte) 4); + Base.hashIntoList(contact, sink); + } + } + + @Override + public int memSize() { + return MEM_SIZE_OBJECT + extensionData.memSize() + Base.memSize(type) + Base.memSize(name) + + Base.memSize(contact); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof Contributor that && + extensionData.equals(that.extensionData) && + Objects.equals(type, that.type) && + Objects.equals(name, that.name) && + Objects.equals(contact, that.contact); + } + + @Override + public int hashCode() { + int result = extensionData.hashCode(); + result = 31 * result + Objects.hashCode(type); + result = 31 * result + Objects.hashCode(name); + result = 31 * result + Objects.hashCode(contact); + return result; + } + + @Override + public java.lang.String toString() { + return "Contributor{" + + extensionData + + ", type=" + type + + ", name=" + name + + ", contact=" + contact + + '}'; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Count.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Count.java new file mode 100644 index 000000000..63f4e18ff --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Count.java @@ -0,0 +1,71 @@ +package blaze.fhir.spec.type; + +import blaze.Interner; +import blaze.Interners; +import clojure.lang.IPersistentMap; +import clojure.lang.Keyword; +import clojure.lang.RT; + +@SuppressWarnings("DuplicatedCode") +public final class Count extends AbstractQuantity { + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "Count"); + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueCount"); + + private static final Count EMPTY = new Count(ExtensionData.EMPTY, null, null, null, null, null); + + private static final Interner INTERNER = Interners.weakInterner( + k -> new Count(k.extensionData(), k.value(), k.comparator(), k.unit(), k.system(), k.code()) + ); + + private Count(ExtensionData extensionData, Decimal value, Code comparator, String unit, Uri system, Code code) { + super(extensionData, value, comparator, unit, system, code); + } + + private static Count maybeIntern(ExtensionData extensionData, Decimal value, Code comparator, String unit, + Uri system, Code code) { + return extensionData.isInterned() && Base.isInterned(value) && Base.isInterned(comparator) && + Base.isInterned(unit) && Base.isInterned(system) && Base.isInterned(code) + ? INTERNER.intern(new InternerKey(extensionData, value, comparator, unit, system, code)) + : new Count(extensionData, value, comparator, unit, system, code); + } + + public static Count create(IPersistentMap m) { + return maybeIntern(ExtensionData.fromMap(m), (Decimal) m.valAt(VALUE), (Code) m.valAt(COMPARATOR), + (String) m.valAt(UNIT), (Uri) m.valAt(SYSTEM), (Code) m.valAt(CODE)); + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE; + return super.valAt(key, notFound); + } + + @Override + public Count empty() { + return EMPTY; + } + + @Override + public Count assoc(Object key, Object val) { + if (key == VALUE) return maybeIntern(extensionData, (Decimal) val, comparator, unit, system, code); + if (key == COMPARATOR) return maybeIntern(extensionData, value, (Code) val, unit, system, code); + if (key == UNIT) return maybeIntern(extensionData, value, comparator, (String) val, system, code); + if (key == SYSTEM) return maybeIntern(extensionData, value, comparator, unit, (Uri) val, code); + if (key == CODE) return maybeIntern(extensionData, value, comparator, unit, system, (Code) val); + if (key == EXTENSION) + return maybeIntern(extensionData.withExtension(val), value, comparator, unit, system, code); + if (key == ID) return maybeIntern(extensionData.withId(val), value, comparator, unit, system, code); + return this; + } + + @Override + public Count withMeta(IPersistentMap meta) { + return maybeIntern(extensionData.withMeta(meta), value, comparator, unit, system, code); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/DataRequirement.java b/modules/fhir-structure/java/blaze/fhir/spec/type/DataRequirement.java new file mode 100644 index 000000000..2cbed0fb3 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/DataRequirement.java @@ -0,0 +1,921 @@ +package blaze.fhir.spec.type; + +import clojure.lang.*; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.io.SerializedString; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; + +import static blaze.fhir.spec.type.Base.appendElement; +import static blaze.fhir.spec.type.Complex.serializeJsonComplexList; +import static blaze.fhir.spec.type.Primitive.serializeJsonPrimitiveList; + +@SuppressWarnings("DuplicatedCode") +public final class DataRequirement extends AbstractElement implements Complex, ExtensionValue { + + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - extension data reference + * 4 byte - type reference + * 4 byte - profile reference + * 4 byte - subject reference + * 4 byte - mustSupport reference + * 4 byte - codeFilter reference + * 4 byte - dateFilter reference + * 4 byte - limit reference + * 4 byte - sort reference + * 4 byte - padding + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 40; + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "DataRequirement"); + + private static final Keyword TYPE = RT.keyword(null, "type"); + private static final Keyword PROFILE = RT.keyword(null, "profile"); + private static final Keyword SUBJECT_CODEABLE_CONCEPT = RT.keyword(null, "subjectCodeableConcept"); + private static final Keyword SUBJECT_REFERENCE = RT.keyword(null, "subjectReference"); + private static final Keyword MUST_SUPPORT = RT.keyword(null, "mustSupport"); + private static final Keyword CODE_FILTER = RT.keyword(null, "codeFilter"); + private static final Keyword DATE_FILTER = RT.keyword(null, "dateFilter"); + private static final Keyword LIMIT = RT.keyword(null, "limit"); + private static final Keyword SORT = RT.keyword(null, "sort"); + + private static final Keyword[] FIELDS = {ID, EXTENSION, TYPE, PROFILE, SUBJECT_CODEABLE_CONCEPT, SUBJECT_REFERENCE, MUST_SUPPORT, CODE_FILTER, DATE_FILTER, LIMIT, SORT}; + + private static final FieldName FIELD_NAME_TYPE = FieldName.of("type"); + private static final FieldName FIELD_NAME_PROFILE = FieldName.of("profile"); + private static final FieldName FIELD_NAME_SUBJECT_CODEABLE_CONCEPT = FieldName.of("subjectCodeableConcept"); + private static final FieldName FIELD_NAME_SUBJECT_REFERENCE = FieldName.of("subjectReference"); + private static final FieldName FIELD_NAME_MUST_SUPPORT = FieldName.of("mustSupport"); + private static final SerializedString FIELD_NAME_CODE_FILTER = new SerializedString("codeFilter"); + private static final SerializedString FIELD_NAME_DATE_FILTER = new SerializedString("dateFilter"); + private static final FieldName FIELD_NAME_LIMIT = FieldName.of("limit"); + private static final FieldName FIELD_NAME_SORT = FieldName.of("sort"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueDataRequirement"); + + private static final byte HASH_MARKER = 65; + + private static final DataRequirement EMPTY = new DataRequirement(ExtensionData.EMPTY, null, null, null, null, null, null, null, null); + + private static final ILookupThunk FHIR_TYPE_LOOKUP_THUNK = new ILookupThunk() { + @Override + public Object get(Object target) { + return target instanceof DataRequirement ? FHIR_TYPE : this; + } + }; + + private final Code type; + private final List profile; + private final ExtensionValue subject; + private final List mustSupport; + private final List codeFilter; + private final List dateFilter; + private final PositiveInt limit; + private final List sort; + + private DataRequirement(ExtensionData extensionData, Code type, List profile, ExtensionValue subject, List mustSupport, List codeFilter, List dateFilter, PositiveInt limit, List sort) { + super(extensionData); + this.type = type; + this.profile = profile; + this.subject = subject; + this.mustSupport = mustSupport; + this.codeFilter = codeFilter; + this.dateFilter = dateFilter; + this.limit = limit; + this.sort = sort; + } + + public static DataRequirement create(IPersistentMap m) { + Object subject = m.valAt(SUBJECT_CODEABLE_CONCEPT); + if (subject == null) { + subject = m.valAt(SUBJECT_REFERENCE); + } + return new DataRequirement(ExtensionData.fromMap(m), (Code) m.valAt(TYPE), + Base.listFrom(m, PROFILE), + (ExtensionValue) subject, + Base.listFrom(m, MUST_SUPPORT), + Base.listFrom(m, CODE_FILTER), + Base.listFrom(m, DATE_FILTER), + (PositiveInt) m.valAt(LIMIT), + Base.listFrom(m, SORT)); + } + + public Code type() { + return type; + } + + public List profile() { + return profile; + } + + public ExtensionValue subject() { + return subject; + } + + public List mustSupport() { + return mustSupport; + } + + public List codeFilter() { + return codeFilter; + } + + public List dateFilter() { + return dateFilter; + } + + public PositiveInt limit() { + return limit; + } + + public List sort() { + return sort; + } + + @Override + public ILookupThunk getLookupThunk(Keyword key) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE_LOOKUP_THUNK; + return super.getLookupThunk(key); + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE; + if (key == TYPE) return type; + if (key == PROFILE) return profile; + if (key == SUBJECT_CODEABLE_CONCEPT) return subject instanceof CodeableConcept ? subject : null; + if (key == SUBJECT_REFERENCE) return subject instanceof Reference ? subject : null; + if (key == MUST_SUPPORT) return mustSupport; + if (key == CODE_FILTER) return codeFilter; + if (key == DATE_FILTER) return dateFilter; + if (key == LIMIT) return limit; + if (key == SORT) return sort; + return super.valAt(key, notFound); + } + + @Override + public ISeq seq() { + ISeq seq = PersistentList.EMPTY; + seq = appendElement(seq, SORT, sort); + seq = appendElement(seq, LIMIT, limit); + seq = appendElement(seq, DATE_FILTER, dateFilter); + seq = appendElement(seq, CODE_FILTER, codeFilter); + seq = appendElement(seq, MUST_SUPPORT, mustSupport); + if (subject instanceof CodeableConcept) { + seq = appendElement(seq, SUBJECT_CODEABLE_CONCEPT, subject); + } else if (subject instanceof Reference) { + seq = appendElement(seq, SUBJECT_REFERENCE, subject); + } + seq = appendElement(seq, PROFILE, profile); + seq = appendElement(seq, TYPE, type); + return extensionData.append(seq); + } + + @Override + public DataRequirement empty() { + return EMPTY; + } + + @Override + public Iterator> iterator() { + return new BaseIterator(this, FIELDS); + } + + @SuppressWarnings("unchecked") + @Override + public DataRequirement assoc(Object key, Object val) { + if (key == TYPE) return new DataRequirement(extensionData, (Code) val, profile, subject, mustSupport, codeFilter, dateFilter, limit, sort); + if (key == PROFILE) return new DataRequirement(extensionData, type, (List) val, subject, mustSupport, codeFilter, dateFilter, limit, sort); + if (key == SUBJECT_CODEABLE_CONCEPT || key == SUBJECT_REFERENCE) return new DataRequirement(extensionData, type, profile, (ExtensionValue) val, mustSupport, codeFilter, dateFilter, limit, sort); + if (key == MUST_SUPPORT) return new DataRequirement(extensionData, type, profile, subject, (List) val, codeFilter, dateFilter, limit, sort); + if (key == CODE_FILTER) return new DataRequirement(extensionData, type, profile, subject, mustSupport, (List) val, dateFilter, limit, sort); + if (key == DATE_FILTER) return new DataRequirement(extensionData, type, profile, subject, mustSupport, codeFilter, (List) val, limit, sort); + if (key == LIMIT) return new DataRequirement(extensionData, type, profile, subject, mustSupport, codeFilter, dateFilter, (PositiveInt) val, sort); + if (key == SORT) return new DataRequirement(extensionData, type, profile, subject, mustSupport, codeFilter, dateFilter, limit, (List) val); + if (key == EXTENSION) return new DataRequirement(extensionData.withExtension(val), type, profile, subject, mustSupport, codeFilter, dateFilter, limit, sort); + if (key == ID) return new DataRequirement(extensionData.withId(val), type, profile, subject, mustSupport, codeFilter, dateFilter, limit, sort); + return this; + } + + @Override + public DataRequirement withMeta(IPersistentMap meta) { + return new DataRequirement(extensionData.withMeta(meta), type, profile, subject, mustSupport, codeFilter, dateFilter, limit, sort); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeAsJsonValue(JsonGenerator generator) throws IOException { + generator.writeStartObject(); + serializeJsonBase(generator); + if (type != null) { + type.serializeAsJsonProperty(generator, FIELD_NAME_TYPE); + } + if (profile != null && !profile.isEmpty()) { + serializeJsonPrimitiveList(profile, generator, FIELD_NAME_PROFILE); + } + if (subject != null) { + if (subject instanceof CodeableConcept c) { + c.serializeJsonField(generator, FIELD_NAME_SUBJECT_CODEABLE_CONCEPT); + } else if (subject instanceof Reference r) { + r.serializeJsonField(generator, FIELD_NAME_SUBJECT_REFERENCE); + } + } + if (mustSupport != null && !mustSupport.isEmpty()) { + serializeJsonPrimitiveList(mustSupport, generator, FIELD_NAME_MUST_SUPPORT); + } + if (codeFilter != null && !codeFilter.isEmpty()) { + serializeJsonComplexList(codeFilter, generator, FIELD_NAME_CODE_FILTER); + } + if (dateFilter != null && !dateFilter.isEmpty()) { + serializeJsonComplexList(dateFilter, generator, FIELD_NAME_DATE_FILTER); + } + if (limit != null) { + limit.serializeAsJsonProperty(generator, FIELD_NAME_LIMIT); + } + if (sort != null && !sort.isEmpty()) { + serializeJsonComplexList(sort, generator, FIELD_NAME_SORT.normal()); + } + generator.writeEndObject(); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (type != null) { + sink.putByte((byte) 2); + type.hashInto(sink); + } + if (profile != null) { + sink.putByte((byte) 3); + Base.hashIntoList(profile, sink); + } + if (subject != null) { + sink.putByte((byte) 4); + subject.hashInto(sink); + } + if (mustSupport != null) { + sink.putByte((byte) 5); + Base.hashIntoList(mustSupport, sink); + } + if (codeFilter != null) { + sink.putByte((byte) 6); + Base.hashIntoList(codeFilter, sink); + } + if (dateFilter != null) { + sink.putByte((byte) 7); + Base.hashIntoList(dateFilter, sink); + } + if (limit != null) { + sink.putByte((byte) 8); + limit.hashInto(sink); + } + if (sort != null) { + sink.putByte((byte) 9); + Base.hashIntoList(sort, sink); + } + } + + @Override + public int memSize() { + return MEM_SIZE_OBJECT + extensionData.memSize() + Base.memSize(type) + Base.memSize(profile) + + Base.memSize(subject) + Base.memSize(mustSupport) + Base.memSize(codeFilter) + + Base.memSize(dateFilter) + Base.memSize(limit) + Base.memSize(sort); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof DataRequirement that && + extensionData.equals(that.extensionData) && + Objects.equals(type, that.type) && + Objects.equals(profile, that.profile) && + Objects.equals(subject, that.subject) && + Objects.equals(mustSupport, that.mustSupport) && + Objects.equals(codeFilter, that.codeFilter) && + Objects.equals(dateFilter, that.dateFilter) && + Objects.equals(limit, that.limit) && + Objects.equals(sort, that.sort); + } + + @Override + public int hashCode() { + int result = extensionData.hashCode(); + result = 31 * result + Objects.hashCode(type); + result = 31 * result + Objects.hashCode(profile); + result = 31 * result + Objects.hashCode(subject); + result = 31 * result + Objects.hashCode(mustSupport); + result = 31 * result + Objects.hashCode(codeFilter); + result = 31 * result + Objects.hashCode(dateFilter); + result = 31 * result + Objects.hashCode(limit); + result = 31 * result + Objects.hashCode(sort); + return result; + } + + @Override + public java.lang.String toString() { + return "DataRequirement{" + + extensionData + + ", type=" + type + + ", profile=" + profile + + ", subject=" + subject + + ", mustSupport=" + mustSupport + + ", codeFilter=" + codeFilter + + ", dateFilter=" + dateFilter + + ", limit=" + limit + + ", sort=" + sort + + '}'; + } + + public static final class CodeFilter extends AbstractElement implements Complex { + + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - extension data reference + * 4 byte - path reference + * 4 byte - searchParam reference + * 4 byte - valueSet reference + * 4 byte - code reference + * 4 byte - padding + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 24; + + private static final Keyword FHIR_TYPE = RT.keyword("fhir.DataRequirement", "codeFilter"); + + private static final Keyword PATH = RT.keyword(null, "path"); + private static final Keyword SEARCH_PARAM = RT.keyword(null, "searchParam"); + private static final Keyword VALUE_SET = RT.keyword(null, "valueSet"); + private static final Keyword CODE = RT.keyword(null, "code"); + + private static final Keyword[] FIELDS = {ID, EXTENSION, PATH, SEARCH_PARAM, VALUE_SET, CODE}; + + private static final FieldName FIELD_NAME_PATH = FieldName.of("path"); + private static final FieldName FIELD_NAME_SEARCH_PARAM = FieldName.of("searchParam"); + private static final FieldName FIELD_NAME_VALUE_SET = FieldName.of("valueSet"); + private static final SerializedString FIELD_NAME_CODE = new SerializedString("code"); + + private static final byte HASH_MARKER = 62; + + private static final CodeFilter EMPTY = new CodeFilter(ExtensionData.EMPTY, null, null, null, null); + + private static final ILookupThunk FHIR_TYPE_LOOKUP_THUNK = new ILookupThunk() { + @Override + public Object get(Object target) { + return target instanceof CodeFilter ? FHIR_TYPE : this; + } + }; + + private final String path; + private final String searchParam; + private final Canonical valueSet; + private final List code; + + private CodeFilter(ExtensionData extensionData, String path, String searchParam, Canonical valueSet, List code) { + super(extensionData); + this.path = path; + this.searchParam = searchParam; + this.valueSet = valueSet; + this.code = code; + } + + public static CodeFilter create(IPersistentMap m) { + return new CodeFilter(ExtensionData.fromMap(m), (String) m.valAt(PATH), (String) m.valAt(SEARCH_PARAM), + (Canonical) m.valAt(VALUE_SET), Base.listFrom(m, CODE)); + } + + public String path() { + return path; + } + + public String searchParam() { + return searchParam; + } + + public Canonical valueSet() { + return valueSet; + } + + public List code() { + return code; + } + + @Override + public ILookupThunk getLookupThunk(Keyword key) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE_LOOKUP_THUNK; + return super.getLookupThunk(key); + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE; + if (key == PATH) return path; + if (key == SEARCH_PARAM) return searchParam; + if (key == VALUE_SET) return valueSet; + if (key == CODE) return code; + return super.valAt(key, notFound); + } + + @Override + public ISeq seq() { + ISeq seq = PersistentList.EMPTY; + seq = appendElement(seq, CODE, code); + seq = appendElement(seq, VALUE_SET, valueSet); + seq = appendElement(seq, SEARCH_PARAM, searchParam); + seq = appendElement(seq, PATH, path); + return extensionData.append(seq); + } + + @Override + public CodeFilter empty() { + return EMPTY; + } + + @Override + public Iterator> iterator() { + return new BaseIterator(this, FIELDS); + } + + @Override + public CodeFilter assoc(Object key, Object val) { + if (key == PATH) + return new CodeFilter(extensionData, (String) val, searchParam, valueSet, code); + if (key == SEARCH_PARAM) + return new CodeFilter(extensionData, path, (String) val, valueSet, code); + if (key == VALUE_SET) + return new CodeFilter(extensionData, path, searchParam, (Canonical) val, code); + if (key == CODE) + return new CodeFilter(extensionData, path, searchParam, valueSet, Lists.nullToEmpty(val)); + if (key == EXTENSION) + return new CodeFilter(extensionData.withExtension(val), path, searchParam, valueSet, code); + if (key == ID) + return new CodeFilter(extensionData.withId(val), path, searchParam, valueSet, code); + return this; + } + + @Override + public CodeFilter withMeta(IPersistentMap meta) { + return new CodeFilter(extensionData.withMeta(meta), path, searchParam, valueSet, code); + } + + @Override + public void serializeAsJsonValue(JsonGenerator generator) throws IOException { + generator.writeStartObject(); + serializeJsonBase(generator); + if (path != null) { + path.serializeAsJsonProperty(generator, FIELD_NAME_PATH); + } + if (searchParam != null) { + searchParam.serializeAsJsonProperty(generator, FIELD_NAME_SEARCH_PARAM); + } + if (valueSet != null) { + valueSet.serializeAsJsonProperty(generator, FIELD_NAME_VALUE_SET); + } + if (code != null && !code.isEmpty()) { + serializeJsonComplexList(code, generator, FIELD_NAME_CODE); + } + generator.writeEndObject(); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (path != null) { + sink.putByte((byte) 2); + path.hashInto(sink); + } + if (searchParam != null) { + sink.putByte((byte) 3); + searchParam.hashInto(sink); + } + if (valueSet != null) { + sink.putByte((byte) 4); + valueSet.hashInto(sink); + } + if (code != null) { + sink.putByte((byte) 5); + Base.hashIntoList(code, sink); + } + } + + @Override + public int memSize() { + return MEM_SIZE_OBJECT + extensionData.memSize() + Base.memSize(path) + Base.memSize(searchParam) + + Base.memSize(valueSet) + Base.memSize(code); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof CodeFilter that && + extensionData.equals(that.extensionData) && + Objects.equals(path, that.path) && + Objects.equals(searchParam, that.searchParam) && + Objects.equals(valueSet, that.valueSet) && + Objects.equals(code, that.code); + } + + @Override + public int hashCode() { + int result = extensionData.hashCode(); + result = 31 * result + Objects.hashCode(path); + result = 31 * result + Objects.hashCode(searchParam); + result = 31 * result + Objects.hashCode(valueSet); + result = 31 * result + Objects.hashCode(code); + return result; + } + + @Override + public java.lang.String toString() { + return "DataRequirement.CodeFilter{" + + extensionData + + ", path=" + path + + ", searchParam=" + searchParam + + ", valueSet=" + valueSet + + ", code=" + code + + '}'; + } + } + + public static final class DateFilter extends AbstractElement implements Complex { + + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - extension data reference + * 4 byte - path reference + * 4 byte - searchParam reference + * 4 byte - value reference + * 4 byte - padding + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 20; + + private static final Keyword FHIR_TYPE = RT.keyword("fhir.DataRequirement", "dateFilter"); + + private static final Keyword PATH = RT.keyword(null, "path"); + private static final Keyword SEARCH_PARAM = RT.keyword(null, "searchParam"); + private static final Keyword VALUE_DATE_TIME = RT.keyword(null, "valueDateTime"); + private static final Keyword VALUE_PERIOD = RT.keyword(null, "valuePeriod"); + private static final Keyword VALUE_DURATION = RT.keyword(null, "valueDuration"); + + private static final Keyword[] FIELDS = {ID, EXTENSION, PATH, SEARCH_PARAM, VALUE_DATE_TIME, VALUE_PERIOD, VALUE_DURATION}; + + private static final FieldName FIELD_NAME_PATH = FieldName.of("path"); + private static final FieldName FIELD_NAME_SEARCH_PARAM = FieldName.of("searchParam"); + + private static final byte HASH_MARKER = 63; + + private static final DateFilter EMPTY = new DateFilter(ExtensionData.EMPTY, null, null, null); + + private static final ILookupThunk FHIR_TYPE_LOOKUP_THUNK = new ILookupThunk() { + @Override + public Object get(Object target) { + return target instanceof DateFilter ? FHIR_TYPE : this; + } + }; + + private final String path; + private final String searchParam; + private final ExtensionValue value; + + private DateFilter(ExtensionData extensionData, String path, String searchParam, ExtensionValue value) { + super(extensionData); + this.path = path; + this.searchParam = searchParam; + this.value = value; + } + + public static DateFilter create(IPersistentMap m) { + Object value = m.valAt(VALUE_DATE_TIME); + if (value == null) { + value = m.valAt(VALUE_PERIOD); + } + if (value == null) { + value = m.valAt(VALUE_DURATION); + } + return new DateFilter(ExtensionData.fromMap(m), (String) m.valAt(PATH), (String) m.valAt(SEARCH_PARAM), + (ExtensionValue) value); + } + + public String path() { + return path; + } + + public String searchParam() { + return searchParam; + } + + public ExtensionValue value() { + return value; + } + + @Override + public ILookupThunk getLookupThunk(Keyword key) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE_LOOKUP_THUNK; + return super.getLookupThunk(key); + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE; + if (key == PATH) return path; + if (key == SEARCH_PARAM) return searchParam; + if (key == VALUE_DATE_TIME) return value instanceof DateTime ? value : null; + if (key == VALUE_PERIOD) return value instanceof Period ? value : null; + if (key == VALUE_DURATION) return value instanceof Duration ? value : null; + return super.valAt(key, notFound); + } + + @Override + public ISeq seq() { + ISeq seq = PersistentList.EMPTY; + if (value instanceof DateTime) { + seq = appendElement(seq, VALUE_DATE_TIME, value); + } else if (value instanceof Period) { + seq = appendElement(seq, VALUE_PERIOD, value); + } else if (value instanceof Duration) { + seq = appendElement(seq, VALUE_DURATION, value); + } + seq = appendElement(seq, SEARCH_PARAM, searchParam); + seq = appendElement(seq, PATH, path); + return extensionData.append(seq); + } + + @Override + public DateFilter empty() { + return EMPTY; + } + + @Override + public Iterator> iterator() { + return new BaseIterator(this, FIELDS); + } + + @Override + public DateFilter assoc(Object key, Object val) { + if (key == PATH) + return new DateFilter(extensionData, (String) val, searchParam, value); + if (key == SEARCH_PARAM) + return new DateFilter(extensionData, path, (String) val, value); + if (key == VALUE_DATE_TIME || key == VALUE_PERIOD || key == VALUE_DURATION) + return new DateFilter(extensionData, path, searchParam, (ExtensionValue) val); + if (key == EXTENSION) + return new DateFilter(extensionData.withExtension(val), path, searchParam, value); + if (key == ID) + return new DateFilter(extensionData.withId(val), path, searchParam, value); + return this; + } + + @Override + public DateFilter withMeta(IPersistentMap meta) { + return new DateFilter(extensionData.withMeta(meta), path, searchParam, value); + } + + @Override + public void serializeAsJsonValue(JsonGenerator generator) throws IOException { + generator.writeStartObject(); + serializeJsonBase(generator); + if (path != null) { + path.serializeAsJsonProperty(generator, FIELD_NAME_PATH); + } + if (searchParam != null) { + searchParam.serializeAsJsonProperty(generator, FIELD_NAME_SEARCH_PARAM); + } + if (value != null) { + value.serializeJsonField(generator, value.fieldNameExtensionValue()); + } + generator.writeEndObject(); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (path != null) { + sink.putByte((byte) 2); + path.hashInto(sink); + } + if (searchParam != null) { + sink.putByte((byte) 3); + searchParam.hashInto(sink); + } + if (value != null) { + sink.putByte((byte) 4); + value.hashInto(sink); + } + } + + @Override + public int memSize() { + return MEM_SIZE_OBJECT + extensionData.memSize() + Base.memSize(path) + Base.memSize(searchParam) + + Base.memSize(value); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof DateFilter that && + extensionData.equals(that.extensionData) && + Objects.equals(path, that.path) && + Objects.equals(searchParam, that.searchParam) && + Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + int result = extensionData.hashCode(); + result = 31 * result + Objects.hashCode(path); + result = 31 * result + Objects.hashCode(searchParam); + result = 31 * result + Objects.hashCode(value); + return result; + } + + @Override + public java.lang.String toString() { + return "DataRequirement.DateFilter{" + + extensionData + + ", path=" + path + + ", searchParam=" + searchParam + + ", value=" + value + + '}'; + } + } + + public static final class Sort extends AbstractElement implements Complex { + + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - extension data reference + * 4 byte - path reference + * 4 byte - direction reference + * 4 byte - padding + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 16; + + private static final Keyword FHIR_TYPE = RT.keyword("fhir.DataRequirement", "sort"); + + private static final Keyword PATH = RT.keyword(null, "path"); + private static final Keyword DIRECTION = RT.keyword(null, "direction"); + + private static final Keyword[] FIELDS = {ID, EXTENSION, PATH, DIRECTION}; + + private static final FieldName FIELD_NAME_PATH = FieldName.of("path"); + private static final FieldName FIELD_NAME_DIRECTION = FieldName.of("direction"); + + private static final byte HASH_MARKER = 64; + + private static final Sort EMPTY = new Sort(ExtensionData.EMPTY, null, null); + + private static final ILookupThunk FHIR_TYPE_LOOKUP_THUNK = new ILookupThunk() { + @Override + public Object get(Object target) { + return target instanceof Sort ? FHIR_TYPE : this; + } + }; + + private final String path; + private final Code direction; + + private Sort(ExtensionData extensionData, String path, Code direction) { + super(extensionData); + this.path = path; + this.direction = direction; + } + + public static Sort create(IPersistentMap m) { + return new Sort(ExtensionData.fromMap(m), (String) m.valAt(PATH), (Code) m.valAt(DIRECTION)); + } + + public String path() { + return path; + } + + public Code direction() { + return direction; + } + + @Override + public ILookupThunk getLookupThunk(Keyword key) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE_LOOKUP_THUNK; + return super.getLookupThunk(key); + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE; + if (key == PATH) return path; + if (key == DIRECTION) return direction; + return super.valAt(key, notFound); + } + + @Override + public ISeq seq() { + ISeq seq = PersistentList.EMPTY; + seq = appendElement(seq, DIRECTION, direction); + seq = appendElement(seq, PATH, path); + return extensionData.append(seq); + } + + @Override + public Sort empty() { + return EMPTY; + } + + @Override + public Iterator> iterator() { + return new BaseIterator(this, FIELDS); + } + + @Override + public Sort assoc(Object key, Object val) { + if (key == PATH) return new Sort(extensionData, (String) val, direction); + if (key == DIRECTION) return new Sort(extensionData, path, (Code) val); + if (key == EXTENSION) return new Sort(extensionData.withExtension(val), path, direction); + if (key == ID) return new Sort(extensionData.withId(val), path, direction); + return this; + } + + @Override + public Sort withMeta(IPersistentMap meta) { + return new Sort(extensionData.withMeta(meta), path, direction); + } + + @Override + public void serializeAsJsonValue(JsonGenerator generator) throws IOException { + generator.writeStartObject(); + serializeJsonBase(generator); + if (path != null) { + path.serializeAsJsonProperty(generator, FIELD_NAME_PATH); + } + if (direction != null) { + direction.serializeAsJsonProperty(generator, FIELD_NAME_DIRECTION); + } + generator.writeEndObject(); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (path != null) { + sink.putByte((byte) 2); + path.hashInto(sink); + } + if (direction != null) { + sink.putByte((byte) 3); + direction.hashInto(sink); + } + } + + @Override + public int memSize() { + return MEM_SIZE_OBJECT + extensionData.memSize() + Base.memSize(path) + Base.memSize(direction); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof Sort that && + extensionData.equals(that.extensionData) && + Objects.equals(path, that.path) && + Objects.equals(direction, that.direction); + } + + @Override + public int hashCode() { + int result = extensionData.hashCode(); + result = 31 * result + Objects.hashCode(path); + result = 31 * result + Objects.hashCode(direction); + return result; + } + + @Override + public java.lang.String toString() { + return "DataRequirement.Sort{" + + extensionData + + ", path=" + path + + ", direction=" + direction + + '}'; + } + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Date.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Date.java new file mode 100644 index 000000000..5fb5b79a8 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Date.java @@ -0,0 +1,121 @@ +package blaze.fhir.spec.type; + +import blaze.Interner; +import blaze.Interners; +import clojure.lang.IPersistentMap; +import clojure.lang.Keyword; +import clojure.lang.RT; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.lang.String; +import java.util.Objects; + +@SuppressWarnings("DuplicatedCode") +public final class Date extends PrimitiveElement { + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "date"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueDate"); + + private static final byte HASH_MARKER = 10; + + private static final Interner INTERNER = Interners.weakInterner(k -> new Date(k, null)); + private static final Date EMPTY = new Date(ExtensionData.EMPTY, null); + + private final blaze.fhir.spec.type.system.Date value; + + private Date(ExtensionData extensionData, blaze.fhir.spec.type.system.Date value) { + super(extensionData); + this.value = value; + } + + private static Date maybeIntern(ExtensionData extensionData, blaze.fhir.spec.type.system.Date value) { + return extensionData.isInterned() && value == null ? INTERNER.intern(extensionData) : new Date(extensionData, value); + } + + public static Date create(blaze.fhir.spec.type.system.Date value) { + return value == null ? EMPTY : new Date(ExtensionData.EMPTY, value); + } + + public static Date create(IPersistentMap m) { + return maybeIntern(ExtensionData.fromMap(m), (blaze.fhir.spec.type.system.Date) m.valAt(VALUE)); + } + + public blaze.fhir.spec.type.system.Date value() { + return value; + } + + @Override + public Object valAt(Object key, Object notFound) { + return key == FHIR_TYPE_KEY ? FHIR_TYPE : super.valAt(key, notFound); + } + + @Override + public Date empty() { + return EMPTY; + } + + @Override + public Date assoc(Object key, Object val) { + if (key == VALUE) return maybeIntern(extensionData, (blaze.fhir.spec.type.system.Date) val); + if (key == EXTENSION) return maybeIntern(extensionData.withExtension(val), value); + if (key == ID) return maybeIntern(extensionData.withId(val), value); + return this; + } + + @Override + public Date withMeta(IPersistentMap meta) { + return maybeIntern(extensionData.withMeta(meta), value); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeJsonPrimitiveValue(JsonGenerator generator) throws IOException { + if (hasValue()) { + // TODO: improve performance with AsciiByteArrayAppendable + generator.writeString(value.toString()); + } else { + generator.writeNull(); + } + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (value != null) { + sink.putByte((byte) 2); + value.hashInto(sink); + } + } + + @Override + public int memSize() { + return super.memSize() + (value == null ? 0 : value.memSize()); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof Date that && + extensionData.equals(that.extensionData) && + Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return 31 * extensionData.hashCode() + Objects.hashCode(value); + } + + @Override + public String toString() { + return "Date{" + extensionData + ", value=" + value + '}'; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/DateTime.java b/modules/fhir-structure/java/blaze/fhir/spec/type/DateTime.java new file mode 100644 index 000000000..e13557610 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/DateTime.java @@ -0,0 +1,128 @@ +package blaze.fhir.spec.type; + +import blaze.Interner; +import blaze.Interners; +import blaze.fhir.spec.type.system.DateTimes; +import clojure.lang.IPersistentMap; +import clojure.lang.Keyword; +import clojure.lang.RT; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.lang.String; +import java.time.temporal.Temporal; +import java.util.Objects; + +@SuppressWarnings("DuplicatedCode") +public final class DateTime extends PrimitiveElement { + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "dateTime"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueDateTime"); + + private static final byte HASH_MARKER = 11; + + private static final Interner INTERNER = Interners.weakInterner(k -> new DateTime(k, null)); + private static final DateTime EMPTY = new DateTime(ExtensionData.EMPTY, null); + + private final Temporal value; + + private DateTime(ExtensionData extensionData, Temporal value) { + super(extensionData); + this.value = value; + } + + private static DateTime maybeIntern(ExtensionData extensionData, Temporal value) { + return extensionData.isInterned() && value == null ? INTERNER.intern(extensionData) : new DateTime(extensionData, value); + } + + public static DateTime create(Temporal value) { + return value == null ? EMPTY : new DateTime(ExtensionData.EMPTY, value); + } + + public static DateTime create(IPersistentMap m) { + return maybeIntern(ExtensionData.fromMap(m), (Temporal) m.valAt(VALUE)); + } + + public Temporal value() { + return value; + } + + @Override + public Object valAt(Object key, Object notFound) { + return key == FHIR_TYPE_KEY ? FHIR_TYPE : super.valAt(key, notFound); + } + + @Override + public String valueAsString() { + return value == null ? null : DateTimes.toString(value); + } + + @Override + public DateTime empty() { + return EMPTY; + } + + @Override + public DateTime assoc(Object key, Object val) { + if (key == VALUE) return maybeIntern(extensionData, (Temporal) val); + if (key == EXTENSION) return maybeIntern(extensionData.withExtension(val), value); + if (key == ID) return maybeIntern(extensionData.withId(val), value); + return this; + } + + @Override + public DateTime withMeta(IPersistentMap meta) { + return maybeIntern(extensionData.withMeta(meta), value); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeJsonPrimitiveValue(JsonGenerator generator) throws IOException { + if (hasValue()) { + // TODO: improve performance with AsciiByteArrayAppendable + generator.writeString(valueAsString()); + } else { + generator.writeNull(); + } + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (value != null) { + sink.putByte((byte) 2); + DateTimes.hashInto(value, sink); + } + } + + @Override + public int memSize() { + return super.memSize() + DateTimes.memSize(value); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof DateTime that && + extensionData.equals(that.extensionData) && + Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return 31 * extensionData.hashCode() + Objects.hashCode(value); + } + + @Override + public String toString() { + return "DateTime{" + extensionData + ", value=" + value + '}'; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Decimal.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Decimal.java new file mode 100644 index 000000000..da64e55fb --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Decimal.java @@ -0,0 +1,122 @@ +package blaze.fhir.spec.type; + +import blaze.Interner; +import blaze.Interners; +import blaze.fhir.spec.type.system.Decimals; +import clojure.lang.IPersistentMap; +import clojure.lang.Keyword; +import clojure.lang.RT; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.lang.String; +import java.math.BigDecimal; +import java.util.Objects; + +@SuppressWarnings("DuplicatedCode") +public final class Decimal extends PrimitiveElement { + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "decimal"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueDecimal"); + + private static final byte HASH_MARKER = 4; + + private static final Interner INTERNER = Interners.weakInterner(k -> new Decimal(k, null)); + private static final Decimal EMPTY = new Decimal(ExtensionData.EMPTY, null); + + private final BigDecimal value; + + private Decimal(ExtensionData extensionData, BigDecimal value) { + super(extensionData); + this.value = value; + } + + private static Decimal maybeIntern(ExtensionData extensionData, BigDecimal value) { + return extensionData.isInterned() && value == null ? INTERNER.intern(extensionData) : new Decimal(extensionData, value); + } + + public static Decimal create(BigDecimal value) { + return value == null ? EMPTY : new Decimal(ExtensionData.EMPTY, value); + } + + public static Decimal create(IPersistentMap m) { + return maybeIntern(ExtensionData.fromMap(m), (BigDecimal) m.valAt(VALUE)); + } + + public BigDecimal value() { + return value; + } + + @Override + public Object valAt(Object key, Object notFound) { + return key == FHIR_TYPE_KEY ? FHIR_TYPE : super.valAt(key, notFound); + } + + @Override + public Decimal empty() { + return EMPTY; + } + + @Override + public Decimal assoc(Object key, Object val) { + if (key == VALUE) return maybeIntern(extensionData, (BigDecimal) val); + if (key == EXTENSION) return maybeIntern(extensionData.withExtension(val), value); + if (key == ID) return maybeIntern(extensionData.withId(val), value); + return this; + } + + @Override + public Decimal withMeta(IPersistentMap meta) { + return maybeIntern(extensionData.withMeta(meta), value); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeJsonPrimitiveValue(JsonGenerator generator) throws IOException { + if (hasValue()) { + generator.writeNumber(value); + } else { + generator.writeNull(); + } + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (value != null) { + sink.putByte((byte) 2); + Decimals.hashInto(value, sink); + } + } + + @Override + public int memSize() { + return super.memSize() + Decimals.memSize(value); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof Decimal d && + extensionData.equals(d.extensionData) && + Objects.equals(value, d.value); + } + + @Override + public int hashCode() { + return 31 * extensionData.hashCode() + Objects.hashCode(value); + } + + @Override + public String toString() { + return "Decimal{" + extensionData + ", value=" + value + '}'; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Distance.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Distance.java new file mode 100644 index 000000000..f6c243a14 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Distance.java @@ -0,0 +1,71 @@ +package blaze.fhir.spec.type; + +import blaze.Interner; +import blaze.Interners; +import clojure.lang.IPersistentMap; +import clojure.lang.Keyword; +import clojure.lang.RT; + +@SuppressWarnings("DuplicatedCode") +public final class Distance extends AbstractQuantity { + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "Distance"); + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueDistance"); + + private static final Distance EMPTY = new Distance(ExtensionData.EMPTY, null, null, null, null, null); + + private static final Interner INTERNER = Interners.weakInterner( + k -> new Distance(k.extensionData(), k.value(), k.comparator(), k.unit(), k.system(), k.code()) + ); + + private Distance(ExtensionData extensionData, Decimal value, Code comparator, String unit, Uri system, Code code) { + super(extensionData, value, comparator, unit, system, code); + } + + private static Distance maybeIntern(ExtensionData extensionData, Decimal value, Code comparator, String unit, + Uri system, Code code) { + return extensionData.isInterned() && Base.isInterned(value) && Base.isInterned(comparator) && + Base.isInterned(unit) && Base.isInterned(system) && Base.isInterned(code) + ? INTERNER.intern(new InternerKey(extensionData, value, comparator, unit, system, code)) + : new Distance(extensionData, value, comparator, unit, system, code); + } + + public static Distance create(IPersistentMap m) { + return maybeIntern(ExtensionData.fromMap(m), (Decimal) m.valAt(VALUE), (Code) m.valAt(COMPARATOR), + (String) m.valAt(UNIT), (Uri) m.valAt(SYSTEM), (Code) m.valAt(CODE)); + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE; + return super.valAt(key, notFound); + } + + @Override + public Distance empty() { + return EMPTY; + } + + @Override + public Distance assoc(Object key, Object val) { + if (key == VALUE) return maybeIntern(extensionData, (Decimal) val, comparator, unit, system, code); + if (key == COMPARATOR) return maybeIntern(extensionData, value, (Code) val, unit, system, code); + if (key == UNIT) return maybeIntern(extensionData, value, comparator, (String) val, system, code); + if (key == SYSTEM) return maybeIntern(extensionData, value, comparator, unit, (Uri) val, code); + if (key == CODE) return maybeIntern(extensionData, value, comparator, unit, system, (Code) val); + if (key == EXTENSION) + return maybeIntern(extensionData.withExtension(val), value, comparator, unit, system, code); + if (key == ID) return maybeIntern(extensionData.withId(val), value, comparator, unit, system, code); + return this; + } + + @Override + public Distance withMeta(IPersistentMap meta) { + return maybeIntern(extensionData.withMeta(meta), value, comparator, unit, system, code); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Dosage.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Dosage.java new file mode 100644 index 000000000..c3468c934 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Dosage.java @@ -0,0 +1,652 @@ +package blaze.fhir.spec.type; + +import clojure.lang.*; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; + +import static blaze.fhir.spec.type.Base.appendElement; +import static blaze.fhir.spec.type.Complex.serializeJsonComplexList; +import static java.util.Objects.requireNonNull; + +@SuppressWarnings("DuplicatedCode") +public final class Dosage extends AbstractBackboneElement implements Complex, ExtensionValue { + + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - extension data reference + * 4 byte - modifierExtension reference + * 4 byte - sequence reference + * 4 byte - text reference + * 4 byte - additionalInstruction reference + * 4 byte - patientInstruction reference + * 4 byte - timing reference + * 4 byte - asNeeded reference + * 4 byte - site reference + * 4 byte - route reference + * 4 byte - method reference + * 4 byte - doseAndRate reference + * 4 byte - maxDosePerPeriod reference + * 4 byte - maxDosePerAdministration reference + * 4 byte - maxDosePerLifetime reference + * 4 byte - padding + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 64; + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "Dosage"); + + private static final Keyword SEQUENCE = RT.keyword(null, "sequence"); + private static final Keyword TEXT = RT.keyword(null, "text"); + private static final Keyword ADDITIONAL_INSTRUCTION = RT.keyword(null, "additionalInstruction"); + private static final Keyword PATIENT_INSTRUCTION = RT.keyword(null, "patientInstruction"); + private static final Keyword TIMING = RT.keyword(null, "timing"); + private static final Keyword AS_NEEDED = RT.keyword(null, "asNeeded"); + private static final Keyword SITE = RT.keyword(null, "site"); + private static final Keyword ROUTE = RT.keyword(null, "route"); + private static final Keyword METHOD = RT.keyword(null, "method"); + private static final Keyword DOSE_AND_RATE = RT.keyword(null, "doseAndRate"); + private static final Keyword MAX_DOSE_PER_PERIOD = RT.keyword(null, "maxDosePerPeriod"); + private static final Keyword MAX_DOSE_PER_ADMINISTRATION = RT.keyword(null, "maxDosePerAdministration"); + private static final Keyword MAX_DOSE_PER_LIFETIME = RT.keyword(null, "maxDosePerLifetime"); + + private static final Keyword[] FIELDS = {ID, EXTENSION, MODIFIER_EXTENSION, SEQUENCE, TEXT, ADDITIONAL_INSTRUCTION, + PATIENT_INSTRUCTION, TIMING, AS_NEEDED, SITE, ROUTE, METHOD, DOSE_AND_RATE, MAX_DOSE_PER_PERIOD, + MAX_DOSE_PER_ADMINISTRATION, MAX_DOSE_PER_LIFETIME}; + + private static final FieldName FIELD_NAME_SEQUENCE = FieldName.of("sequence"); + private static final FieldName FIELD_NAME_TEXT = FieldName.of("text"); + private static final FieldName FIELD_NAME_ADDITIONAL_INSTRUCTION = FieldName.of("additionalInstruction"); + private static final FieldName FIELD_NAME_PATIENT_INSTRUCTION = FieldName.of("patientInstruction"); + private static final FieldName FIELD_NAME_TIMING = FieldName.of("timing"); + private static final FieldName FIELD_NAME_AS_NEEDED_BOOLEAN = FieldName.of("asNeededBoolean"); + private static final FieldName FIELD_NAME_AS_NEEDED_CODEABLE_CONCEPT = FieldName.of("asNeededCodeableConcept"); + private static final FieldName FIELD_NAME_SITE = FieldName.of("site"); + private static final FieldName FIELD_NAME_ROUTE = FieldName.of("route"); + private static final FieldName FIELD_NAME_METHOD = FieldName.of("method"); + private static final FieldName FIELD_NAME_DOSE_AND_RATE = FieldName.of("doseAndRate"); + private static final FieldName FIELD_NAME_MAX_DOSE_PER_PERIOD = FieldName.of("maxDosePerPeriod"); + private static final FieldName FIELD_NAME_MAX_DOSE_PER_ADMINISTRATION = FieldName.of("maxDosePerAdministration"); + private static final FieldName FIELD_NAME_MAX_DOSE_PER_LIFETIME = FieldName.of("maxDosePerLifetime"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueDosage"); + + private static final byte HASH_MARKER = 61; + + @SuppressWarnings("unchecked") + private static final Dosage EMPTY = new Dosage(ExtensionData.EMPTY, PersistentVector.EMPTY, null, null, + PersistentVector.EMPTY, null, null, null, null, null, null, PersistentVector.EMPTY, null, null, null); + + private final Integer sequence; + private final String text; + private final List additionalInstruction; + private final String patientInstruction; + private final Timing timing; + private final Element asNeeded; + private final CodeableConcept site; + private final CodeableConcept route; + private final CodeableConcept method; + private final List doseAndRate; + private final Ratio maxDosePerPeriod; + private final Quantity maxDosePerAdministration; + private final Quantity maxDosePerLifetime; + + private Dosage(ExtensionData extensionData, List modifierExtension, Integer sequence, String text, + List additionalInstruction, String patientInstruction, Timing timing, + Element asNeeded, CodeableConcept site, CodeableConcept route, CodeableConcept method, + List doseAndRate, Ratio maxDosePerPeriod, Quantity maxDosePerAdministration, + Quantity maxDosePerLifetime) { + super(extensionData, modifierExtension); + this.sequence = sequence; + this.text = text; + this.additionalInstruction = requireNonNull(additionalInstruction); + this.patientInstruction = patientInstruction; + this.timing = timing; + this.asNeeded = asNeeded; + this.site = site; + this.route = route; + this.method = method; + this.doseAndRate = requireNonNull(doseAndRate); + this.maxDosePerPeriod = maxDosePerPeriod; + this.maxDosePerAdministration = maxDosePerAdministration; + this.maxDosePerLifetime = maxDosePerLifetime; + } + + public static Dosage create(IPersistentMap m) { + return new Dosage(ExtensionData.fromMap(m), Base.listFrom(m, MODIFIER_EXTENSION), (Integer) m.valAt(SEQUENCE), + (String) m.valAt(TEXT), Base.listFrom(m, ADDITIONAL_INSTRUCTION), (String) m.valAt(PATIENT_INSTRUCTION), + (Timing) m.valAt(TIMING), (Element) m.valAt(AS_NEEDED), (CodeableConcept) m.valAt(SITE), + (CodeableConcept) m.valAt(ROUTE), (CodeableConcept) m.valAt(METHOD), Base.listFrom(m, DOSE_AND_RATE), + (Ratio) m.valAt(MAX_DOSE_PER_PERIOD), (Quantity) m.valAt(MAX_DOSE_PER_ADMINISTRATION), + (Quantity) m.valAt(MAX_DOSE_PER_LIFETIME)); + } + + public Integer sequence() { + return sequence; + } + + public String text() { + return text; + } + + public List additionalInstruction() { + return additionalInstruction; + } + + public String patientInstruction() { + return patientInstruction; + } + + public Timing timing() { + return timing; + } + + public Base asNeeded() { + return asNeeded; + } + + public CodeableConcept site() { + return site; + } + + public CodeableConcept route() { + return route; + } + + public CodeableConcept method() { + return method; + } + + public List doseAndRate() { + return doseAndRate; + } + + public Ratio maxDosePerPeriod() { + return maxDosePerPeriod; + } + + public Quantity maxDosePerAdministration() { + return maxDosePerAdministration; + } + + public Quantity maxDosePerLifetime() { + return maxDosePerLifetime; + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE; + if (key == SEQUENCE) return sequence; + if (key == TEXT) return text; + if (key == ADDITIONAL_INSTRUCTION) return additionalInstruction; + if (key == PATIENT_INSTRUCTION) return patientInstruction; + if (key == TIMING) return timing; + if (key == AS_NEEDED) return asNeeded; + if (key == SITE) return site; + if (key == ROUTE) return route; + if (key == METHOD) return method; + if (key == DOSE_AND_RATE) return doseAndRate; + if (key == MAX_DOSE_PER_PERIOD) return maxDosePerPeriod; + if (key == MAX_DOSE_PER_ADMINISTRATION) return maxDosePerAdministration; + if (key == MAX_DOSE_PER_LIFETIME) return maxDosePerLifetime; + return super.valAt(key, notFound); + } + + @Override + public ISeq seq() { + ISeq seq = PersistentList.EMPTY; + seq = appendElement(seq, MAX_DOSE_PER_LIFETIME, maxDosePerLifetime); + seq = appendElement(seq, MAX_DOSE_PER_ADMINISTRATION, maxDosePerAdministration); + seq = appendElement(seq, MAX_DOSE_PER_PERIOD, maxDosePerPeriod); + if (!doseAndRate.isEmpty()) { + seq = appendElement(seq, DOSE_AND_RATE, doseAndRate); + } + seq = appendElement(seq, METHOD, method); + seq = appendElement(seq, ROUTE, route); + seq = appendElement(seq, SITE, site); + seq = appendElement(seq, AS_NEEDED, asNeeded); + seq = appendElement(seq, TIMING, timing); + seq = appendElement(seq, PATIENT_INSTRUCTION, patientInstruction); + if (!additionalInstruction.isEmpty()) { + seq = appendElement(seq, ADDITIONAL_INSTRUCTION, additionalInstruction); + } + seq = appendElement(seq, TEXT, text); + seq = appendElement(seq, SEQUENCE, sequence); + seq = appendElement(seq, MODIFIER_EXTENSION, modifierExtension); + return extensionData.append(seq); + } + + @Override + public Dosage empty() { + return EMPTY; + } + + @Override + public Iterator> iterator() { + return new BaseIterator(this, FIELDS); + } + + @Override + public Dosage assoc(Object key, Object val) { + if (key == SEQUENCE) + return new Dosage(extensionData, modifierExtension, (Integer) val, text, additionalInstruction, patientInstruction, timing, asNeeded, site, route, method, doseAndRate, maxDosePerPeriod, maxDosePerAdministration, maxDosePerLifetime); + if (key == TEXT) + return new Dosage(extensionData, modifierExtension, sequence, (String) val, additionalInstruction, patientInstruction, timing, asNeeded, site, route, method, doseAndRate, maxDosePerPeriod, maxDosePerAdministration, maxDosePerLifetime); + if (key == ADDITIONAL_INSTRUCTION) + return new Dosage(extensionData, modifierExtension, sequence, text, Lists.nullToEmpty(val), patientInstruction, timing, asNeeded, site, route, method, doseAndRate, maxDosePerPeriod, maxDosePerAdministration, maxDosePerLifetime); + if (key == PATIENT_INSTRUCTION) + return new Dosage(extensionData, modifierExtension, sequence, text, additionalInstruction, (String) val, timing, asNeeded, site, route, method, doseAndRate, maxDosePerPeriod, maxDosePerAdministration, maxDosePerLifetime); + if (key == TIMING) + return new Dosage(extensionData, modifierExtension, sequence, text, additionalInstruction, patientInstruction, (Timing) val, asNeeded, site, route, method, doseAndRate, maxDosePerPeriod, maxDosePerAdministration, maxDosePerLifetime); + if (key == AS_NEEDED) + return new Dosage(extensionData, modifierExtension, sequence, text, additionalInstruction, patientInstruction, timing, (Element) val, site, route, method, doseAndRate, maxDosePerPeriod, maxDosePerAdministration, maxDosePerLifetime); + if (key == SITE) + return new Dosage(extensionData, modifierExtension, sequence, text, additionalInstruction, patientInstruction, timing, asNeeded, (CodeableConcept) val, route, method, doseAndRate, maxDosePerPeriod, maxDosePerAdministration, maxDosePerLifetime); + if (key == ROUTE) + return new Dosage(extensionData, modifierExtension, sequence, text, additionalInstruction, patientInstruction, timing, asNeeded, site, (CodeableConcept) val, method, doseAndRate, maxDosePerPeriod, maxDosePerAdministration, maxDosePerLifetime); + if (key == METHOD) + return new Dosage(extensionData, modifierExtension, sequence, text, additionalInstruction, patientInstruction, timing, asNeeded, site, route, (CodeableConcept) val, doseAndRate, maxDosePerPeriod, maxDosePerAdministration, maxDosePerLifetime); + if (key == DOSE_AND_RATE) + return new Dosage(extensionData, modifierExtension, sequence, text, additionalInstruction, patientInstruction, timing, asNeeded, site, route, method, Lists.nullToEmpty(val), maxDosePerPeriod, maxDosePerAdministration, maxDosePerLifetime); + if (key == MAX_DOSE_PER_PERIOD) + return new Dosage(extensionData, modifierExtension, sequence, text, additionalInstruction, patientInstruction, timing, asNeeded, site, route, method, doseAndRate, (Ratio) val, maxDosePerAdministration, maxDosePerLifetime); + if (key == MAX_DOSE_PER_ADMINISTRATION) + return new Dosage(extensionData, modifierExtension, sequence, text, additionalInstruction, patientInstruction, timing, asNeeded, site, route, method, doseAndRate, maxDosePerPeriod, (Quantity) val, maxDosePerLifetime); + if (key == MAX_DOSE_PER_LIFETIME) + return new Dosage(extensionData, modifierExtension, sequence, text, additionalInstruction, patientInstruction, timing, asNeeded, site, route, method, doseAndRate, maxDosePerPeriod, maxDosePerAdministration, (Quantity) val); + if (key == MODIFIER_EXTENSION) + return new Dosage(extensionData, Lists.nullToEmpty(val), sequence, text, additionalInstruction, patientInstruction, timing, asNeeded, site, route, method, doseAndRate, maxDosePerPeriod, maxDosePerAdministration, maxDosePerLifetime); + if (key == EXTENSION) + return new Dosage(extensionData.withExtension(val), modifierExtension, sequence, text, additionalInstruction, patientInstruction, timing, asNeeded, site, route, method, doseAndRate, maxDosePerPeriod, maxDosePerAdministration, maxDosePerLifetime); + if (key == ID) + return new Dosage(extensionData.withId(val), modifierExtension, sequence, text, additionalInstruction, patientInstruction, timing, asNeeded, site, route, method, doseAndRate, maxDosePerPeriod, maxDosePerAdministration, maxDosePerLifetime); + return this; + } + + @Override + public Dosage withMeta(IPersistentMap meta) { + return new Dosage(extensionData.withMeta(meta), modifierExtension, sequence, text, additionalInstruction, patientInstruction, timing, asNeeded, site, route, method, doseAndRate, maxDosePerPeriod, maxDosePerAdministration, maxDosePerLifetime); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeAsJsonValue(JsonGenerator generator) throws IOException { + generator.writeStartObject(); + serializeJsonBase(generator); + if (sequence != null) { + sequence.serializeAsJsonProperty(generator, FIELD_NAME_SEQUENCE); + } + if (text != null) { + text.serializeAsJsonProperty(generator, FIELD_NAME_TEXT); + } + if (!additionalInstruction.isEmpty()) { + serializeJsonComplexList(additionalInstruction, generator, FIELD_NAME_ADDITIONAL_INSTRUCTION.normal()); + } + if (patientInstruction != null) { + patientInstruction.serializeAsJsonProperty(generator, FIELD_NAME_PATIENT_INSTRUCTION); + } + if (timing != null) { + timing.serializeJsonField(generator, FIELD_NAME_TIMING); + } + if (asNeeded != null) { + switch (asNeeded) { + case Boolean asNeededBoolean -> + asNeededBoolean.serializeAsJsonProperty(generator, FIELD_NAME_AS_NEEDED_BOOLEAN); + case CodeableConcept asNeededCodeableConcept -> + asNeededCodeableConcept.serializeJsonField(generator, FIELD_NAME_AS_NEEDED_CODEABLE_CONCEPT); + default -> { + } + } + } + if (site != null) { + site.serializeJsonField(generator, FIELD_NAME_SITE); + } + if (route != null) { + route.serializeJsonField(generator, FIELD_NAME_ROUTE); + } + if (method != null) { + method.serializeJsonField(generator, FIELD_NAME_METHOD); + } + if (!doseAndRate.isEmpty()) { + serializeJsonComplexList(doseAndRate, generator, FIELD_NAME_DOSE_AND_RATE.normal()); + } + if (maxDosePerPeriod != null) { + maxDosePerPeriod.serializeJsonField(generator, FIELD_NAME_MAX_DOSE_PER_PERIOD); + } + if (maxDosePerAdministration != null) { + maxDosePerAdministration.serializeJsonField(generator, FIELD_NAME_MAX_DOSE_PER_ADMINISTRATION); + } + if (maxDosePerLifetime != null) { + maxDosePerLifetime.serializeJsonField(generator, FIELD_NAME_MAX_DOSE_PER_LIFETIME); + } + generator.writeEndObject(); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (!modifierExtension.isEmpty()) { + sink.putByte((byte) 2); + Base.hashIntoList(modifierExtension, sink); + } + if (sequence != null) { + sink.putByte((byte) 3); + sequence.hashInto(sink); + } + if (text != null) { + sink.putByte((byte) 4); + text.hashInto(sink); + } + if (!additionalInstruction.isEmpty()) { + sink.putByte((byte) 5); + Base.hashIntoList(additionalInstruction, sink); + } + if (patientInstruction != null) { + sink.putByte((byte) 6); + patientInstruction.hashInto(sink); + } + if (timing != null) { + sink.putByte((byte) 7); + timing.hashInto(sink); + } + if (asNeeded != null) { + sink.putByte((byte) 8); + asNeeded.hashInto(sink); + } + if (site != null) { + sink.putByte((byte) 9); + site.hashInto(sink); + } + if (route != null) { + sink.putByte((byte) 10); + route.hashInto(sink); + } + if (method != null) { + sink.putByte((byte) 11); + method.hashInto(sink); + } + if (!doseAndRate.isEmpty()) { + sink.putByte((byte) 12); + Base.hashIntoList(doseAndRate, sink); + } + if (maxDosePerPeriod != null) { + sink.putByte((byte) 13); + maxDosePerPeriod.hashInto(sink); + } + if (maxDosePerAdministration != null) { + sink.putByte((byte) 14); + maxDosePerAdministration.hashInto(sink); + } + if (maxDosePerLifetime != null) { + sink.putByte((byte) 15); + maxDosePerLifetime.hashInto(sink); + } + } + + @Override + public int memSize() { + return MEM_SIZE_OBJECT + extensionData.memSize() + Base.memSize(modifierExtension) + Base.memSize(sequence) + + Base.memSize(text) + Base.memSize(additionalInstruction) + Base.memSize(patientInstruction) + + Base.memSize(timing) + Base.memSize(asNeeded) + Base.memSize(site) + Base.memSize(route) + + Base.memSize(method) + Base.memSize(doseAndRate) + Base.memSize(maxDosePerPeriod) + + Base.memSize(maxDosePerAdministration) + Base.memSize(maxDosePerLifetime); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof Dosage that && + extensionData.equals(that.extensionData) && + modifierExtension.equals(that.modifierExtension) && + Objects.equals(sequence, that.sequence) && + Objects.equals(text, that.text) && + additionalInstruction.equals(that.additionalInstruction) && + Objects.equals(patientInstruction, that.patientInstruction) && + Objects.equals(timing, that.timing) && + Objects.equals(asNeeded, that.asNeeded) && + Objects.equals(site, that.site) && + Objects.equals(route, that.route) && + Objects.equals(method, that.method) && + doseAndRate.equals(that.doseAndRate) && + Objects.equals(maxDosePerPeriod, that.maxDosePerPeriod) && + Objects.equals(maxDosePerAdministration, that.maxDosePerAdministration) && + Objects.equals(maxDosePerLifetime, that.maxDosePerLifetime); + } + + @Override + public int hashCode() { + int result = extensionData.hashCode(); + result = 31 * result + modifierExtension.hashCode(); + result = 31 * result + Objects.hashCode(sequence); + result = 31 * result + Objects.hashCode(text); + result = 31 * result + additionalInstruction.hashCode(); + result = 31 * result + Objects.hashCode(patientInstruction); + result = 31 * result + Objects.hashCode(timing); + result = 31 * result + Objects.hashCode(asNeeded); + result = 31 * result + Objects.hashCode(site); + result = 31 * result + Objects.hashCode(route); + result = 31 * result + Objects.hashCode(method); + result = 31 * result + doseAndRate.hashCode(); + result = 31 * result + Objects.hashCode(maxDosePerPeriod); + result = 31 * result + Objects.hashCode(maxDosePerAdministration); + result = 31 * result + Objects.hashCode(maxDosePerLifetime); + return result; + } + + @Override + public java.lang.String toString() { + return "Dosage{" + + extensionData + + ", modifierExtension=" + modifierExtension + + ", sequence=" + sequence + + ", text=" + text + + ", additionalInstruction=" + additionalInstruction + + ", patientInstruction=" + patientInstruction + + ", timing=" + timing + + ", asNeeded=" + asNeeded + + ", site=" + site + + ", route=" + route + + ", method=" + method + + ", doseAndRate=" + doseAndRate + + ", maxDosePerPeriod=" + maxDosePerPeriod + + ", maxDosePerAdministration=" + maxDosePerAdministration + + ", maxDosePerLifetime=" + maxDosePerLifetime + + '}'; + } + + @SuppressWarnings("DuplicatedCode") + public static class DoseAndRate extends AbstractElement implements Complex { + + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - extension data reference + * 4 byte - type reference + * 4 byte - dose reference + * 4 byte - rate reference + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 16; + + private static final Keyword FHIR_TYPE = RT.keyword("fhir.Dosage", "doseAndRate"); + + private static final Keyword TYPE = RT.keyword(null, "type"); + private static final Keyword DOSE = RT.keyword(null, "dose"); + private static final Keyword RATE = RT.keyword(null, "rate"); + + private static final Keyword[] FIELDS = {ID, EXTENSION, TYPE, DOSE, RATE}; + + private static final FieldName FIELD_NAME_TYPE = FieldName.of("type"); + private static final FieldName FIELD_NAME_DOSE_RANGE = FieldName.of("doseRange"); + private static final FieldName FIELD_NAME_DOSE_QUANTITY = FieldName.of("doseQuantity"); + private static final FieldName FIELD_NAME_RATE_RATIO = FieldName.of("rateRatio"); + private static final FieldName FIELD_NAME_RATE_RANGE = FieldName.of("rateRange"); + private static final FieldName FIELD_NAME_RATE_QUANTITY = FieldName.of("rateQuantity"); + + private static final byte HASH_MARKER = 62; + + private static final DoseAndRate EMPTY = new DoseAndRate(ExtensionData.EMPTY, null, null, null); + + private final CodeableConcept type; + private final Element dose; + private final Element rate; + + private DoseAndRate(ExtensionData extensionData, CodeableConcept type, Element dose, Element rate) { + super(extensionData); + this.type = type; + this.dose = dose; + this.rate = rate; + } + + public static DoseAndRate create(IPersistentMap m) { + return new DoseAndRate(ExtensionData.fromMap(m), (CodeableConcept) m.valAt(TYPE), (Element) m.valAt(DOSE), + (Element) m.valAt(RATE)); + } + + public CodeableConcept type() { + return type; + } + + public Base dose() { + return dose; + } + + public Base rate() { + return rate; + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE; + if (key == TYPE) return type; + if (key == DOSE) return dose; + if (key == RATE) return rate; + return super.valAt(key, notFound); + } + + @Override + public ISeq seq() { + ISeq seq = PersistentList.EMPTY; + seq = appendElement(seq, RATE, rate); + seq = appendElement(seq, DOSE, dose); + seq = appendElement(seq, TYPE, type); + return extensionData.append(seq); + } + + @Override + public DoseAndRate empty() { + return EMPTY; + } + + @Override + public Iterator> iterator() { + return new BaseIterator(this, FIELDS); + } + + @Override + public DoseAndRate assoc(Object key, Object val) { + if (key == TYPE) return new DoseAndRate(extensionData, (CodeableConcept) val, dose, rate); + if (key == DOSE) return new DoseAndRate(extensionData, type, (Element) val, rate); + if (key == RATE) return new DoseAndRate(extensionData, type, dose, (Element) val); + if (key == EXTENSION) return new DoseAndRate(extensionData.withExtension(val), type, dose, rate); + if (key == ID) return new DoseAndRate(extensionData.withId(val), type, dose, rate); + return this; + } + + @Override + public DoseAndRate withMeta(IPersistentMap meta) { + return new DoseAndRate(extensionData.withMeta(meta), type, dose, rate); + } + + @Override + public void serializeAsJsonValue(JsonGenerator generator) throws IOException { + generator.writeStartObject(); + serializeJsonBase(generator); + if (type != null) { + type.serializeJsonField(generator, FIELD_NAME_TYPE); + } + if (dose != null) { + switch (dose) { + case Range doseRange -> doseRange.serializeJsonField(generator, FIELD_NAME_DOSE_RANGE); + case Quantity doseQuantity -> doseQuantity.serializeJsonField(generator, FIELD_NAME_DOSE_QUANTITY); + default -> { + } + } + } + if (rate != null) { + switch (rate) { + case Ratio rateRatio -> rateRatio.serializeJsonField(generator, FIELD_NAME_RATE_RATIO); + case Range rateRange -> rateRange.serializeJsonField(generator, FIELD_NAME_RATE_RANGE); + case Quantity rateQuantity -> rateQuantity.serializeJsonField(generator, FIELD_NAME_RATE_QUANTITY); + default -> { + } + } + } + generator.writeEndObject(); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (type != null) { + sink.putByte((byte) 2); + type.hashInto(sink); + } + if (dose != null) { + sink.putByte((byte) 3); + dose.hashInto(sink); + } + if (rate != null) { + sink.putByte((byte) 4); + rate.hashInto(sink); + } + } + + @Override + public int memSize() { + return MEM_SIZE_OBJECT + extensionData.memSize() + Base.memSize(type) + Base.memSize(dose) + Base.memSize(rate); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof DoseAndRate that && + extensionData.equals(that.extensionData) && + Objects.equals(type, that.type) && + Objects.equals(dose, that.dose) && + Objects.equals(rate, that.rate); + } + + @Override + public int hashCode() { + int result = extensionData.hashCode(); + result = 31 * result + Objects.hashCode(type); + result = 31 * result + Objects.hashCode(dose); + result = 31 * result + Objects.hashCode(rate); + return result; + } + + @Override + public java.lang.String toString() { + return "Dosage.DoseAndRate{" + + extensionData + + ", type=" + type + + ", dose=" + dose + + ", rate=" + rate + + '}'; + } + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Duration.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Duration.java new file mode 100644 index 000000000..8dc7ca762 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Duration.java @@ -0,0 +1,71 @@ +package blaze.fhir.spec.type; + +import blaze.Interner; +import blaze.Interners; +import clojure.lang.IPersistentMap; +import clojure.lang.Keyword; +import clojure.lang.RT; + +@SuppressWarnings("DuplicatedCode") +public final class Duration extends AbstractQuantity { + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "Duration"); + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueDuration"); + + private static final Duration EMPTY = new Duration(ExtensionData.EMPTY, null, null, null, null, null); + + private static final Interner INTERNER = Interners.weakInterner( + k -> new Duration(k.extensionData(), k.value(), k.comparator(), k.unit(), k.system(), k.code()) + ); + + private Duration(ExtensionData extensionData, Decimal value, Code comparator, String unit, Uri system, Code code) { + super(extensionData, value, comparator, unit, system, code); + } + + private static Duration maybeIntern(ExtensionData extensionData, Decimal value, Code comparator, String unit, + Uri system, Code code) { + return extensionData.isInterned() && Base.isInterned(value) && Base.isInterned(comparator) && + Base.isInterned(unit) && Base.isInterned(system) && Base.isInterned(code) + ? INTERNER.intern(new InternerKey(extensionData, value, comparator, unit, system, code)) + : new Duration(extensionData, value, comparator, unit, system, code); + } + + public static Duration create(IPersistentMap m) { + return maybeIntern(ExtensionData.fromMap(m), (Decimal) m.valAt(VALUE), (Code) m.valAt(COMPARATOR), + (String) m.valAt(UNIT), (Uri) m.valAt(SYSTEM), (Code) m.valAt(CODE)); + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE; + return super.valAt(key, notFound); + } + + @Override + public Duration empty() { + return EMPTY; + } + + @Override + public Duration assoc(Object key, Object val) { + if (key == VALUE) return maybeIntern(extensionData, (Decimal) val, comparator, unit, system, code); + if (key == COMPARATOR) return maybeIntern(extensionData, value, (Code) val, unit, system, code); + if (key == UNIT) return maybeIntern(extensionData, value, comparator, (String) val, system, code); + if (key == SYSTEM) return maybeIntern(extensionData, value, comparator, unit, (Uri) val, code); + if (key == CODE) return maybeIntern(extensionData, value, comparator, unit, system, (Code) val); + if (key == EXTENSION) + return maybeIntern(extensionData.withExtension(val), value, comparator, unit, system, code); + if (key == ID) return maybeIntern(extensionData.withId(val), value, comparator, unit, system, code); + return this; + } + + @Override + public Duration withMeta(IPersistentMap meta) { + return maybeIntern(extensionData.withMeta(meta), value, comparator, unit, system, code); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Element.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Element.java new file mode 100644 index 000000000..eb32080c9 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Element.java @@ -0,0 +1,31 @@ +package blaze.fhir.spec.type; + +import clojure.lang.ILookupThunk; +import clojure.lang.Keyword; + +import java.lang.String; +import java.util.List; + +public interface Element extends Base { + + String id(); + + List extension(); + + @Override + default ILookupThunk getLookupThunk(Keyword key) { + if (key == EXTENSION) return new ILookupThunk() { + @Override + public Object get(Object target) { + return target instanceof Element p ? p.extension() : this; + } + }; + if (key == ID) return new ILookupThunk() { + @Override + public Object get(Object target) { + return target instanceof Element p ? p.id() : this; + } + }; + return Base.super.getLookupThunk(key); + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Expression.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Expression.java new file mode 100644 index 000000000..6eb2eddce --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Expression.java @@ -0,0 +1,242 @@ +package blaze.fhir.spec.type; + +import clojure.lang.*; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.util.Iterator; +import java.util.Objects; + +import static blaze.fhir.spec.type.Base.appendElement; + +@SuppressWarnings("DuplicatedCode") +public final class Expression extends AbstractElement implements Complex, ExtensionValue { + + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - extension data reference + * 4 byte - description reference + * 4 byte - name reference + * 4 byte - language reference + * 4 byte - expression reference + * 4 byte - reference reference + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 24; + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "Expression"); + + private static final Keyword DESCRIPTION = RT.keyword(null, "description"); + private static final Keyword NAME = RT.keyword(null, "name"); + private static final Keyword LANGUAGE = RT.keyword(null, "language"); + private static final Keyword EXPRESSION = RT.keyword(null, "expression"); + private static final Keyword REFERENCE = RT.keyword(null, "reference"); + + private static final Keyword[] FIELDS = {ID, EXTENSION, DESCRIPTION, NAME, LANGUAGE, EXPRESSION, REFERENCE}; + + private static final FieldName FIELD_NAME_DESCRIPTION = FieldName.of("description"); + private static final FieldName FIELD_NAME_NAME = FieldName.of("name"); + private static final FieldName FIELD_NAME_LANGUAGE = FieldName.of("language"); + private static final FieldName FIELD_NAME_EXPRESSION = FieldName.of("expression"); + private static final FieldName FIELD_NAME_REFERENCE = FieldName.of("reference"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueExpression"); + + private static final byte HASH_MARKER = 55; + + private static final Expression EMPTY = new Expression(ExtensionData.EMPTY, null, null, null, null, null); + + private final String description; + private final Id name; + private final Code language; + private final String expression; + private final Uri reference; + + private Expression(ExtensionData extensionData, String description, Id name, Code language, String expression, + Uri reference) { + super(extensionData); + this.description = description; + this.name = name; + this.language = language; + this.expression = expression; + this.reference = reference; + } + + public static Expression create(IPersistentMap m) { + return new Expression(ExtensionData.fromMap(m), (String) m.valAt(DESCRIPTION), (Id) m.valAt(NAME), + (Code) m.valAt(LANGUAGE), (String) m.valAt(EXPRESSION), (Uri) m.valAt(REFERENCE)); + } + + public String description() { + return description; + } + + public Id name() { + return name; + } + + public Code language() { + return language; + } + + public String expression() { + return expression; + } + + public Uri reference() { + return reference; + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE; + if (key == DESCRIPTION) return description; + if (key == NAME) return name; + if (key == LANGUAGE) return language; + if (key == EXPRESSION) return expression; + if (key == REFERENCE) return reference; + return super.valAt(key, notFound); + } + + @Override + public ISeq seq() { + ISeq seq = PersistentList.EMPTY; + seq = appendElement(seq, REFERENCE, reference); + seq = appendElement(seq, EXPRESSION, expression); + seq = appendElement(seq, LANGUAGE, language); + seq = appendElement(seq, NAME, name); + seq = appendElement(seq, DESCRIPTION, description); + return extensionData.append(seq); + } + + @Override + public Expression empty() { + return EMPTY; + } + + @Override + public Iterator> iterator() { + return new BaseIterator(this, FIELDS); + } + + @Override + public Expression assoc(Object key, Object val) { + if (key == DESCRIPTION) + return new Expression(extensionData, (String) val, name, language, expression, reference); + if (key == NAME) + return new Expression(extensionData, description, (Id) val, language, expression, reference); + if (key == LANGUAGE) + return new Expression(extensionData, description, name, (Code) val, expression, reference); + if (key == EXPRESSION) + return new Expression(extensionData, description, name, language, (String) val, reference); + if (key == REFERENCE) + return new Expression(extensionData, description, name, language, expression, (Uri) val); + if (key == EXTENSION) + return new Expression(extensionData.withExtension(val), description, name, language, expression, reference); + if (key == ID) + return new Expression(extensionData.withId(val), description, name, language, expression, reference); + return this; + } + + @Override + public Expression withMeta(IPersistentMap meta) { + return new Expression(extensionData.withMeta(meta), description, name, language, expression, reference); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeAsJsonValue(JsonGenerator generator) throws IOException { + generator.writeStartObject(); + serializeJsonBase(generator); + if (description != null) { + description.serializeAsJsonProperty(generator, FIELD_NAME_DESCRIPTION); + } + if (name != null) { + name.serializeAsJsonProperty(generator, FIELD_NAME_NAME); + } + if (language != null) { + language.serializeAsJsonProperty(generator, FIELD_NAME_LANGUAGE); + } + if (expression != null) { + expression.serializeAsJsonProperty(generator, FIELD_NAME_EXPRESSION); + } + if (reference != null) { + reference.serializeAsJsonProperty(generator, FIELD_NAME_REFERENCE); + } + generator.writeEndObject(); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (description != null) { + sink.putByte((byte) 2); + description.hashInto(sink); + } + if (name != null) { + sink.putByte((byte) 3); + name.hashInto(sink); + } + if (language != null) { + sink.putByte((byte) 4); + language.hashInto(sink); + } + if (expression != null) { + sink.putByte((byte) 5); + expression.hashInto(sink); + } + if (reference != null) { + sink.putByte((byte) 6); + reference.hashInto(sink); + } + } + + @Override + public int memSize() { + return MEM_SIZE_OBJECT + extensionData.memSize() + Base.memSize(description) + Base.memSize(name) + + Base.memSize(language) + Base.memSize(expression) + Base.memSize(reference); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof Expression that && + extensionData.equals(that.extensionData) && + Objects.equals(description, that.description) && + Objects.equals(name, that.name) && + Objects.equals(language, that.language) && + Objects.equals(expression, that.expression) && + Objects.equals(reference, that.reference); + } + + @Override + public int hashCode() { + int result = extensionData.hashCode(); + result = 31 * result + Objects.hashCode(description); + result = 31 * result + Objects.hashCode(name); + result = 31 * result + Objects.hashCode(language); + result = 31 * result + Objects.hashCode(expression); + result = 31 * result + Objects.hashCode(reference); + return result; + } + + @Override + public java.lang.String toString() { + return "Expression{" + + extensionData + + ", description=" + description + + ", name=" + name + + ", language=" + language + + ", expression=" + expression + + ", reference=" + reference + + '}'; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Extension.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Extension.java new file mode 100644 index 000000000..e37914b6b --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Extension.java @@ -0,0 +1,198 @@ +package blaze.fhir.spec.type; + +import blaze.Interner; +import blaze.Interners; +import clojure.lang.*; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.io.SerializedString; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.lang.String; +import java.util.Iterator; +import java.util.Objects; +import java.util.stream.Stream; + +import static blaze.fhir.spec.type.Base.appendElement; +import static java.util.Objects.requireNonNull; + +@SuppressWarnings("DuplicatedCode") +public final class Extension extends AbstractElement implements Complex { + + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - extension data reference + * 4 byte - url reference + * 4 byte - value reference + * 1 byte - interned boolean + * 3 byte - padding + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 16; + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "Extension"); + + private static final Keyword URL = RT.keyword(null, "url"); + + private static final Keyword[] FIELDS = {ID, EXTENSION, URL, VALUE}; + + private static final FieldName FIELD_NAME_URL = FieldName.of("url"); + + private static final byte HASH_MARKER = 39; + + private static final Interner URL_INTERNER = Interners.weakInterner(SerializedString::new); + private static final Interner INTERNER = Interners.weakInterner( + k -> new Extension(k.extensionData, k.url, k.value, true) + ); + private static final Extension EMPTY = new Extension(ExtensionData.EMPTY, null, null, true); + + private final SerializedString url; + private final ExtensionValue value; + private final boolean interned; + + private Extension(ExtensionData extensionData, SerializedString url, ExtensionValue value, boolean interned) { + super(extensionData); + this.url = url; + this.value = value; + this.interned = interned; + } + + private static Extension maybeIntern(ExtensionData extensionData, SerializedString url, ExtensionValue value) { + return extensionData.isInterned() && Base.isInterned(value) + ? INTERNER.intern(new InternerKey(extensionData, url, value)) + : new Extension(extensionData, url, value, false); + } + + public static Extension create(IPersistentMap m) { + var url = (String) m.valAt(URL); + return maybeIntern(ExtensionData.fromMap(m), url == null ? null : URL_INTERNER.intern(url), + (ExtensionValue) m.valAt(VALUE)); + } + + @Override + public boolean isInterned() { + return interned; + } + + public String url() { + return url == null ? null : url.getValue(); + } + + public Base value() { + return value; + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE; + if (key == VALUE) return value; + if (key == URL) return url(); + return super.valAt(key, notFound); + } + + @Override + public ISeq seq() { + ISeq seq = PersistentList.EMPTY; + seq = appendElement(seq, VALUE, value); + seq = appendElement(seq, URL, url()); + return extensionData.append(seq); + } + + @Override + public Extension empty() { + return EMPTY; + } + + @Override + public Iterator> iterator() { + return new BaseIterator(this, FIELDS); + } + + @Override + public Extension assoc(Object key, Object val) { + if (key == VALUE) return maybeIntern(extensionData, url, (ExtensionValue) val); + if (key == URL) + return maybeIntern(extensionData, val == null ? null : URL_INTERNER.intern((String) val), value); + if (key == EXTENSION) return maybeIntern(extensionData.withExtension(val), url, value); + if (key == ID) return maybeIntern(extensionData.withId(val), url, value); + return this; + } + + @Override + public Extension withMeta(IPersistentMap meta) { + return maybeIntern(extensionData.withMeta(meta), url, value); + } + + @Override + public void serializeAsJsonValue(JsonGenerator generator) throws IOException { + generator.writeStartObject(); + serializeJsonBase(generator); + if (url != null) { + generator.writeFieldName(FIELD_NAME_URL.normal()); + generator.writeString(url); + } + if (value != null) { + value.serializeJsonField(generator, value.fieldNameExtensionValue()); + } + generator.writeEndObject(); + } + + @Override + @SuppressWarnings({"UnstableApiUsage"}) + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (url != null) { + sink.putByte((byte) 2); + // for compatibility reasons, we use the hash signature of a FHIR.String instead of a System.String + blaze.fhir.spec.type.String.hashIntoValue(sink, url()); + } + if (value != null) { + sink.putByte((byte) 3); + value.hashInto(sink); + } + } + + @Override + public Stream references() { + return value == null ? super.references() : Stream.concat(super.references(), value.references()); + } + + @Override + public int memSize() { + return isInterned() ? 0 : MEM_SIZE_OBJECT + extensionData.memSize() + Base.memSize(value); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof Extension that && + extensionData.equals(that.extensionData) && + Objects.equals(url, that.url) && + Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + int result = extensionData.hashCode(); + result = 31 * result + Objects.hashCode(url); + result = 31 * result + Objects.hashCode(value); + return result; + } + + @Override + public String toString() { + return "Extension{" + + extensionData + + ", url=" + (url == null ? null : '\'' + url() + '\'') + + ", value=" + value + + '}'; + } + + private record InternerKey(ExtensionData extensionData, SerializedString url, ExtensionValue value) { + private InternerKey { + requireNonNull(extensionData); + } + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/ExtensionData.java b/modules/fhir-structure/java/blaze/fhir/spec/type/ExtensionData.java new file mode 100644 index 000000000..17ed2272d --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/ExtensionData.java @@ -0,0 +1,146 @@ +package blaze.fhir.spec.type; + +import blaze.Interner; +import blaze.Interners; +import blaze.fhir.spec.type.system.Strings; +import clojure.lang.IPersistentMap; +import clojure.lang.ISeq; +import clojure.lang.PersistentArrayMap; +import clojure.lang.PersistentVector; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.lang.String; +import java.util.List; +import java.util.Objects; +import java.util.stream.Stream; + +import static blaze.fhir.spec.type.Base.*; +import static blaze.fhir.spec.type.Complex.serializeJsonComplexList; +import static java.util.Objects.requireNonNull; + +public final class ExtensionData { + + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - id reference + * 4 byte - extension reference + * 4 byte - meta reference + * 4 byte - padding + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 16; + + private static final FieldName FIELD_NAME_ID = FieldName.of("id"); + private static final FieldName FIELD_NAME_EXTENSION = FieldName.of("extension"); + + final String id; + final List extension; + final IPersistentMap meta; + + private static final Interner, ExtensionData> INTERNER = Interners.weakInterner(k -> new ExtensionData(null, k, null)); + + @SuppressWarnings("unchecked") + public static final ExtensionData EMPTY = new ExtensionData(null, PersistentVector.EMPTY, null); + + private ExtensionData(String id, List extension, IPersistentMap meta) { + this.id = id; + this.extension = requireNonNull(extension); + this.meta = meta; + } + + private static ExtensionData maybeIntern(String id, List extension, IPersistentMap meta) { + if (id == null && (meta == null || meta.count() == 0)) { + if (extension.isEmpty()) return EMPTY; + if (Base.areAllInterned(extension)) return INTERNER.intern(extension); + } + return new ExtensionData(id, extension, meta); + } + + static ExtensionData fromMap(IPersistentMap m) { + return maybeIntern((String) m.valAt(ID), Base.listFrom(m, EXTENSION), null); + } + + Object valAt(Object key, Object notFound) { + if (key == EXTENSION) return extension; + if (key == ID) return id; + return notFound; + } + + boolean isNotEmpty() { + return this != EMPTY; + } + + boolean isInterned() { + return this == EMPTY || (id == null && Base.areAllInterned(extension) && (meta == null || meta.count() == 0)); + } + + Stream references() { + return extension.stream().flatMap(Extension::references); + } + + int memSize() { + return isInterned() ? 0 : MEM_SIZE_OBJECT + (id == null ? 0 : Strings.memSize(id)) + Base.memSize(extension) + + (meta == null ? 0 : Base.memSize(meta)); + } + + ExtensionData withId(Object id) { + return maybeIntern((String) id, extension, meta); + } + + ExtensionData withExtension(Object extension) { + return maybeIntern(id, Lists.nullToEmpty(extension), meta); + } + + ExtensionData withMeta(IPersistentMap meta) { + return maybeIntern(id, extension, meta); + } + + ISeq append(ISeq seq) { + seq = appendElement(seq, EXTENSION, extension); + seq = appendElement(seq, ID, id); + return seq.count() == 0 ? null : seq; + } + + void serializeJson(JsonGenerator generator) throws IOException { + if (id != null) { + generator.writeFieldName(FIELD_NAME_ID.normal()); + generator.writeString(id); + } + if (!extension.isEmpty()) { + serializeJsonComplexList(extension, generator, FIELD_NAME_EXTENSION.normal()); + } + } + + @SuppressWarnings("UnstableApiUsage") + void hashInto(PrimitiveSink sink) { + if (id != null) { + sink.putByte((byte) 0); + Strings.hashInto(id, sink); + } + if (!extension.isEmpty()) { + sink.putByte((byte) 1); + Base.hashIntoList(extension, sink); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof ExtensionData that && + Objects.equals(id, that.id) && + extension.equals(that.extension); + } + + @Override + public int hashCode() { + return 31 * Objects.hashCode(id) + extension.hashCode(); + } + + @Override + public String toString() { + return "id=" + (id == null ? null : '\'' + id + '\'') + ", extension=" + extension; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/ExtensionValue.java b/modules/fhir-structure/java/blaze/fhir/spec/type/ExtensionValue.java new file mode 100644 index 000000000..695781399 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/ExtensionValue.java @@ -0,0 +1,6 @@ +package blaze.fhir.spec.type; + +public interface ExtensionValue extends Element { + + FieldName fieldNameExtensionValue(); +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/FieldName.java b/modules/fhir-structure/java/blaze/fhir/spec/type/FieldName.java new file mode 100644 index 000000000..f7bde00ba --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/FieldName.java @@ -0,0 +1,24 @@ +package blaze.fhir.spec.type; + +import blaze.Interner; +import blaze.Interners; +import com.fasterxml.jackson.core.SerializableString; +import com.fasterxml.jackson.core.io.SerializedString; + +import static java.util.Objects.requireNonNull; + +public record FieldName(SerializableString normal, SerializableString extended) { + + private static final Interner INTERNER = Interners.strongInterner( + name -> new FieldName(new SerializedString(name), new SerializedString("_" + name)) + ); + + public FieldName { + requireNonNull(normal); + requireNonNull(extended); + } + + public static FieldName of(java.lang.String name) { + return INTERNER.intern(requireNonNull(name)); + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/HumanName.java b/modules/fhir-structure/java/blaze/fhir/spec/type/HumanName.java new file mode 100644 index 000000000..96230d323 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/HumanName.java @@ -0,0 +1,296 @@ +package blaze.fhir.spec.type; + +import clojure.lang.*; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; + +import static blaze.fhir.spec.type.Base.appendElement; +import static blaze.fhir.spec.type.Primitive.serializeJsonPrimitiveList; +import static java.util.Objects.requireNonNull; + +@SuppressWarnings("DuplicatedCode") +public final class HumanName extends AbstractElement implements Complex, ExtensionValue { + + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - extension data reference + * 4 byte - use reference + * 4 byte - text reference + * 4 byte - family reference + * 4 byte - given reference + * 4 byte - prefix reference + * 4 byte - suffix reference + * 4 byte - period reference + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 32; + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "HumanName"); + + private static final Keyword USE = RT.keyword(null, "use"); + private static final Keyword TEXT = RT.keyword(null, "text"); + private static final Keyword FAMILY = RT.keyword(null, "family"); + private static final Keyword GIVEN = RT.keyword(null, "given"); + private static final Keyword PREFIX = RT.keyword(null, "prefix"); + private static final Keyword SUFFIX = RT.keyword(null, "suffix"); + private static final Keyword PERIOD = RT.keyword(null, "period"); + + private static final Keyword[] FIELDS = {ID, EXTENSION, USE, TEXT, FAMILY, GIVEN, PREFIX, SUFFIX, PERIOD}; + + private static final FieldName FIELD_NAME_USE = FieldName.of("use"); + private static final FieldName FIELD_NAME_TEXT = FieldName.of("text"); + private static final FieldName FIELD_NAME_FAMILY = FieldName.of("family"); + private static final FieldName FIELD_NAME_GIVEN = FieldName.of("given"); + private static final FieldName FIELD_NAME_PREFIX = FieldName.of("prefix"); + private static final FieldName FIELD_NAME_SUFFIX = FieldName.of("suffix"); + private static final FieldName FIELD_NAME_PERIOD = FieldName.of("period"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueHumanName"); + + private static final byte HASH_MARKER = 46; + + @SuppressWarnings("unchecked") + private static final HumanName EMPTY = new HumanName(ExtensionData.EMPTY, null, null, null, PersistentVector.EMPTY, + PersistentVector.EMPTY, PersistentVector.EMPTY, null); + + private final Code use; + private final String text; + private final String family; + private final List given; + private final List prefix; + private final List suffix; + private final Period period; + + private HumanName(ExtensionData extensionData, Code use, String text, String family, List given, + List prefix, List suffix, Period period) { + super(extensionData); + this.use = use; + this.text = text; + this.family = family; + this.given = requireNonNull(given); + this.prefix = requireNonNull(prefix); + this.suffix = requireNonNull(suffix); + this.period = period; + } + + public static HumanName create(IPersistentMap m) { + return new HumanName(ExtensionData.fromMap(m), (Code) m.valAt(USE), (String) m.valAt(TEXT), + (String) m.valAt(FAMILY), Base.listFrom(m, GIVEN), Base.listFrom(m, PREFIX), Base.listFrom(m, SUFFIX), + (Period) m.valAt(PERIOD)); + } + + public Code use() { + return use; + } + + public String text() { + return text; + } + + public String family() { + return family; + } + + public List given() { + return given; + } + + public List prefix() { + return prefix; + } + + public List suffix() { + return suffix; + } + + public Period period() { + return period; + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE; + if (key == USE) return use; + if (key == TEXT) return text; + if (key == FAMILY) return family; + if (key == GIVEN) return given; + if (key == PREFIX) return prefix; + if (key == SUFFIX) return suffix; + if (key == PERIOD) return period; + return super.valAt(key, notFound); + } + + @Override + public ISeq seq() { + ISeq seq = PersistentList.EMPTY; + seq = appendElement(seq, PERIOD, period); + if (!suffix.isEmpty()) { + seq = appendElement(seq, SUFFIX, suffix); + } + if (!prefix.isEmpty()) { + seq = appendElement(seq, PREFIX, prefix); + } + if (!given.isEmpty()) { + seq = appendElement(seq, GIVEN, given); + } + seq = appendElement(seq, FAMILY, family); + seq = appendElement(seq, TEXT, text); + seq = appendElement(seq, USE, use); + return extensionData.append(seq); + } + + @Override + public HumanName empty() { + return EMPTY; + } + + @Override + public Iterator> iterator() { + return new BaseIterator(this, FIELDS); + } + + @Override + public HumanName assoc(Object key, Object val) { + if (key == USE) return new HumanName(extensionData, (Code) val, text, family, given, prefix, suffix, period); + if (key == TEXT) return new HumanName(extensionData, use, (String) val, family, given, prefix, suffix, period); + if (key == FAMILY) return new HumanName(extensionData, use, text, (String) val, given, prefix, suffix, period); + if (key == GIVEN) + return new HumanName(extensionData, use, text, family, Lists.nullToEmpty(val), prefix, suffix, period); + if (key == PREFIX) + return new HumanName(extensionData, use, text, family, given, Lists.nullToEmpty(val), suffix, period); + if (key == SUFFIX) + return new HumanName(extensionData, use, text, family, given, prefix, Lists.nullToEmpty(val), period); + if (key == PERIOD) return new HumanName(extensionData, use, text, family, given, prefix, suffix, (Period) val); + if (key == EXTENSION) + return new HumanName(extensionData.withExtension(val), use, text, family, given, prefix, suffix, period); + if (key == ID) + return new HumanName(extensionData.withId(val), use, text, family, given, prefix, suffix, period); + return this; + } + + @Override + public HumanName withMeta(IPersistentMap meta) { + return new HumanName(extensionData.withMeta(meta), use, text, family, given, prefix, suffix, period); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeAsJsonValue(JsonGenerator generator) throws IOException { + generator.writeStartObject(); + serializeJsonBase(generator); + if (use != null) { + use.serializeAsJsonProperty(generator, FIELD_NAME_USE); + } + if (text != null) { + text.serializeAsJsonProperty(generator, FIELD_NAME_TEXT); + } + if (family != null) { + family.serializeAsJsonProperty(generator, FIELD_NAME_FAMILY); + } + if (!given.isEmpty()) { + serializeJsonPrimitiveList(given, generator, FIELD_NAME_GIVEN); + } + if (!prefix.isEmpty()) { + serializeJsonPrimitiveList(prefix, generator, FIELD_NAME_PREFIX); + } + if (!suffix.isEmpty()) { + serializeJsonPrimitiveList(suffix, generator, FIELD_NAME_SUFFIX); + } + if (period != null) { + period.serializeJsonField(generator, FIELD_NAME_PERIOD); + } + generator.writeEndObject(); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (use != null) { + sink.putByte((byte) 2); + use.hashInto(sink); + } + if (text != null) { + sink.putByte((byte) 3); + text.hashInto(sink); + } + if (family != null) { + sink.putByte((byte) 4); + family.hashInto(sink); + } + if (!given.isEmpty()) { + sink.putByte((byte) 5); + Base.hashIntoList(given, sink); + } + if (!prefix.isEmpty()) { + sink.putByte((byte) 6); + Base.hashIntoList(prefix, sink); + } + if (!suffix.isEmpty()) { + sink.putByte((byte) 7); + Base.hashIntoList(suffix, sink); + } + if (period != null) { + sink.putByte((byte) 8); + period.hashInto(sink); + } + } + + @Override + public int memSize() { + return MEM_SIZE_OBJECT + extensionData.memSize() + Base.memSize(use) + Base.memSize(text) + Base.memSize(family) + + Base.memSize(given) + Base.memSize(prefix) + Base.memSize(suffix) + Base.memSize(period); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof HumanName that && + extensionData.equals(that.extensionData) && + Objects.equals(use, that.use) && + Objects.equals(text, that.text) && + Objects.equals(family, that.family) && + given.equals(that.given) && + prefix.equals(that.prefix) && + suffix.equals(that.suffix) && + Objects.equals(period, that.period); + } + + @Override + public int hashCode() { + int result = extensionData.hashCode(); + result = 31 * result + Objects.hashCode(use); + result = 31 * result + Objects.hashCode(text); + result = 31 * result + Objects.hashCode(family); + result = 31 * result + given.hashCode(); + result = 31 * result + prefix.hashCode(); + result = 31 * result + suffix.hashCode(); + result = 31 * result + Objects.hashCode(period); + return result; + } + + @Override + public java.lang.String toString() { + return "HumanName{" + + extensionData + + ", use=" + use + + ", text=" + text + + ", family=" + family + + ", given=" + given + + ", prefix=" + prefix + + ", suffix=" + suffix + + ", period=" + period + + '}'; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Id.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Id.java new file mode 100644 index 000000000..b3fb64616 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Id.java @@ -0,0 +1,121 @@ +package blaze.fhir.spec.type; + +import blaze.Interner; +import blaze.Interners; +import blaze.fhir.spec.type.system.Strings; +import clojure.lang.IPersistentMap; +import clojure.lang.Keyword; +import clojure.lang.RT; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.lang.String; +import java.util.Objects; + +@SuppressWarnings("DuplicatedCode") +public final class Id extends PrimitiveElement { + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "id"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueId"); + + private static final byte HASH_MARKER = 15; + + private static final Interner INTERNER = Interners.weakInterner(k -> new Id(k, null)); + private static final Id EMPTY = new Id(ExtensionData.EMPTY, null); + + private final String value; + + private Id(ExtensionData extensionData, String value) { + super(extensionData); + this.value = value; + } + + private static Id maybeIntern(ExtensionData extensionData, String value) { + return extensionData.isInterned() && value == null ? INTERNER.intern(extensionData) : new Id(extensionData, value); + } + + public static Id create(String value) { + return value == null ? EMPTY : new Id(ExtensionData.EMPTY, value); + } + + public static Id create(IPersistentMap m) { + return maybeIntern(ExtensionData.fromMap(m), (String) m.valAt(VALUE)); + } + + public String value() { + return value; + } + + @Override + public Object valAt(Object key, Object notFound) { + return key == FHIR_TYPE_KEY ? FHIR_TYPE : super.valAt(key, notFound); + } + + @Override + public Id empty() { + return EMPTY; + } + + @Override + public Id assoc(Object key, Object val) { + if (key == VALUE) return maybeIntern(extensionData, (String) val); + if (key == EXTENSION) return maybeIntern(extensionData.withExtension(val), value); + if (key == ID) return maybeIntern(extensionData.withId(val), value); + return this; + } + + @Override + public Id withMeta(IPersistentMap meta) { + return maybeIntern(extensionData.withMeta(meta), value); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeJsonPrimitiveValue(JsonGenerator generator) throws IOException { + if (hasValue()) { + generator.writeString(value); + } else { + generator.writeNull(); + } + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (value != null) { + sink.putByte((byte) 2); + Strings.hashInto(value, sink); + } + } + + @Override + public int memSize() { + return super.memSize() + Strings.memSize(value); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof Id that && + extensionData.equals(that.extensionData) && + Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return 31 * extensionData.hashCode() + Objects.hashCode(value); + } + + @Override + public String toString() { + return "Id{" + extensionData + ", value=" + (value == null ? null : '\'' + value + '\'') + '}'; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Identifier.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Identifier.java new file mode 100644 index 000000000..cac9230f9 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Identifier.java @@ -0,0 +1,261 @@ +package blaze.fhir.spec.type; + +import clojure.lang.*; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.util.Iterator; +import java.util.Objects; + +import static blaze.fhir.spec.type.Base.appendElement; + +@SuppressWarnings("DuplicatedCode") +public final class Identifier extends AbstractElement implements Complex, ExtensionValue { + + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - extension data reference + * 4 byte - use reference + * 4 byte - type reference + * 4 byte - system reference + * 4 byte - value reference + * 4 byte - period reference + * 4 byte - assigner reference + * 4 byte - padding + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 32; + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "Identifier"); + + private static final Keyword USE = RT.keyword(null, "use"); + private static final Keyword TYPE = RT.keyword(null, "type"); + private static final Keyword SYSTEM = RT.keyword(null, "system"); + private static final Keyword VALUE = RT.keyword(null, "value"); + private static final Keyword PERIOD = RT.keyword(null, "period"); + private static final Keyword ASSIGNER = RT.keyword(null, "assigner"); + + private static final Keyword[] FIELDS = {ID, EXTENSION, USE, TYPE, SYSTEM, VALUE, PERIOD, ASSIGNER}; + + private static final FieldName FIELD_NAME_USE = FieldName.of("use"); + private static final FieldName FIELD_NAME_TYPE = FieldName.of("type"); + private static final FieldName FIELD_NAME_SYSTEM = FieldName.of("system"); + private static final FieldName FIELD_NAME_VALUE = FieldName.of("value"); + private static final FieldName FIELD_NAME_PERIOD = FieldName.of("period"); + private static final FieldName FIELD_NAME_ASSIGNER = FieldName.of("assigner"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueIdentifier"); + + private static final byte HASH_MARKER = 42; + + private static final Identifier EMPTY = new Identifier(ExtensionData.EMPTY, null, null, null, null, null, null); + + private final Code use; + private final CodeableConcept type; + private final Uri system; + private final String value; + private final Period period; + private final Reference assigner; + + private Identifier(ExtensionData extensionData, Code use, CodeableConcept type, Uri system, String value, + Period period, Reference assigner) { + super(extensionData); + this.use = use; + this.type = type; + this.system = system; + this.value = value; + this.period = period; + this.assigner = assigner; + } + + public static Identifier create(IPersistentMap m) { + return new Identifier(ExtensionData.fromMap(m), (Code) m.valAt(USE), + (CodeableConcept) m.valAt(TYPE), (Uri) m.valAt(SYSTEM), (String) m.valAt(VALUE), (Period) m.valAt(PERIOD), + (Reference) m.valAt(ASSIGNER)); + } + + public Code use() { + return use; + } + + public CodeableConcept type() { + return type; + } + + public Uri system() { + return system; + } + + public String value() { + return value; + } + + public Period period() { + return period; + } + + public Reference assigner() { + return assigner; + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE; + if (key == USE) return use; + if (key == TYPE) return type; + if (key == SYSTEM) return system; + if (key == VALUE) return value; + if (key == PERIOD) return period; + if (key == ASSIGNER) return assigner; + return super.valAt(key, notFound); + } + + @Override + public ISeq seq() { + ISeq seq = PersistentList.EMPTY; + seq = appendElement(seq, ASSIGNER, assigner); + seq = appendElement(seq, PERIOD, period); + seq = appendElement(seq, VALUE, value); + seq = appendElement(seq, SYSTEM, system); + seq = appendElement(seq, TYPE, type); + seq = appendElement(seq, USE, use); + return extensionData.append(seq); + } + + @Override + public Identifier empty() { + return EMPTY; + } + + @Override + public Iterator> iterator() { + return new BaseIterator(this, FIELDS); + } + + @Override + public Identifier assoc(Object key, Object val) { + if (key == USE) return new Identifier(extensionData, (Code) val, type, system, value, period, assigner); + if (key == TYPE) + return new Identifier(extensionData, use, (CodeableConcept) val, system, value, period, assigner); + if (key == SYSTEM) return new Identifier(extensionData, use, type, (Uri) val, value, period, assigner); + if (key == VALUE) return new Identifier(extensionData, use, type, system, (String) val, period, assigner); + if (key == PERIOD) return new Identifier(extensionData, use, type, system, value, (Period) val, assigner); + if (key == ASSIGNER) return new Identifier(extensionData, use, type, system, value, period, (Reference) val); + if (key == EXTENSION) + return new Identifier(extensionData.withExtension(val), use, type, system, value, period, assigner); + if (key == ID) return new Identifier(extensionData.withId(val), use, type, system, value, period, assigner); + return this; + } + + @Override + public Identifier withMeta(IPersistentMap meta) { + return new Identifier(extensionData.withMeta(meta), use, type, system, value, period, assigner); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeAsJsonValue(JsonGenerator generator) throws IOException { + generator.writeStartObject(); + serializeJsonBase(generator); + if (use != null) { + use.serializeAsJsonProperty(generator, FIELD_NAME_USE); + } + if (type != null) { + type.serializeJsonField(generator, FIELD_NAME_TYPE); + } + if (system != null) { + system.serializeAsJsonProperty(generator, FIELD_NAME_SYSTEM); + } + if (value != null) { + value.serializeAsJsonProperty(generator, FIELD_NAME_VALUE); + } + if (period != null) { + period.serializeJsonField(generator, FIELD_NAME_PERIOD); + } + if (assigner != null) { + assigner.serializeJsonField(generator, FIELD_NAME_ASSIGNER); + } + generator.writeEndObject(); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (use != null) { + sink.putByte((byte) 2); + use.hashInto(sink); + } + if (type != null) { + sink.putByte((byte) 3); + type.hashInto(sink); + } + if (system != null) { + sink.putByte((byte) 4); + system.hashInto(sink); + } + if (value != null) { + sink.putByte((byte) 5); + value.hashInto(sink); + } + if (period != null) { + sink.putByte((byte) 6); + period.hashInto(sink); + } + if (assigner != null) { + sink.putByte((byte) 7); + assigner.hashInto(sink); + } + } + + @Override + public int memSize() { + return MEM_SIZE_OBJECT + extensionData.memSize() + Base.memSize(use) + Base.memSize(type) + Base.memSize(system) + + Base.memSize(value) + Base.memSize(period); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof Identifier that && + extensionData.equals(that.extensionData) && + Objects.equals(use, that.use) && + Objects.equals(type, that.type) && + Objects.equals(system, that.system) && + Objects.equals(value, that.value) && + Objects.equals(period, that.period) && + Objects.equals(assigner, that.assigner); + } + + @Override + public int hashCode() { + int result = extensionData.hashCode(); + result = 31 * result + Objects.hashCode(use); + result = 31 * result + Objects.hashCode(type); + result = 31 * result + Objects.hashCode(system); + result = 31 * result + Objects.hashCode(value); + result = 31 * result + Objects.hashCode(period); + result = 31 * result + Objects.hashCode(assigner); + return result; + } + + @Override + public java.lang.String toString() { + return "Identifier{" + + extensionData + + ", use=" + use + + ", type=" + type + + ", system=" + system + + ", value=" + value + + ", period=" + period + + ", assigner=" + assigner + + '}'; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Instant.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Instant.java new file mode 100644 index 000000000..ed21e1821 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Instant.java @@ -0,0 +1,129 @@ +package blaze.fhir.spec.type; + +import blaze.Interner; +import blaze.Interners; +import blaze.fhir.spec.type.system.DateTimes; +import clojure.lang.IPersistentMap; +import clojure.lang.Keyword; +import clojure.lang.RT; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.lang.String; +import java.time.OffsetDateTime; +import java.util.Objects; + +@SuppressWarnings("DuplicatedCode") +public final class Instant extends PrimitiveElement { + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "instant"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueInstant"); + + private static final byte HASH_MARKER = 9; + + private static final Interner INTERNER = Interners.weakInterner(k -> new Instant(k, null)); + private static final Instant EMPTY = new Instant(ExtensionData.EMPTY, null); + + private final OffsetDateTime value; + + private Instant(ExtensionData extensionData, OffsetDateTime value) { + super(extensionData); + this.value = value; + } + + private static Instant maybeIntern(ExtensionData extensionData, OffsetDateTime value) { + return extensionData.isInterned() && value == null ? INTERNER.intern(extensionData) : new Instant(extensionData, value); + } + + public static Instant create(OffsetDateTime value) { + return value == null ? EMPTY : new Instant(ExtensionData.EMPTY, value); + } + + public static Instant create(IPersistentMap m) { + return maybeIntern(ExtensionData.fromMap(m), (OffsetDateTime) m.valAt(VALUE)); + } + + public OffsetDateTime value() { + return value; + } + + @Override + public Object valAt(Object key, Object notFound) { + return key == FHIR_TYPE_KEY ? FHIR_TYPE : super.valAt(key, notFound); + } + + @Override + public String valueAsString() { + return value == null ? null : DateTimes.toString(value); + } + + @Override + public Instant empty() { + return EMPTY; + } + + @Override + public Instant assoc(Object key, Object val) { + if (key == VALUE) return maybeIntern(extensionData, (OffsetDateTime) val); + if (key == EXTENSION) return maybeIntern(extensionData.withExtension(val), value); + if (key == ID) return maybeIntern(extensionData.withId(val), value); + return this; + } + + @Override + public Instant withMeta(IPersistentMap meta) { + return maybeIntern(extensionData.withMeta(meta), value); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeJsonPrimitiveValue(JsonGenerator generator) throws IOException { + if (hasValue()) { + var appendable = new AsciiByteArrayAppendable(35); + DateTimes.DATE_TIME.formatTo(value, appendable); + generator.writeRawUTF8String(appendable.toByteArray(), 0, appendable.length()); + } else { + generator.writeNull(); + } + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (value != null) { + sink.putByte((byte) 2); + DateTimes.hashInto(value, sink); + } + } + + @Override + public int memSize() { + return super.memSize() + DateTimes.memSize(value); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof Instant that && + extensionData.equals(that.extensionData) && + Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return 31 * extensionData.hashCode() + Objects.hashCode(value); + } + + @Override + public String toString() { + return "Instant{" + extensionData + ", value=" + value + '}'; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Integer.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Integer.java new file mode 100644 index 000000000..fad2be125 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Integer.java @@ -0,0 +1,151 @@ +package blaze.fhir.spec.type; + +import blaze.Interner; +import blaze.Interners; +import blaze.fhir.spec.type.system.Integers; +import clojure.lang.IPersistentMap; +import clojure.lang.Keyword; +import clojure.lang.RT; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.lang.String; +import java.util.Objects; + +@SuppressWarnings("DuplicatedCode") +public final class Integer extends PrimitiveElement { + + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - extension data reference + * 4 byte - int value + * 1 byte - boolean value + * 7 byte - padding + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 16; + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "integer"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueInteger"); + + private static final byte HASH_MARKER = 1; + + private static final Interner INTERNER = Interners.weakInterner(k -> new Integer(k, 0, true)); + private static final Integer EMPTY = new Integer(ExtensionData.EMPTY, 0, true); + + private final int value; + private final boolean isValueNull; + + private Integer(ExtensionData extensionData, int value, boolean isValueNull) { + super(extensionData); + this.value = value; + this.isValueNull = isValueNull; + } + + private static Integer maybeIntern(ExtensionData extensionData, int value, boolean isValueNull) { + return extensionData.isInterned() && isValueNull + ? INTERNER.intern(extensionData) + : new Integer(extensionData, value, isValueNull); + } + + private static int toInt(Number value) { + long val = value.longValue(); + if ((int) val != val) { + throw new IllegalArgumentException("Invalid integer value `%d`.".formatted(val)); + } + return (int) val; + } + + public static Integer create(Number value) { + return value == null ? EMPTY : new Integer(ExtensionData.EMPTY, toInt(value), false); + } + + public static Integer create(IPersistentMap m) { + var value = (Number) m.valAt(VALUE); + return maybeIntern(ExtensionData.fromMap(m), value == null ? 0 : toInt(value), value == null); + } + + @Override + public boolean hasValue() { + return !isValueNull; + } + + @Override + public java.lang.Integer value() { + return isValueNull ? null : value; + } + + @Override + public Object valAt(Object key, Object notFound) { + return key == FHIR_TYPE_KEY ? FHIR_TYPE : super.valAt(key, notFound); + } + + @Override + public Integer empty() { + return EMPTY; + } + + @Override + public Integer assoc(Object key, Object val) { + if (key == VALUE) return maybeIntern(extensionData, val == null ? 0 : toInt((Number) val), val == null); + if (key == EXTENSION) return maybeIntern(extensionData.withExtension(val), value, isValueNull); + if (key == ID) return maybeIntern(extensionData.withId(val), value, isValueNull); + return this; + } + + @Override + public Integer withMeta(IPersistentMap meta) { + return maybeIntern(extensionData.withMeta(meta), value, isValueNull); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeJsonPrimitiveValue(JsonGenerator generator) throws IOException { + if (hasValue()) { + generator.writeNumber(value); + } else { + generator.writeNull(); + } + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (hasValue()) { + sink.putByte((byte) 2); + Integers.hashInto(value, sink); + } + } + + @Override + public int memSize() { + return isInterned() ? 0 : MEM_SIZE_OBJECT + extensionData.memSize(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof Integer that && + extensionData.equals(that.extensionData) && + Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return 31 * extensionData.hashCode() + Objects.hashCode(value); + } + + @Override + public String toString() { + return "Integer{" + extensionData + ", value=" + value + '}'; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Integer64.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Integer64.java new file mode 100644 index 000000000..b8b59e749 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Integer64.java @@ -0,0 +1,105 @@ +package blaze.fhir.spec.type; + +import blaze.fhir.spec.type.system.Longs; +import clojure.lang.IPersistentMap; +import clojure.lang.Keyword; +import clojure.lang.RT; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.lang.String; +import java.util.Objects; + +@SuppressWarnings("DuplicatedCode") +public final class Integer64 extends PrimitiveElement { + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "integer64"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueInteger64"); + + private static final byte HASH_MARKER = 2; + + private static final Integer64 EMPTY = new Integer64(ExtensionData.EMPTY, null); + + private final Long value; + + private Integer64(ExtensionData extensionData, Long value) { + super(extensionData); + this.value = value; + } + + public static Integer64 create(IPersistentMap m) { + return new Integer64(ExtensionData.fromMap(m), (Long) m.valAt(VALUE)); + } + + public Long value() { + return value; + } + + @Override + public Object valAt(Object key, Object notFound) { + return key == FHIR_TYPE_KEY ? FHIR_TYPE : super.valAt(key, notFound); + } + + @Override + public Integer64 empty() { + return EMPTY; + } + + @Override + public Integer64 assoc(Object key, Object val) { + if (key == VALUE) return new Integer64(extensionData, (Long) val); + if (key == EXTENSION) return new Integer64(extensionData.withExtension(val), value); + if (key == ID) return new Integer64(extensionData.withId(val), value); + return this; + } + + @Override + public Integer64 withMeta(IPersistentMap meta) { + return new Integer64(extensionData.withMeta(meta), value); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeJsonPrimitiveValue(JsonGenerator generator) throws IOException { + if (hasValue()) { + generator.writeNumber(value); + } else { + generator.writeNull(); + } + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (value != null) { + sink.putByte((byte) 2); + Longs.hashInto(value, sink); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof Integer64 that && + extensionData.equals(that.extensionData) && + Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return 31 * extensionData.hashCode() + Objects.hashCode(value); + } + + @Override + public String toString() { + return "Integer64{" + extensionData + ", value=" + value + '}'; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Lists.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Lists.java new file mode 100644 index 000000000..a819cbebe --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Lists.java @@ -0,0 +1,33 @@ +package blaze.fhir.spec.type; + +import blaze.Interner; +import blaze.Interners; +import clojure.lang.PersistentVector; + +import java.util.List; + +public final class Lists { + + private static final Interner, PersistentVector> INTERNER = Interners.weakInterner(PersistentVector::create); + + private Lists() { + } + + /** + * Finalizes the mutable {@code list}, interning it if all elements are itself interned. + *

+ * Please be aware that the mutable list is used as key for interning. So it's super important that {@code list} + * isn't mutated after this function is called. + * + * @param list the list to intern + * @return a {@link PersistentVector} created from the list + */ + public static PersistentVector intern(List list) { + return Base.areAllInternedExt(list) ? INTERNER.intern(list) : PersistentVector.create(list); + } + + @SuppressWarnings("unchecked") + public static List nullToEmpty(Object list) { + return list == null ? PersistentVector.EMPTY : (List) list; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Markdown.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Markdown.java new file mode 100644 index 000000000..819f37cc3 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Markdown.java @@ -0,0 +1,121 @@ +package blaze.fhir.spec.type; + +import blaze.Interner; +import blaze.Interners; +import blaze.fhir.spec.type.system.Strings; +import clojure.lang.IPersistentMap; +import clojure.lang.Keyword; +import clojure.lang.RT; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.lang.String; +import java.util.Objects; + +@SuppressWarnings("DuplicatedCode") +public final class Markdown extends PrimitiveElement { + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "markdown"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueMarkdown"); + + private static final byte HASH_MARKER = 16; + + private static final Interner INTERNER = Interners.weakInterner(k -> new Markdown(k, null)); + private static final Markdown EMPTY = new Markdown(ExtensionData.EMPTY, null); + + private final String value; + + private Markdown(ExtensionData extensionData, String value) { + super(extensionData); + this.value = value; + } + + private static Markdown maybeIntern(ExtensionData extensionData, String value) { + return extensionData.isInterned() && value == null ? INTERNER.intern(extensionData) : new Markdown(extensionData, value); + } + + public static Markdown create(String value) { + return value == null ? EMPTY : new Markdown(ExtensionData.EMPTY, value); + } + + public static Markdown create(IPersistentMap m) { + return maybeIntern(ExtensionData.fromMap(m), (String) m.valAt(VALUE)); + } + + public String value() { + return value; + } + + @Override + public Object valAt(Object key, Object notFound) { + return key == FHIR_TYPE_KEY ? FHIR_TYPE : super.valAt(key, notFound); + } + + @Override + public Markdown empty() { + return EMPTY; + } + + @Override + public Markdown assoc(Object key, Object val) { + if (key == VALUE) return maybeIntern(extensionData, (String) val); + if (key == EXTENSION) return maybeIntern(extensionData.withExtension(val), value); + if (key == ID) return maybeIntern(extensionData.withId(val), value); + return this; + } + + @Override + public Markdown withMeta(IPersistentMap meta) { + return maybeIntern(extensionData.withMeta(meta), value); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeJsonPrimitiveValue(JsonGenerator generator) throws IOException { + if (hasValue()) { + generator.writeString(value); + } else { + generator.writeNull(); + } + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (value != null) { + sink.putByte((byte) 2); + Strings.hashInto(value, sink); + } + } + + @Override + public int memSize() { + return super.memSize() + Strings.memSize(value); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof Markdown that && + extensionData.equals(that.extensionData) && + Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return 31 * extensionData.hashCode() + Objects.hashCode(value); + } + + @Override + public String toString() { + return "Markdown{" + extensionData + ", value='" + value + "'" + '}'; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Meta.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Meta.java new file mode 100644 index 000000000..732685285 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Meta.java @@ -0,0 +1,311 @@ +package blaze.fhir.spec.type; + +import blaze.Interner; +import blaze.Interners; +import clojure.lang.*; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.io.SerializedString; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.lang.String; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; + +import static blaze.fhir.spec.type.Base.appendElement; +import static blaze.fhir.spec.type.Complex.serializeJsonComplexList; +import static java.util.Objects.requireNonNull; + +@SuppressWarnings("DuplicatedCode") +public final class Meta extends AbstractElement implements Complex, ExtensionValue { + + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - extension data reference + * 4 byte - versionId reference + * 4 byte - lastUpdated reference + * 4 byte - source reference + * 4 byte - profile reference + * 4 byte - security reference + * 4 byte - tag reference + * 1 byte - interned boolean + * 3 byte - padding + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 32; + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "Meta"); + + private static final Keyword VERSION_ID = RT.keyword(null, "versionId"); + private static final Keyword LAST_UPDATED = RT.keyword(null, "lastUpdated"); + private static final Keyword SOURCE = RT.keyword(null, "source"); + private static final Keyword PROFILE = RT.keyword(null, "profile"); + private static final Keyword SECURITY = RT.keyword(null, "security"); + private static final Keyword TAG = RT.keyword(null, "tag"); + + private static final Keyword[] FIELDS = {ID, EXTENSION, VERSION_ID, LAST_UPDATED, SOURCE, PROFILE, SECURITY, TAG}; + + private static final FieldName FIELD_NAME_VERSION_ID = FieldName.of("versionId"); + private static final FieldName FIELD_NAME_LAST_UPDATED = FieldName.of("lastUpdated"); + private static final FieldName FIELD_NAME_SOURCE = FieldName.of("source"); + private static final FieldName FIELD_NAME_PROFILE = FieldName.of("profile"); + private static final SerializedString FIELD_NAME_SECURITY = new SerializedString("security"); + private static final SerializedString FIELD_NAME_TAG = new SerializedString("tag"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueMeta"); + + private static final byte HASH_MARKER = 44; + + private static final Interner INTERNER = Interners.weakInterner( + k -> new Meta(k.extensionData, null, null, k.source, k.profile, k.security, k.tag, true) + ); + @SuppressWarnings("unchecked") + private static final Meta EMPTY = new Meta(ExtensionData.EMPTY, null, null, null, PersistentVector.EMPTY, + PersistentVector.EMPTY, PersistentVector.EMPTY, true); + + private final Id versionId; + private final Instant lastUpdated; + private final Uri source; + private final List profile; + private final List security; + private final List tag; + private final boolean interned; + + private Meta(ExtensionData extensionData, Id versionId, Instant lastUpdated, Uri source, List profile, + List security, List tag, boolean interned) { + super(extensionData); + this.versionId = versionId; + this.lastUpdated = lastUpdated; + this.source = source; + this.profile = requireNonNull(profile); + this.security = requireNonNull(security); + this.tag = requireNonNull(tag); + this.interned = interned; + } + + private static Meta maybeIntern(ExtensionData extensionData, Id versionId, Instant lastUpdated, Uri source, + List profile, List security, List tag) { + return extensionData.isInterned() && versionId == null && lastUpdated == null && Base.isInterned(source) && + Base.areAllInterned(profile) && Base.areAllInterned(security) && Base.areAllInterned(tag) + ? INTERNER.intern(new InternerKey(extensionData, source, profile, security, tag)) + : new Meta(extensionData, versionId, lastUpdated, source, profile, security, tag, false); + } + + public static Meta create(IPersistentMap m) { + return maybeIntern(ExtensionData.fromMap(m), (Id) m.valAt(VERSION_ID), (Instant) m.valAt(LAST_UPDATED), + (Uri) m.valAt(SOURCE), Base.listFrom(m, PROFILE), Base.listFrom(m, SECURITY), Base.listFrom(m, TAG)); + } + + @Override + public boolean isInterned() { + return interned; + } + + public Id versionId() { + return versionId; + } + + public Instant lastUpdated() { + return lastUpdated; + } + + public Uri source() { + return source; + } + + public List profile() { + return profile; + } + + public List security() { + return security; + } + + public List tag() { + return tag; + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE; + if (key == VERSION_ID) return versionId; + if (key == LAST_UPDATED) return lastUpdated; + if (key == SOURCE) return source; + if (key == PROFILE) return profile; + if (key == SECURITY) return security; + if (key == TAG) return tag; + return super.valAt(key, notFound); + } + + @Override + public ISeq seq() { + ISeq seq = PersistentList.EMPTY; + if (!tag.isEmpty()) { + seq = appendElement(seq, TAG, tag); + } + if (!security.isEmpty()) { + seq = appendElement(seq, SECURITY, security); + } + if (!profile.isEmpty()) { + seq = appendElement(seq, PROFILE, profile); + } + seq = appendElement(seq, SOURCE, source); + seq = appendElement(seq, LAST_UPDATED, lastUpdated); + seq = appendElement(seq, VERSION_ID, versionId); + return extensionData.append(seq); + } + + @Override + public Meta empty() { + return EMPTY; + } + + @Override + public Iterator> iterator() { + return new BaseIterator(this, FIELDS); + } + + @Override + public Meta assoc(Object key, Object val) { + if (key == VERSION_ID) + return maybeIntern(extensionData, (Id) val, lastUpdated, source, profile, security, tag); + if (key == LAST_UPDATED) + return maybeIntern(extensionData, versionId, (Instant) val, source, profile, security, tag); + if (key == SOURCE) + return maybeIntern(extensionData, versionId, lastUpdated, (Uri) val, profile, security, tag); + if (key == PROFILE) + return maybeIntern(extensionData, versionId, lastUpdated, source, Lists.nullToEmpty(val), security, tag); + if (key == SECURITY) + return maybeIntern(extensionData, versionId, lastUpdated, source, profile, Lists.nullToEmpty(val), tag); + if (key == TAG) + return maybeIntern(extensionData, versionId, lastUpdated, source, profile, security, Lists.nullToEmpty(val)); + if (key == EXTENSION) + return maybeIntern(extensionData.withExtension(val), versionId, lastUpdated, source, profile, security, tag); + if (key == ID) + return maybeIntern(extensionData.withId(val), versionId, lastUpdated, source, profile, security, tag); + return this; + } + + @Override + public Meta withMeta(IPersistentMap meta) { + return maybeIntern(extensionData.withMeta(meta), versionId, lastUpdated, source, profile, security, tag); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeAsJsonValue(JsonGenerator generator) throws IOException { + generator.writeStartObject(); + serializeJsonBase(generator); + if (versionId != null) { + versionId.serializeAsJsonProperty(generator, FIELD_NAME_VERSION_ID); + } + if (lastUpdated != null) { + lastUpdated.serializeAsJsonProperty(generator, FIELD_NAME_LAST_UPDATED); + } + if (source != null) { + source.serializeAsJsonProperty(generator, FIELD_NAME_SOURCE); + } + if (!profile.isEmpty()) { + Primitive.serializeJsonPrimitiveList(profile, generator, FIELD_NAME_PROFILE); + } + if (!security.isEmpty()) { + serializeJsonComplexList(security, generator, FIELD_NAME_SECURITY); + } + if (!tag.isEmpty()) { + serializeJsonComplexList(tag, generator, FIELD_NAME_TAG); + } + generator.writeEndObject(); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (versionId != null) { + sink.putByte((byte) 2); + versionId.hashInto(sink); + } + if (lastUpdated != null) { + sink.putByte((byte) 3); + lastUpdated.hashInto(sink); + } + if (source != null) { + sink.putByte((byte) 4); + source.hashInto(sink); + } + if (!profile.isEmpty()) { + sink.putByte((byte) 5); + Base.hashIntoList(profile, sink); + } + if (!security.isEmpty()) { + sink.putByte((byte) 6); + Base.hashIntoList(security, sink); + } + if (!tag.isEmpty()) { + sink.putByte((byte) 7); + Base.hashIntoList(tag, sink); + } + } + + @Override + public int memSize() { + return isInterned() ? 0 : MEM_SIZE_OBJECT + extensionData.memSize() + Base.memSize(versionId) + + Base.memSize(lastUpdated) + Base.memSize(source) + Base.memSize(profile) + Base.memSize(security) + + Base.memSize(tag); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof Meta that && + extensionData.equals(that.extensionData) && + Objects.equals(versionId, that.versionId) && + Objects.equals(lastUpdated, that.lastUpdated) && + Objects.equals(source, that.source) && + profile.equals(that.profile) && + security.equals(that.security) && + tag.equals(that.tag); + } + + @Override + public int hashCode() { + int result = extensionData.hashCode(); + result = 31 * result + Objects.hashCode(versionId); + result = 31 * result + Objects.hashCode(lastUpdated); + result = 31 * result + Objects.hashCode(source); + result = 31 * result + profile.hashCode(); + result = 31 * result + security.hashCode(); + result = 31 * result + tag.hashCode(); + return result; + } + + @Override + public String toString() { + return "Meta{" + + extensionData + + ", versionId=" + versionId + + ", lastUpdated=" + lastUpdated + + ", source=" + source + + ", profile=" + profile + + ", security=" + security + + ", tag=" + tag + + '}'; + } + + private record InternerKey(ExtensionData extensionData, Uri source, List profile, List security, + List tag) { + private InternerKey { + requireNonNull(extensionData); + requireNonNull(profile); + requireNonNull(security); + requireNonNull(tag); + } + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Money.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Money.java new file mode 100644 index 000000000..d0d63fc93 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Money.java @@ -0,0 +1,197 @@ +package blaze.fhir.spec.type; + +import clojure.lang.*; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.util.Iterator; +import java.util.Objects; + +import static blaze.fhir.spec.type.Base.appendElement; + +@SuppressWarnings("DuplicatedCode") +public final class Money extends AbstractElement implements Complex, ExtensionValue { + + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - extension data reference + * 4 byte - value reference + * 4 byte - currency reference + * 4 byte - padding + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 16; + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "Money"); + + private static final Keyword VALUE = RT.keyword(null, "value"); + private static final Keyword CURRENCY = RT.keyword(null, "currency"); + + private static final Keyword[] FIELDS = {ID, EXTENSION, VALUE, CURRENCY}; + + private static final FieldName FIELD_NAME_VALUE = FieldName.of("value"); + private static final FieldName FIELD_NAME_CURRENCY = FieldName.of("currency"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueMoney"); + + private static final byte HASH_MARKER = 61; + + private static final Money EMPTY = new Money(ExtensionData.EMPTY, null, null); + + private static final ILookupThunk FHIR_TYPE_LOOKUP_THUNK = new ILookupThunk() { + @Override + public Object get(Object target) { + return target instanceof Money ? FHIR_TYPE : this; + } + }; + + private static final ILookupThunk VALUE_LOOKUP_THUNK = new ILookupThunk() { + @Override + public Object get(Object target) { + return target instanceof Money m ? m.value : this; + } + }; + + private static final ILookupThunk CURRENCY_LOOKUP_THUNK = new ILookupThunk() { + @Override + public Object get(Object target) { + return target instanceof Money m ? m.currency : this; + } + }; + + private final Decimal value; + private final Code currency; + + private Money(ExtensionData extensionData, Decimal value, Code currency) { + super(extensionData); + this.value = value; + this.currency = currency; + } + + public static Money create(IPersistentMap m) { + return new Money(ExtensionData.fromMap(m), (Decimal) m.valAt(VALUE), (Code) m.valAt(CURRENCY)); + } + + public Decimal value() { + return value; + } + + public Code currency() { + return currency; + } + + @Override + public ILookupThunk getLookupThunk(Keyword key) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE_LOOKUP_THUNK; + if (key == VALUE) return VALUE_LOOKUP_THUNK; + if (key == CURRENCY) return CURRENCY_LOOKUP_THUNK; + return super.getLookupThunk(key); + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE; + if (key == VALUE) return value; + if (key == CURRENCY) return currency; + return super.valAt(key, notFound); + } + + @Override + public ISeq seq() { + ISeq seq = PersistentList.EMPTY; + seq = appendElement(seq, CURRENCY, currency); + seq = appendElement(seq, VALUE, value); + return extensionData.append(seq); + } + + @Override + public Money empty() { + return EMPTY; + } + + @Override + public Iterator> iterator() { + return new BaseIterator(this, FIELDS); + } + + @Override + public Money assoc(Object key, Object val) { + if (key == VALUE) return new Money(extensionData, (Decimal) val, currency); + if (key == CURRENCY) return new Money(extensionData, value, (Code) val); + if (key == EXTENSION) + return new Money(extensionData.withExtension(val), value, currency); + if (key == ID) return new Money(extensionData.withId(val), value, currency); + return this; + } + + @Override + public Money withMeta(IPersistentMap meta) { + return new Money(extensionData.withMeta(meta), value, currency); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeAsJsonValue(JsonGenerator generator) throws IOException { + generator.writeStartObject(); + serializeJsonBase(generator); + if (value != null) { + value.serializeAsJsonProperty(generator, FIELD_NAME_VALUE); + } + if (currency != null) { + currency.serializeAsJsonProperty(generator, FIELD_NAME_CURRENCY); + } + generator.writeEndObject(); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (value != null) { + sink.putByte((byte) 2); + value.hashInto(sink); + } + if (currency != null) { + sink.putByte((byte) 3); + currency.hashInto(sink); + } + } + + @Override + public int memSize() { + return MEM_SIZE_OBJECT + extensionData.memSize() + Base.memSize(value) + Base.memSize(currency); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof Money that && + extensionData.equals(that.extensionData) && + Objects.equals(value, that.value) && + Objects.equals(currency, that.currency); + } + + @Override + public int hashCode() { + int result = extensionData.hashCode(); + result = 31 * result + Objects.hashCode(value); + result = 31 * result + Objects.hashCode(currency); + return result; + } + + @Override + public java.lang.String toString() { + return "Money{" + + extensionData + + ", value=" + value + + ", currency=" + currency + + '}'; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Oid.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Oid.java new file mode 100644 index 000000000..db4713897 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Oid.java @@ -0,0 +1,121 @@ +package blaze.fhir.spec.type; + +import blaze.Interner; +import blaze.Interners; +import blaze.fhir.spec.type.system.Strings; +import clojure.lang.IPersistentMap; +import clojure.lang.Keyword; +import clojure.lang.RT; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.lang.String; +import java.util.Objects; + +@SuppressWarnings("DuplicatedCode") +public final class Oid extends PrimitiveElement { + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "oid"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueOid"); + + private static final byte HASH_MARKER = 14; + + private static final Interner INTERNER = Interners.weakInterner(k -> new Oid(k, null)); + private static final Oid EMPTY = new Oid(ExtensionData.EMPTY, null); + + private final String value; + + private Oid(ExtensionData extensionData, String value) { + super(extensionData); + this.value = value; + } + + private static Oid maybeIntern(ExtensionData extensionData, String value) { + return extensionData.isInterned() && value == null ? INTERNER.intern(extensionData) : new Oid(extensionData, value); + } + + public static Oid create(String value) { + return value == null ? EMPTY : new Oid(ExtensionData.EMPTY, value); + } + + public static Oid create(IPersistentMap m) { + return maybeIntern(ExtensionData.fromMap(m), (String) m.valAt(VALUE)); + } + + public String value() { + return value; + } + + @Override + public Object valAt(Object key, Object notFound) { + return key == FHIR_TYPE_KEY ? FHIR_TYPE : super.valAt(key, notFound); + } + + @Override + public Oid empty() { + return EMPTY; + } + + @Override + public Oid assoc(Object key, Object val) { + if (key == VALUE) return maybeIntern(extensionData, (String) val); + if (key == EXTENSION) return maybeIntern(extensionData.withExtension(val), value); + if (key == ID) return maybeIntern(extensionData.withId(val), value); + return this; + } + + @Override + public Oid withMeta(IPersistentMap meta) { + return maybeIntern(extensionData.withMeta(meta), value); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeJsonPrimitiveValue(JsonGenerator generator) throws IOException { + if (hasValue()) { + generator.writeString(value); + } else { + generator.writeNull(); + } + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (value != null) { + sink.putByte((byte) 2); + Strings.hashInto(value, sink); + } + } + + @Override + public int memSize() { + return super.memSize() + Strings.memSize(value); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof Oid that && + extensionData.equals(that.extensionData) && + Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return 31 * extensionData.hashCode() + Objects.hashCode(value); + } + + @Override + public String toString() { + return "Oid{" + extensionData + ", value=" + (value == null ? null : '\'' + value + '\'') + '}'; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/ParameterDefinition.java b/modules/fhir-structure/java/blaze/fhir/spec/type/ParameterDefinition.java new file mode 100644 index 000000000..ec4e1978e --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/ParameterDefinition.java @@ -0,0 +1,288 @@ +package blaze.fhir.spec.type; + +import clojure.lang.*; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.util.Iterator; +import java.util.Objects; + +import static blaze.fhir.spec.type.Base.appendElement; + +@SuppressWarnings("DuplicatedCode") +public final class ParameterDefinition extends AbstractElement implements Complex, ExtensionValue { + + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - extension data reference + * 4 byte - name reference + * 4 byte - use reference + * 4 byte - min reference + * 4 byte - max reference + * 4 byte - documentation reference + * 4 byte - type reference + * 4 byte - profile reference + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 32; + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "ParameterDefinition"); + + private static final Keyword NAME = RT.keyword(null, "name"); + private static final Keyword USE = RT.keyword(null, "use"); + private static final Keyword MIN = RT.keyword(null, "min"); + private static final Keyword MAX = RT.keyword(null, "max"); + private static final Keyword DOCUMENTATION = RT.keyword(null, "documentation"); + private static final Keyword TYPE = RT.keyword(null, "type"); + private static final Keyword PROFILE = RT.keyword(null, "profile"); + + private static final Keyword[] FIELDS = {ID, EXTENSION, NAME, USE, MIN, MAX, DOCUMENTATION, TYPE, PROFILE}; + + private static final FieldName FIELD_NAME_NAME = FieldName.of("name"); + private static final FieldName FIELD_NAME_USE = FieldName.of("use"); + private static final FieldName FIELD_NAME_MIN = FieldName.of("min"); + private static final FieldName FIELD_NAME_MAX = FieldName.of("max"); + private static final FieldName FIELD_NAME_DOCUMENTATION = FieldName.of("documentation"); + private static final FieldName FIELD_NAME_TYPE = FieldName.of("type"); + private static final FieldName FIELD_NAME_PROFILE = FieldName.of("profile"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueParameterDefinition"); + + private static final byte HASH_MARKER = 66; + + private static final ParameterDefinition EMPTY = new ParameterDefinition(ExtensionData.EMPTY, null, null, null, null, null, null, null); + + private final Code name; + private final Code use; + private final Integer min; + private final String max; + private final String documentation; + private final Code type; + private final Canonical profile; + + private ParameterDefinition(ExtensionData extensionData, Code name, Code use, Integer min, String max, String documentation, Code type, Canonical profile) { + super(extensionData); + this.name = name; + this.use = use; + this.min = min; + this.max = max; + this.documentation = documentation; + this.type = type; + this.profile = profile; + } + + public static ParameterDefinition create(IPersistentMap m) { + return new ParameterDefinition(ExtensionData.fromMap(m), (Code) m.valAt(NAME), (Code) m.valAt(USE), + (Integer) m.valAt(MIN), (String) m.valAt(MAX), (String) m.valAt(DOCUMENTATION), (Code) m.valAt(TYPE), + (Canonical) m.valAt(PROFILE)); + } + + public Code name() { + return name; + } + + public Code use() { + return use; + } + + public Integer min() { + return min; + } + + public String max() { + return max; + } + + public String documentation() { + return documentation; + } + + public Code type() { + return type; + } + + public Canonical profile() { + return profile; + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE; + if (key == NAME) return name; + if (key == USE) return use; + if (key == MIN) return min; + if (key == MAX) return max; + if (key == DOCUMENTATION) return documentation; + if (key == TYPE) return type; + if (key == PROFILE) return profile; + return super.valAt(key, notFound); + } + + @Override + public ISeq seq() { + ISeq seq = PersistentList.EMPTY; + seq = appendElement(seq, PROFILE, profile); + seq = appendElement(seq, TYPE, type); + seq = appendElement(seq, DOCUMENTATION, documentation); + seq = appendElement(seq, MAX, max); + seq = appendElement(seq, MIN, min); + seq = appendElement(seq, USE, use); + seq = appendElement(seq, NAME, name); + return extensionData.append(seq); + } + + @Override + public ParameterDefinition empty() { + return EMPTY; + } + + @Override + public Iterator> iterator() { + return new BaseIterator(this, FIELDS); + } + + @Override + public ParameterDefinition assoc(Object key, Object val) { + if (key == NAME) + return new ParameterDefinition(extensionData, (Code) val, use, min, max, documentation, type, profile); + if (key == USE) + return new ParameterDefinition(extensionData, name, (Code) val, min, max, documentation, type, profile); + if (key == MIN) + return new ParameterDefinition(extensionData, name, use, (Integer) val, max, documentation, type, profile); + if (key == MAX) + return new ParameterDefinition(extensionData, name, use, min, (String) val, documentation, type, profile); + if (key == DOCUMENTATION) + return new ParameterDefinition(extensionData, name, use, min, max, (String) val, type, profile); + if (key == TYPE) + return new ParameterDefinition(extensionData, name, use, min, max, documentation, (Code) val, profile); + if (key == PROFILE) + return new ParameterDefinition(extensionData, name, use, min, max, documentation, type, (Canonical) val); + if (key == EXTENSION) + return new ParameterDefinition(extensionData.withExtension(val), name, use, min, max, documentation, type, profile); + if (key == ID) + return new ParameterDefinition(extensionData.withId(val), name, use, min, max, documentation, type, profile); + return this; + } + + @Override + public ParameterDefinition withMeta(IPersistentMap meta) { + return new ParameterDefinition(extensionData.withMeta(meta), name, use, min, max, documentation, type, profile); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeAsJsonValue(JsonGenerator generator) throws IOException { + generator.writeStartObject(); + serializeJsonBase(generator); + if (name != null) { + name.serializeAsJsonProperty(generator, FIELD_NAME_NAME); + } + if (use != null) { + use.serializeAsJsonProperty(generator, FIELD_NAME_USE); + } + if (min != null) { + min.serializeAsJsonProperty(generator, FIELD_NAME_MIN); + } + if (max != null) { + max.serializeAsJsonProperty(generator, FIELD_NAME_MAX); + } + if (documentation != null) { + documentation.serializeAsJsonProperty(generator, FIELD_NAME_DOCUMENTATION); + } + if (type != null) { + type.serializeAsJsonProperty(generator, FIELD_NAME_TYPE); + } + if (profile != null) { + profile.serializeAsJsonProperty(generator, FIELD_NAME_PROFILE); + } + generator.writeEndObject(); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (name != null) { + sink.putByte((byte) 2); + name.hashInto(sink); + } + if (use != null) { + sink.putByte((byte) 3); + use.hashInto(sink); + } + if (min != null) { + sink.putByte((byte) 4); + min.hashInto(sink); + } + if (max != null) { + sink.putByte((byte) 5); + max.hashInto(sink); + } + if (documentation != null) { + sink.putByte((byte) 6); + documentation.hashInto(sink); + } + if (type != null) { + sink.putByte((byte) 7); + type.hashInto(sink); + } + if (profile != null) { + sink.putByte((byte) 8); + profile.hashInto(sink); + } + } + + @Override + public int memSize() { + return MEM_SIZE_OBJECT + extensionData.memSize() + Base.memSize(name) + Base.memSize(use) + Base.memSize(min) + + Base.memSize(max) + Base.memSize(documentation) + Base.memSize(type) + Base.memSize(profile); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof ParameterDefinition that && + extensionData.equals(that.extensionData) && + Objects.equals(name, that.name) && + Objects.equals(use, that.use) && + Objects.equals(min, that.min) && + Objects.equals(max, that.max) && + Objects.equals(documentation, that.documentation) && + Objects.equals(type, that.type) && + Objects.equals(profile, that.profile); + } + + @Override + public int hashCode() { + int result = extensionData.hashCode(); + result = 31 * result + Objects.hashCode(name); + result = 31 * result + Objects.hashCode(use); + result = 31 * result + Objects.hashCode(min); + result = 31 * result + Objects.hashCode(max); + result = 31 * result + Objects.hashCode(documentation); + result = 31 * result + Objects.hashCode(type); + result = 31 * result + Objects.hashCode(profile); + return result; + } + + @Override + public java.lang.String toString() { + return "ParameterDefinition{" + + "extensionData=" + extensionData + + ", name=" + name + + ", use=" + use + + ", min=" + min + + ", max=" + max + + ", documentation=" + documentation + + ", type=" + type + + ", profile=" + profile + + '}'; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Period.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Period.java new file mode 100644 index 000000000..b34535aa3 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Period.java @@ -0,0 +1,181 @@ +package blaze.fhir.spec.type; + +import blaze.Interner; +import blaze.Interners; +import clojure.lang.*; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.lang.String; +import java.util.Iterator; +import java.util.Objects; + +import static blaze.fhir.spec.type.Base.appendElement; + +@SuppressWarnings("DuplicatedCode") +public final class Period extends AbstractElement implements Complex, ExtensionValue { + + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - extension data reference + * 4 byte - start reference + * 4 byte - end boolean + * 1 byte - interned boolean + * 3 byte - padding + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 16; + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "Period"); + + private static final Keyword START = RT.keyword(null, "start"); + private static final Keyword END = RT.keyword(null, "end"); + + private static final Keyword[] FIELDS = {ID, EXTENSION, START, END}; + + private static final FieldName FIELD_NAME_START = FieldName.of("start"); + private static final FieldName FIELD_NAME_END = FieldName.of("end"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valuePeriod"); + + private static final byte HASH_MARKER = 41; + + private static final Interner INTERNER = Interners.weakInterner(k -> new Period(k, null, null, true)); + private static final Period EMPTY = new Period(ExtensionData.EMPTY, null, null, true); + + private final DateTime start; + private final DateTime end; + private final boolean interned; + + private Period(ExtensionData extensionData, DateTime start, DateTime end, boolean interned) { + super(extensionData); + this.start = start; + this.end = end; + this.interned = interned; + } + + private static Period maybeIntern(ExtensionData extensionData, DateTime start, DateTime end) { + return extensionData.isInterned() && start == null && end == null + ? INTERNER.intern(extensionData) + : new Period(extensionData, start, end, false); + } + + public static Period create(IPersistentMap m) { + return maybeIntern(ExtensionData.fromMap(m), (DateTime) m.valAt(START), (DateTime) m.valAt(END)); + } + + @Override + public boolean isInterned() { + return interned; + } + + public DateTime start() { + return start; + } + + public DateTime end() { + return end; + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE; + if (key == START) return start; + if (key == END) return end; + return super.valAt(key, notFound); + } + + @Override + public ISeq seq() { + ISeq seq = PersistentList.EMPTY; + seq = appendElement(seq, END, end); + seq = appendElement(seq, START, start); + return extensionData.append(seq); + } + + @Override + public Period empty() { + return EMPTY; + } + + @Override + public Iterator> iterator() { + return new BaseIterator(this, FIELDS); + } + + @Override + public Period assoc(Object key, Object val) { + if (key == START) return maybeIntern(extensionData, (DateTime) val, end); + if (key == END) return maybeIntern(extensionData, start, (DateTime) val); + if (key == EXTENSION) return maybeIntern(extensionData.withExtension(val), start, end); + if (key == ID) return maybeIntern(extensionData.withId(val), start, end); + return this; + } + + @Override + public Period withMeta(IPersistentMap meta) { + return maybeIntern(extensionData.withMeta(meta), start, end); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeAsJsonValue(JsonGenerator generator) throws IOException { + generator.writeStartObject(); + serializeJsonBase(generator); + if (start != null) { + start.serializeAsJsonProperty(generator, FIELD_NAME_START); + } + if (end != null) { + end.serializeAsJsonProperty(generator, FIELD_NAME_END); + } + generator.writeEndObject(); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (start != null) { + sink.putByte((byte) 2); + start.hashInto(sink); + } + if (end != null) { + sink.putByte((byte) 3); + end.hashInto(sink); + } + } + + @Override + public int memSize() { + return isInterned() ? 0 : MEM_SIZE_OBJECT + extensionData.memSize() + Base.memSize(start) + Base.memSize(end); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof Period that && + extensionData.equals(that.extensionData) && + Objects.equals(start, that.start) && + Objects.equals(end, that.end); + } + + @Override + public int hashCode() { + int result = extensionData.hashCode(); + result = 31 * result + Objects.hashCode(start); + result = 31 * result + Objects.hashCode(end); + return result; + } + + @Override + public String toString() { + return "Period{" + extensionData + ", start=" + start + ", end=" + end + '}'; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/PositiveInt.java b/modules/fhir-structure/java/blaze/fhir/spec/type/PositiveInt.java new file mode 100644 index 000000000..e1bea8de2 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/PositiveInt.java @@ -0,0 +1,134 @@ +package blaze.fhir.spec.type; + +import blaze.Interner; +import blaze.Interners; +import blaze.fhir.spec.type.system.Integers; +import clojure.lang.IPersistentMap; +import clojure.lang.Keyword; +import clojure.lang.RT; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.lang.Integer; +import java.lang.String; +import java.util.Objects; + +@SuppressWarnings("DuplicatedCode") +public final class PositiveInt extends PrimitiveElement { + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "positiveInt"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valuePositiveInt"); + + private static final byte HASH_MARKER = 18; + + private static final Interner INTERNER = Interners.weakInterner(k -> new PositiveInt(k, 0)); + private static final PositiveInt EMPTY = new PositiveInt(ExtensionData.EMPTY, 0); + + private final int value; + + private PositiveInt(ExtensionData extensionData, int value) { + super(extensionData); + this.value = value; + } + + private static PositiveInt maybeIntern(ExtensionData extensionData, int value) { + return extensionData.isInterned() && value == 0 + ? INTERNER.intern(extensionData) + : new PositiveInt(extensionData, value); + } + + private static int toPosInt(Number value) { + long val = value.longValue(); + if (val <= 0 || (int) val != val) { + throw new IllegalArgumentException("Invalid positiveInt value `%d`.".formatted(val)); + } + return (int) val; + } + + public static PositiveInt create(Number value) { + return value == null ? EMPTY : new PositiveInt(ExtensionData.EMPTY, toPosInt(value)); + } + + public static PositiveInt create(IPersistentMap m) { + var value = (Number) m.valAt(VALUE); + return maybeIntern(ExtensionData.fromMap(m), value == null ? 0 : toPosInt(value)); + } + + @Override + public boolean hasValue() { + return value > 0; + } + + @Override + public Integer value() { + return hasValue() ? value : null; + } + + @Override + public Object valAt(Object key, Object notFound) { + return key == FHIR_TYPE_KEY ? FHIR_TYPE : super.valAt(key, notFound); + } + + @Override + public PositiveInt empty() { + return EMPTY; + } + + @Override + public PositiveInt assoc(Object key, Object val) { + if (key == VALUE) return maybeIntern(extensionData, val == null ? 0 : toPosInt((Number) val)); + if (key == EXTENSION) return maybeIntern(extensionData.withExtension(val), value); + if (key == ID) return maybeIntern(extensionData.withId(val), value); + return this; + } + + @Override + public PositiveInt withMeta(IPersistentMap meta) { + return maybeIntern(extensionData.withMeta(meta), value); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeJsonPrimitiveValue(JsonGenerator generator) throws IOException { + if (hasValue()) { + generator.writeNumber(value); + } else { + generator.writeNull(); + } + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (hasValue()) { + sink.putByte((byte) 2); + Integers.hashInto(value, sink); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof PositiveInt that && + extensionData.equals(that.extensionData) && + Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return 31 * extensionData.hashCode() + Objects.hashCode(value); + } + + @Override + public String toString() { + return "PositiveInt{" + extensionData + ", value=" + value + '}'; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Primitive.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Primitive.java new file mode 100644 index 000000000..039c3772b --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Primitive.java @@ -0,0 +1,81 @@ +package blaze.fhir.spec.type; + +import clojure.lang.ILookupThunk; +import clojure.lang.Keyword; +import com.fasterxml.jackson.core.JsonGenerator; + +import java.io.IOException; +import java.lang.String; +import java.util.Iterator; +import java.util.List; + +public interface Primitive extends ExtensionValue { + + Keyword[] FIELDS = {ID, EXTENSION, VALUE}; + + static void serializeJsonPrimitiveList(List values, JsonGenerator generator, FieldName fieldName) throws IOException { + if (values.stream().anyMatch(Primitive::hasValue)) { + generator.writeFieldName(fieldName.normal()); + generator.writeStartArray(); + for (Primitive value : values) { + value.serializeJsonPrimitiveValue(generator); + } + generator.writeEndArray(); + } + if (values.stream().anyMatch(Primitive::isExtended)) { + generator.writeFieldName(fieldName.extended()); + generator.writeStartArray(); + for (Primitive value : values) { + value.serializeJsonPrimitiveExtension(generator); + } + generator.writeEndArray(); + } + } + + default boolean hasValue() { + return value() != null; + } + + Object value(); + + default String valueAsString() { + var value = value(); + return value == null ? null : value.toString(); + } + + boolean isExtended(); + + @Override + default ILookupThunk getLookupThunk(Keyword key) { + if (key == VALUE) return new ILookupThunk() { + @Override + public Object get(Object target) { + return target instanceof Primitive p ? p.value() : this; + } + }; + return ExtensionValue.super.getLookupThunk(key); + } + + @Override + default Iterator> iterator() { + return new BaseIterator(this, FIELDS); + } + + void serializeJsonPrimitiveValue(JsonGenerator generator) throws IOException; + + default void serializeAsJsonProperty(JsonGenerator generator, FieldName fieldName) throws IOException { + if (hasValue()) { + generator.writeFieldName(fieldName.normal()); + serializeJsonPrimitiveValue(generator); + } + if (isExtended()) { + generator.writeFieldName(fieldName.extended()); + serializeJsonPrimitiveExtension(generator); + } + } + + @Override + default void serializeJsonField(JsonGenerator generator, FieldName fieldName) throws IOException { + serializeAsJsonProperty(generator, fieldName); + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/PrimitiveElement.java b/modules/fhir-structure/java/blaze/fhir/spec/type/PrimitiveElement.java new file mode 100644 index 000000000..6a89b4e55 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/PrimitiveElement.java @@ -0,0 +1,48 @@ +package blaze.fhir.spec.type; + +import clojure.lang.ISeq; +import clojure.lang.PersistentList; + +import static blaze.fhir.spec.type.Base.appendElement; + +abstract class PrimitiveElement extends AbstractElement implements Primitive { + + /** + * Memory size of most primitive types. + *

+ * 8 byte - object header + * 4 byte - extension data reference + * 4 byte - value reference + */ + protected static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 8; + + protected PrimitiveElement(ExtensionData extensionData) { + super(extensionData); + } + + @Override + public boolean isInterned() { + return extensionData.isInterned() && !hasValue(); + } + + public boolean isExtended() { + return extensionData.isNotEmpty(); + } + + @Override + public Object valAt(Object key, Object notFound) { + return key == VALUE ? value() : extensionData.valAt(key, notFound); + } + + @Override + public ISeq seq() { + ISeq seq = PersistentList.EMPTY; + seq = appendElement(seq, VALUE, value()); + return extensionData.append(seq); + } + + @Override + public int memSize() { + return isInterned() ? 0 : MEM_SIZE_OBJECT + extensionData.memSize(); + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Quantity.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Quantity.java new file mode 100644 index 000000000..5be985742 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Quantity.java @@ -0,0 +1,71 @@ +package blaze.fhir.spec.type; + +import blaze.Interner; +import blaze.Interners; +import clojure.lang.IPersistentMap; +import clojure.lang.Keyword; +import clojure.lang.RT; + +@SuppressWarnings("DuplicatedCode") +public final class Quantity extends AbstractQuantity { + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "Quantity"); + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueQuantity"); + + private static final Quantity EMPTY = new Quantity(ExtensionData.EMPTY, null, null, null, null, null); + + private static final Interner INTERNER = Interners.weakInterner( + k -> new Quantity(k.extensionData(), k.value(), k.comparator(), k.unit(), k.system(), k.code()) + ); + + private Quantity(ExtensionData extensionData, Decimal value, Code comparator, String unit, Uri system, Code code) { + super(extensionData, value, comparator, unit, system, code); + } + + private static Quantity maybeIntern(ExtensionData extensionData, Decimal value, Code comparator, String unit, + Uri system, Code code) { + return extensionData.isInterned() && Base.isInterned(value) && Base.isInterned(comparator) && + Base.isInterned(unit) && Base.isInterned(system) && Base.isInterned(code) + ? INTERNER.intern(new InternerKey(extensionData, value, comparator, unit, system, code)) + : new Quantity(extensionData, value, comparator, unit, system, code); + } + + public static Quantity create(IPersistentMap m) { + return maybeIntern(ExtensionData.fromMap(m), (Decimal) m.valAt(VALUE), (Code) m.valAt(COMPARATOR), + (String) m.valAt(UNIT), (Uri) m.valAt(SYSTEM), (Code) m.valAt(CODE)); + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE; + return super.valAt(key, notFound); + } + + @Override + public Quantity empty() { + return EMPTY; + } + + @Override + public Quantity assoc(Object key, Object val) { + if (key == VALUE) return maybeIntern(extensionData, (Decimal) val, comparator, unit, system, code); + if (key == COMPARATOR) return maybeIntern(extensionData, value, (Code) val, unit, system, code); + if (key == UNIT) return maybeIntern(extensionData, value, comparator, (String) val, system, code); + if (key == SYSTEM) return maybeIntern(extensionData, value, comparator, unit, (Uri) val, code); + if (key == CODE) return maybeIntern(extensionData, value, comparator, unit, system, (Code) val); + if (key == EXTENSION) + return maybeIntern(extensionData.withExtension(val), value, comparator, unit, system, code); + if (key == ID) return maybeIntern(extensionData.withId(val), value, comparator, unit, system, code); + return this; + } + + @Override + public Quantity withMeta(IPersistentMap meta) { + return maybeIntern(extensionData.withMeta(meta), value, comparator, unit, system, code); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Range.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Range.java new file mode 100644 index 000000000..09dd09681 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Range.java @@ -0,0 +1,181 @@ +package blaze.fhir.spec.type; + +import blaze.Interner; +import blaze.Interners; +import clojure.lang.*; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.lang.String; +import java.util.Iterator; +import java.util.Objects; + +import static blaze.fhir.spec.type.Base.appendElement; + +@SuppressWarnings("DuplicatedCode") +public final class Range extends AbstractElement implements Complex, ExtensionValue { + + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - extension data reference + * 4 byte - low reference + * 4 byte - high boolean + * 1 byte - interned boolean + * 3 byte - padding + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 16; + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "Range"); + + private static final Keyword LOW = RT.keyword(null, "low"); + private static final Keyword HIGH = RT.keyword(null, "high"); + + private static final Keyword[] FIELDS = {ID, EXTENSION, LOW, HIGH}; + + private static final FieldName FIELD_NAME_LOW = FieldName.of("low"); + private static final FieldName FIELD_NAME_HIGH = FieldName.of("high"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueRange"); + + private static final byte HASH_MARKER = 50; + + private static final Interner INTERNER = Interners.weakInterner(k -> new Range(k, null, null, true)); + private static final Range EMPTY = new Range(ExtensionData.EMPTY, null, null, true); + + private final Quantity low; + private final Quantity high; + private final boolean interned; + + private Range(ExtensionData extensionData, Quantity low, Quantity high, boolean interned) { + super(extensionData); + this.low = low; + this.high = high; + this.interned = interned; + } + + private static Range maybeIntern(ExtensionData extensionData, Quantity low, Quantity high) { + return extensionData.isInterned() && low == null && high == null + ? INTERNER.intern(extensionData) + : new Range(extensionData, low, high, false); + } + + public static Range create(IPersistentMap m) { + return maybeIntern(ExtensionData.fromMap(m), (Quantity) m.valAt(LOW), (Quantity) m.valAt(HIGH)); + } + + @Override + public boolean isInterned() { + return interned; + } + + public Quantity low() { + return low; + } + + public Quantity high() { + return high; + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE; + if (key == LOW) return low; + if (key == HIGH) return high; + return super.valAt(key, notFound); + } + + @Override + public ISeq seq() { + ISeq seq = PersistentList.EMPTY; + seq = appendElement(seq, HIGH, high); + seq = appendElement(seq, LOW, low); + return extensionData.append(seq); + } + + @Override + public Range empty() { + return EMPTY; + } + + @Override + public Iterator> iterator() { + return new BaseIterator(this, FIELDS); + } + + @Override + public Range assoc(Object key, Object val) { + if (key == LOW) return maybeIntern(extensionData, (Quantity) val, high); + if (key == HIGH) return maybeIntern(extensionData, low, (Quantity) val); + if (key == EXTENSION) return maybeIntern(extensionData.withExtension(val), low, high); + if (key == ID) return maybeIntern(extensionData.withId(val), low, high); + return this; + } + + @Override + public Range withMeta(IPersistentMap meta) { + return maybeIntern(extensionData.withMeta(meta), low, high); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeAsJsonValue(JsonGenerator generator) throws IOException { + generator.writeStartObject(); + serializeJsonBase(generator); + if (low != null) { + low.serializeJsonField(generator, FIELD_NAME_LOW); + } + if (high != null) { + high.serializeJsonField(generator, FIELD_NAME_HIGH); + } + generator.writeEndObject(); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (low != null) { + sink.putByte((byte) 2); + low.hashInto(sink); + } + if (high != null) { + sink.putByte((byte) 3); + high.hashInto(sink); + } + } + + @Override + public int memSize() { + return isInterned() ? 0 : MEM_SIZE_OBJECT + extensionData.memSize() + Base.memSize(low) + Base.memSize(high); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof Range that && + extensionData.equals(that.extensionData) && + Objects.equals(low, that.low) && + Objects.equals(high, that.high); + } + + @Override + public int hashCode() { + int result = extensionData.hashCode(); + result = 31 * result + Objects.hashCode(low); + result = 31 * result + Objects.hashCode(high); + return result; + } + + @Override + public String toString() { + return "Range{" + extensionData + ", low=" + low + ", high=" + high + '}'; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Ratio.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Ratio.java new file mode 100644 index 000000000..84e1c25ad --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Ratio.java @@ -0,0 +1,181 @@ +package blaze.fhir.spec.type; + +import blaze.Interner; +import blaze.Interners; +import clojure.lang.*; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.lang.String; +import java.util.Iterator; +import java.util.Objects; + +import static blaze.fhir.spec.type.Base.appendElement; + +@SuppressWarnings("DuplicatedCode") +public final class Ratio extends AbstractElement implements Complex, ExtensionValue { + + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - extension data reference + * 4 byte - numerator reference + * 4 byte - denominator boolean + * 1 byte - interned boolean + * 3 byte - padding + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 16; + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "Ratio"); + + private static final Keyword NUMERATOR = RT.keyword(null, "numerator"); + private static final Keyword DENOMINATOR = RT.keyword(null, "denominator"); + + private static final Keyword[] FIELDS = {ID, EXTENSION, NUMERATOR, DENOMINATOR}; + + private static final FieldName FIELD_NAME_NUMERATOR = FieldName.of("numerator"); + private static final FieldName FIELD_NAME_DENOMINATOR = FieldName.of("denominator"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueRatio"); + + private static final byte HASH_MARKER = 48; + + private static final Interner INTERNER = Interners.weakInterner(k -> new Ratio(k, null, null, true)); + private static final Ratio EMPTY = new Ratio(ExtensionData.EMPTY, null, null, true); + + private final Quantity numerator; + private final Quantity denominator; + private final boolean interned; + + private Ratio(ExtensionData extensionData, Quantity numerator, Quantity denominator, boolean interned) { + super(extensionData); + this.numerator = numerator; + this.denominator = denominator; + this.interned = interned; + } + + private static Ratio maybeIntern(ExtensionData extensionData, Quantity numerator, Quantity denominator) { + return extensionData.isInterned() && numerator == null && denominator == null + ? INTERNER.intern(extensionData) + : new Ratio(extensionData, numerator, denominator, false); + } + + public static Ratio create(IPersistentMap m) { + return maybeIntern(ExtensionData.fromMap(m), (Quantity) m.valAt(NUMERATOR), (Quantity) m.valAt(DENOMINATOR)); + } + + @Override + public boolean isInterned() { + return interned; + } + + public Quantity numerator() { + return numerator; + } + + public Quantity denominator() { + return denominator; + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE; + if (key == NUMERATOR) return numerator; + if (key == DENOMINATOR) return denominator; + return super.valAt(key, notFound); + } + + @Override + public ISeq seq() { + ISeq seq = PersistentList.EMPTY; + seq = appendElement(seq, DENOMINATOR, denominator); + seq = appendElement(seq, NUMERATOR, numerator); + return extensionData.append(seq); + } + + @Override + public Ratio empty() { + return EMPTY; + } + + @Override + public Iterator> iterator() { + return new BaseIterator(this, FIELDS); + } + + @Override + public Ratio assoc(Object key, Object val) { + if (key == NUMERATOR) return maybeIntern(extensionData, (Quantity) val, denominator); + if (key == DENOMINATOR) return maybeIntern(extensionData, numerator, (Quantity) val); + if (key == EXTENSION) return maybeIntern(extensionData.withExtension(val), numerator, denominator); + if (key == ID) return maybeIntern(extensionData.withId(val), numerator, denominator); + return this; + } + + @Override + public Ratio withMeta(IPersistentMap meta) { + return maybeIntern(extensionData.withMeta(meta), numerator, denominator); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeAsJsonValue(JsonGenerator generator) throws IOException { + generator.writeStartObject(); + serializeJsonBase(generator); + if (numerator != null) { + numerator.serializeJsonField(generator, FIELD_NAME_NUMERATOR); + } + if (denominator != null) { + denominator.serializeJsonField(generator, FIELD_NAME_DENOMINATOR); + } + generator.writeEndObject(); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (numerator != null) { + sink.putByte((byte) 2); + numerator.hashInto(sink); + } + if (denominator != null) { + sink.putByte((byte) 3); + denominator.hashInto(sink); + } + } + + @Override + public int memSize() { + return isInterned() ? 0 : MEM_SIZE_OBJECT + extensionData.memSize() + Base.memSize(numerator) + Base.memSize(denominator); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof Ratio that && + extensionData.equals(that.extensionData) && + Objects.equals(numerator, that.numerator) && + Objects.equals(denominator, that.denominator); + } + + @Override + public int hashCode() { + int result = extensionData.hashCode(); + result = 31 * result + Objects.hashCode(numerator); + result = 31 * result + Objects.hashCode(denominator); + return result; + } + + @Override + public String toString() { + return "Ratio{" + extensionData + ", numerator=" + numerator + ", denominator=" + denominator + '}'; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/RatioRange.java b/modules/fhir-structure/java/blaze/fhir/spec/type/RatioRange.java new file mode 100644 index 000000000..9835a67ff --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/RatioRange.java @@ -0,0 +1,198 @@ +package blaze.fhir.spec.type; + +import clojure.lang.*; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.lang.String; +import java.util.Iterator; +import java.util.Objects; + +import static blaze.fhir.spec.type.Base.appendElement; + +@SuppressWarnings("DuplicatedCode") +public final class RatioRange extends AbstractElement implements Complex, ExtensionValue { + + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - extension data reference + * 4 byte - lowNumerator reference + * 4 byte - highNumerator reference + * 4 byte - denominator reference + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 16; + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "RatioRange"); + + private static final Keyword LOW_NUMERATOR = RT.keyword(null, "lowNumerator"); + private static final Keyword HIGH_NUMERATOR = RT.keyword(null, "highNumerator"); + private static final Keyword DENOMINATOR = RT.keyword(null, "denominator"); + + private static final Keyword[] FIELDS = {ID, EXTENSION, LOW_NUMERATOR, HIGH_NUMERATOR, DENOMINATOR}; + + private static final FieldName FIELD_NAME_LOW_NUMERATOR = FieldName.of("lowNumerator"); + private static final FieldName FIELD_NAME_HIGH_NUMERATOR = FieldName.of("highNumerator"); + private static final FieldName FIELD_NAME_DENOMINATOR = FieldName.of("denominator"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueRatioRange"); + + private static final byte HASH_MARKER = 51; + + private static final RatioRange EMPTY = new RatioRange(ExtensionData.EMPTY, null, null, null); + + private final Quantity lowNumerator; + private final Quantity highNumerator; + private final Quantity denominator; + + private RatioRange(ExtensionData extensionData, Quantity lowNumerator, Quantity highNumerator, Quantity denominator) { + super(extensionData); + this.lowNumerator = lowNumerator; + this.highNumerator = highNumerator; + this.denominator = denominator; + } + + public static RatioRange create(IPersistentMap m) { + return new RatioRange(ExtensionData.fromMap(m), (Quantity) m.valAt(LOW_NUMERATOR), (Quantity) m.valAt(HIGH_NUMERATOR), + (Quantity) m.valAt(DENOMINATOR)); + } + + @Override + public boolean isInterned() { + return extensionData.isInterned() && Base.isInterned(lowNumerator) && Base.isInterned(highNumerator) && + Base.isInterned(denominator); + } + + public Quantity lowNumerator() { + return lowNumerator; + } + + public Quantity highNumerator() { + return highNumerator; + } + + public Quantity denominator() { + return denominator; + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE; + if (key == LOW_NUMERATOR) return lowNumerator; + if (key == HIGH_NUMERATOR) return highNumerator; + if (key == DENOMINATOR) return denominator; + return super.valAt(key, notFound); + } + + @Override + public ISeq seq() { + ISeq seq = PersistentList.EMPTY; + seq = appendElement(seq, DENOMINATOR, denominator); + seq = appendElement(seq, HIGH_NUMERATOR, highNumerator); + seq = appendElement(seq, LOW_NUMERATOR, lowNumerator); + return extensionData.append(seq); + } + + @Override + public RatioRange empty() { + return EMPTY; + } + + @Override + public Iterator> iterator() { + return new BaseIterator(this, FIELDS); + } + + @Override + public RatioRange assoc(Object key, Object val) { + if (key == LOW_NUMERATOR) return new RatioRange(extensionData, (Quantity) val, highNumerator, denominator); + if (key == HIGH_NUMERATOR) return new RatioRange(extensionData, lowNumerator, (Quantity) val, denominator); + if (key == DENOMINATOR) return new RatioRange(extensionData, lowNumerator, highNumerator, (Quantity) val); + if (key == ID) return new RatioRange(extensionData.withId(val), lowNumerator, highNumerator, denominator); + if (key == EXTENSION) + return new RatioRange(extensionData.withExtension(val), lowNumerator, highNumerator, denominator); + return this; + } + + @Override + public RatioRange withMeta(IPersistentMap meta) { + return new RatioRange(extensionData.withMeta(meta), lowNumerator, highNumerator, denominator); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeAsJsonValue(JsonGenerator generator) throws IOException { + generator.writeStartObject(); + serializeJsonBase(generator); + if (lowNumerator != null) { + lowNumerator.serializeJsonField(generator, FIELD_NAME_LOW_NUMERATOR); + } + if (highNumerator != null) { + highNumerator.serializeJsonField(generator, FIELD_NAME_HIGH_NUMERATOR); + } + if (denominator != null) { + denominator.serializeJsonField(generator, FIELD_NAME_DENOMINATOR); + } + generator.writeEndObject(); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (lowNumerator != null) { + sink.putByte((byte) 2); + lowNumerator.hashInto(sink); + } + if (highNumerator != null) { + sink.putByte((byte) 3); + highNumerator.hashInto(sink); + } + if (denominator != null) { + sink.putByte((byte) 4); + denominator.hashInto(sink); + } + } + + @Override + public int memSize() { + return MEM_SIZE_OBJECT + extensionData.memSize() + Base.memSize(lowNumerator) + Base.memSize(highNumerator) + + Base.memSize(denominator); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof RatioRange that && + extensionData.equals(that.extensionData) && + Objects.equals(lowNumerator, that.lowNumerator) && + Objects.equals(highNumerator, that.highNumerator) && + Objects.equals(denominator, that.denominator); + } + + @Override + public int hashCode() { + int result = extensionData.hashCode(); + result = 31 * result + Objects.hashCode(lowNumerator); + result = 31 * result + Objects.hashCode(highNumerator); + result = 31 * result + Objects.hashCode(denominator); + return result; + } + + @Override + public String toString() { + return "RatioRange{" + + extensionData + + ", lowNumerator=" + lowNumerator + + ", highNumerator=" + highNumerator + + ", denominator=" + denominator + + '}'; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Reference.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Reference.java new file mode 100644 index 000000000..7a0a74c3d --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Reference.java @@ -0,0 +1,264 @@ +package blaze.fhir.spec.type; + +import clojure.lang.*; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.util.Iterator; +import java.util.Objects; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +import static blaze.fhir.spec.type.Base.appendElement; + +@SuppressWarnings("DuplicatedCode") +public final class Reference extends AbstractElement implements Complex, ExtensionValue { + + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - extension data reference + * 4 byte - reference reference + * 4 byte - type reference + * 4 byte - identifier reference + * 4 byte - display reference + * 4 byte - padding + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 24; + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "Reference"); + + private static final Keyword REFERENCE = RT.keyword(null, "reference"); + private static final Keyword TYPE = RT.keyword(null, "type"); + private static final Keyword IDENTIFIER = RT.keyword(null, "identifier"); + private static final Keyword DISPLAY = RT.keyword(null, "display"); + + private static final Keyword[] FIELDS = {ID, EXTENSION, REFERENCE, TYPE, IDENTIFIER, DISPLAY}; + + private static final FieldName FIELD_NAME_REFERENCE = FieldName.of("reference"); + private static final FieldName FIELD_NAME_TYPE = FieldName.of("type"); + private static final FieldName FIELD_NAME_IDENTIFIER = FieldName.of("identifier"); + private static final FieldName FIELD_NAME_DISPLAY = FieldName.of("display"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueReference"); + + private static final byte HASH_MARKER = 43; + + private static final Reference EMPTY = new Reference(ExtensionData.EMPTY, null, null, null, null); + + private static final Pattern TYPE_PATTERN = Pattern.compile("[A-Z]([A-Za-z0-9_]){0,254}"); + private static final Pattern ID_PATTERN = Pattern.compile("[A-Za-z0-9\\-.]{1,64}"); + + private static final ILookupThunk FHIR_TYPE_LOOKUP_THUNK = new ILookupThunk() { + @Override + public Object get(Object target) { + return target instanceof Reference ? FHIR_TYPE : this; + } + }; + + private static final ILookupThunk REFERENCE_LOOKUP_THUNK = new ILookupThunk() { + @Override + public Object get(Object target) { + return target instanceof Reference r ? r.reference : this; + } + }; + + private final String reference; + private final Uri type; + private final Identifier identifier; + private final String display; + + private Reference(ExtensionData extensionData, String reference, Uri type, Identifier identifier, String display) { + super(extensionData); + this.reference = reference; + this.type = type; + this.identifier = identifier; + this.display = display; + } + + public static Reference create(IPersistentMap m) { + return new Reference(ExtensionData.fromMap(m), (String) m.valAt(REFERENCE), (Uri) m.valAt(TYPE), + (Identifier) m.valAt(IDENTIFIER), (String) m.valAt(DISPLAY)); + } + + public String reference() { + return reference; + } + + public Uri type() { + return type; + } + + public Identifier identifier() { + return identifier; + } + + public String display() { + return display; + } + + @Override + public ILookupThunk getLookupThunk(Keyword key) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE_LOOKUP_THUNK; + if (key == REFERENCE) return REFERENCE_LOOKUP_THUNK; + return super.getLookupThunk(key); + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE; + if (key == REFERENCE) return reference; + if (key == TYPE) return type; + if (key == IDENTIFIER) return identifier; + if (key == DISPLAY) return display; + return super.valAt(key, notFound); + } + + @Override + public ISeq seq() { + ISeq seq = PersistentList.EMPTY; + seq = appendElement(seq, DISPLAY, display); + seq = appendElement(seq, IDENTIFIER, identifier); + seq = appendElement(seq, TYPE, type); + seq = appendElement(seq, REFERENCE, reference); + return extensionData.append(seq); + } + + @Override + public Reference empty() { + return EMPTY; + } + + @Override + public Iterator> iterator() { + return new BaseIterator(this, FIELDS); + } + + @Override + public Reference assoc(Object key, Object val) { + if (key == REFERENCE) return new Reference(extensionData, (String) val, type, identifier, display); + if (key == TYPE) return new Reference(extensionData, reference, (Uri) val, identifier, display); + if (key == IDENTIFIER) return new Reference(extensionData, reference, type, (Identifier) val, display); + if (key == DISPLAY) return new Reference(extensionData, reference, type, identifier, (String) val); + if (key == EXTENSION) + return new Reference(extensionData.withExtension(val), reference, type, identifier, display); + if (key == ID) return new Reference(extensionData.withId(val), reference, type, identifier, display); + return this; + } + + @Override + public Reference withMeta(IPersistentMap meta) { + return new Reference(extensionData.withMeta(meta), reference, type, identifier, display); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeAsJsonValue(JsonGenerator generator) throws IOException { + generator.writeStartObject(); + serializeJsonBase(generator); + if (reference != null) { + reference.serializeAsJsonProperty(generator, FIELD_NAME_REFERENCE); + } + if (type != null) { + type.serializeAsJsonProperty(generator, FIELD_NAME_TYPE); + } + if (identifier != null) { + identifier.serializeJsonField(generator, FIELD_NAME_IDENTIFIER); + } + if (display != null) { + display.serializeAsJsonProperty(generator, FIELD_NAME_DISPLAY); + } + generator.writeEndObject(); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (reference != null) { + sink.putByte((byte) 2); + reference.hashInto(sink); + } + if (type != null) { + sink.putByte((byte) 3); + type.hashInto(sink); + } + if (identifier != null) { + sink.putByte((byte) 4); + identifier.hashInto(sink); + } + if (display != null) { + sink.putByte((byte) 5); + display.hashInto(sink); + } + } + + private static boolean isType(java.lang.String x) { + return TYPE_PATTERN.matcher(x).matches(); + } + + private static boolean isId(java.lang.String x) { + return ID_PATTERN.matcher(x).matches(); + } + + private Stream ref() { + var ref = reference == null ? null : reference.value(); + if (ref != null) { + var parts = ref.split("/"); + if (parts.length == 2 && isType(parts[0]) && isId(parts[1])) { + return Stream.of(PersistentVector.adopt(parts)); + } + } + return Stream.empty(); + } + + @Override + public Stream references() { + return Stream.concat(super.references(), ref()); + } + + @Override + public int memSize() { + return MEM_SIZE_OBJECT + extensionData.memSize() + Base.memSize(reference) + Base.memSize(type) + + Base.memSize(identifier) + Base.memSize(display); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof Reference that && + extensionData.equals(that.extensionData) && + Objects.equals(reference, that.reference) && + Objects.equals(type, that.type) && + Objects.equals(identifier, that.identifier) && + Objects.equals(display, that.display); + } + + @Override + public int hashCode() { + int result = extensionData.hashCode(); + result = 31 * result + Objects.hashCode(reference); + result = 31 * result + Objects.hashCode(type); + result = 31 * result + Objects.hashCode(identifier); + result = 31 * result + Objects.hashCode(display); + return result; + } + + @Override + public java.lang.String toString() { + return "Reference{" + + extensionData + + ", reference='" + reference + '\'' + + ", type=" + type + + ", identifier=" + identifier + + ", display='" + display + '\'' + + '}'; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/RelatedArtifact.java b/modules/fhir-structure/java/blaze/fhir/spec/type/RelatedArtifact.java new file mode 100644 index 000000000..76a0100f6 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/RelatedArtifact.java @@ -0,0 +1,290 @@ +package blaze.fhir.spec.type; + +import clojure.lang.*; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.util.Iterator; +import java.util.Objects; + +import static blaze.fhir.spec.type.Base.appendElement; + +@SuppressWarnings("DuplicatedCode") +public final class RelatedArtifact extends AbstractElement implements Complex, ExtensionValue { + + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - extension data reference + * 4 byte - type reference + * 4 byte - label reference + * 4 byte - display reference + * 4 byte - citation reference + * 4 byte - url reference + * 4 byte - document reference + * 4 byte - resource reference + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 32; + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "RelatedArtifact"); + + private static final Keyword TYPE = RT.keyword(null, "type"); + private static final Keyword LABEL = RT.keyword(null, "label"); + private static final Keyword DISPLAY = RT.keyword(null, "display"); + private static final Keyword CITATION = RT.keyword(null, "citation"); + private static final Keyword URL = RT.keyword(null, "url"); + private static final Keyword DOCUMENT = RT.keyword(null, "document"); + private static final Keyword RESOURCE = RT.keyword(null, "resource"); + + private static final Keyword[] FIELDS = {ID, EXTENSION, TYPE, LABEL, DISPLAY, CITATION, URL, DOCUMENT, RESOURCE}; + + private static final FieldName FIELD_NAME_TYPE = FieldName.of("type"); + private static final FieldName FIELD_NAME_LABEL = FieldName.of("label"); + private static final FieldName FIELD_NAME_DISPLAY = FieldName.of("display"); + private static final FieldName FIELD_NAME_CITATION = FieldName.of("citation"); + private static final FieldName FIELD_NAME_URL = FieldName.of("url"); + private static final FieldName FIELD_NAME_DOCUMENT = FieldName.of("document"); + private static final FieldName FIELD_NAME_RESOURCE = FieldName.of("resource"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueRelatedArtifact"); + + private static final byte HASH_MARKER = 54; + + private static final RelatedArtifact EMPTY = new RelatedArtifact(ExtensionData.EMPTY, null, null, null, null, null, null, null); + + private final Code type; + private final String label; + private final String display; + private final Markdown citation; + private final Url url; + private final Attachment document; + private final Canonical resource; + + private RelatedArtifact(ExtensionData extensionData, Code type, String label, String display, Markdown citation, + Url url, Attachment document, Canonical resource) { + super(extensionData); + this.type = type; + this.label = label; + this.display = display; + this.citation = citation; + this.url = url; + this.document = document; + this.resource = resource; + } + + public static RelatedArtifact create(IPersistentMap m) { + return new RelatedArtifact(ExtensionData.fromMap(m), (Code) m.valAt(TYPE), (String) m.valAt(LABEL), + (String) m.valAt(DISPLAY), (Markdown) m.valAt(CITATION), (Url) m.valAt(URL), + (Attachment) m.valAt(DOCUMENT), (Canonical) m.valAt(RESOURCE)); + } + + public Code type() { + return type; + } + + public String label() { + return label; + } + + public String display() { + return display; + } + + public Markdown citation() { + return citation; + } + + public Url url() { + return url; + } + + public Attachment document() { + return document; + } + + public Canonical resource() { + return resource; + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE; + if (key == TYPE) return type; + if (key == LABEL) return label; + if (key == DISPLAY) return display; + if (key == CITATION) return citation; + if (key == URL) return url; + if (key == DOCUMENT) return document; + if (key == RESOURCE) return resource; + return super.valAt(key, notFound); + } + + @Override + public ISeq seq() { + ISeq seq = PersistentList.EMPTY; + seq = appendElement(seq, RESOURCE, resource); + seq = appendElement(seq, DOCUMENT, document); + seq = appendElement(seq, URL, url); + seq = appendElement(seq, CITATION, citation); + seq = appendElement(seq, DISPLAY, display); + seq = appendElement(seq, LABEL, label); + seq = appendElement(seq, TYPE, type); + return extensionData.append(seq); + } + + @Override + public RelatedArtifact empty() { + return EMPTY; + } + + @Override + public Iterator> iterator() { + return new BaseIterator(this, FIELDS); + } + + @Override + public RelatedArtifact assoc(Object key, Object val) { + if (key == TYPE) + return new RelatedArtifact(extensionData, (Code) val, label, display, citation, url, document, resource); + if (key == LABEL) + return new RelatedArtifact(extensionData, type, (String) val, display, citation, url, document, resource); + if (key == DISPLAY) + return new RelatedArtifact(extensionData, type, label, (String) val, citation, url, document, resource); + if (key == CITATION) + return new RelatedArtifact(extensionData, type, label, display, (Markdown) val, url, document, resource); + if (key == URL) + return new RelatedArtifact(extensionData, type, label, display, citation, (Url) val, document, resource); + if (key == DOCUMENT) + return new RelatedArtifact(extensionData, type, label, display, citation, url, (Attachment) val, resource); + if (key == RESOURCE) + return new RelatedArtifact(extensionData, type, label, display, citation, url, document, (Canonical) val); + if (key == EXTENSION) + return new RelatedArtifact(extensionData.withExtension(val), type, label, display, citation, url, document, resource); + if (key == ID) + return new RelatedArtifact(extensionData.withId(val), type, label, display, citation, url, document, resource); + return this; + } + + @Override + public RelatedArtifact withMeta(IPersistentMap meta) { + return new RelatedArtifact(extensionData.withMeta(meta), type, label, display, citation, url, document, resource); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeAsJsonValue(JsonGenerator generator) throws IOException { + generator.writeStartObject(); + serializeJsonBase(generator); + if (type != null) { + type.serializeAsJsonProperty(generator, FIELD_NAME_TYPE); + } + if (label != null) { + label.serializeAsJsonProperty(generator, FIELD_NAME_LABEL); + } + if (display != null) { + display.serializeAsJsonProperty(generator, FIELD_NAME_DISPLAY); + } + if (citation != null) { + citation.serializeAsJsonProperty(generator, FIELD_NAME_CITATION); + } + if (url != null) { + url.serializeAsJsonProperty(generator, FIELD_NAME_URL); + } + if (document != null) { + document.serializeJsonField(generator, FIELD_NAME_DOCUMENT); + } + if (resource != null) { + resource.serializeAsJsonProperty(generator, FIELD_NAME_RESOURCE); + } + generator.writeEndObject(); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (type != null) { + sink.putByte((byte) 2); + type.hashInto(sink); + } + if (label != null) { + sink.putByte((byte) 3); + label.hashInto(sink); + } + if (display != null) { + sink.putByte((byte) 4); + display.hashInto(sink); + } + if (citation != null) { + sink.putByte((byte) 5); + citation.hashInto(sink); + } + if (url != null) { + sink.putByte((byte) 6); + url.hashInto(sink); + } + if (document != null) { + sink.putByte((byte) 7); + document.hashInto(sink); + } + if (resource != null) { + sink.putByte((byte) 8); + resource.hashInto(sink); + } + } + + @Override + public int memSize() { + return MEM_SIZE_OBJECT + extensionData.memSize() + Base.memSize(type) + Base.memSize(label) + + Base.memSize(display) + Base.memSize(citation) + Base.memSize(url) + Base.memSize(document) + + Base.memSize(resource); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof RelatedArtifact that && + extensionData.equals(that.extensionData) && + Objects.equals(type, that.type) && + Objects.equals(label, that.label) && + Objects.equals(display, that.display) && + Objects.equals(citation, that.citation) && + Objects.equals(url, that.url) && + Objects.equals(document, that.document) && + Objects.equals(resource, that.resource); + } + + @Override + public int hashCode() { + int result = extensionData.hashCode(); + result = 31 * result + Objects.hashCode(type); + result = 31 * result + Objects.hashCode(label); + result = 31 * result + Objects.hashCode(display); + result = 31 * result + Objects.hashCode(citation); + result = 31 * result + Objects.hashCode(url); + result = 31 * result + Objects.hashCode(document); + result = 31 * result + Objects.hashCode(resource); + return result; + } + + @Override + public java.lang.String toString() { + return "RelatedArtifact{" + + extensionData + + ", type=" + type + + ", label=" + label + + ", display=" + display + + ", citation=" + citation + + ", url=" + url + + ", document=" + document + + ", resource=" + resource + + '}'; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/SampledData.java b/modules/fhir-structure/java/blaze/fhir/spec/type/SampledData.java new file mode 100644 index 000000000..fe80ac037 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/SampledData.java @@ -0,0 +1,290 @@ +package blaze.fhir.spec.type; + +import clojure.lang.*; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.util.Iterator; +import java.util.Objects; + +import static blaze.fhir.spec.type.Base.appendElement; + +@SuppressWarnings("DuplicatedCode") +public final class SampledData extends AbstractElement implements Complex, ExtensionValue { + + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - extension data reference + * 4 byte - origin reference + * 4 byte - period reference + * 4 byte - factor reference + * 4 byte - lowerLimit reference + * 4 byte - upperLimit reference + * 4 byte - dimensions reference + * 4 byte - data reference + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 32; + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "SampledData"); + + private static final Keyword ORIGIN = RT.keyword(null, "origin"); + private static final Keyword PERIOD = RT.keyword(null, "period"); + private static final Keyword FACTOR = RT.keyword(null, "factor"); + private static final Keyword LOWER_LIMIT = RT.keyword(null, "lowerLimit"); + private static final Keyword UPPER_LIMIT = RT.keyword(null, "upperLimit"); + private static final Keyword DIMENSIONS = RT.keyword(null, "dimensions"); + private static final Keyword DATA = RT.keyword(null, "data"); + + private static final Keyword[] FIELDS = {ID, EXTENSION, ORIGIN, PERIOD, FACTOR, LOWER_LIMIT, UPPER_LIMIT, DIMENSIONS, DATA}; + + private static final FieldName FIELD_NAME_ORIGIN = FieldName.of("origin"); + private static final FieldName FIELD_NAME_PERIOD = FieldName.of("period"); + private static final FieldName FIELD_NAME_FACTOR = FieldName.of("factor"); + private static final FieldName FIELD_NAME_LOWER_LIMIT = FieldName.of("lowerLimit"); + private static final FieldName FIELD_NAME_UPPER_LIMIT = FieldName.of("upperLimit"); + private static final FieldName FIELD_NAME_DIMENSIONS = FieldName.of("dimensions"); + private static final FieldName FIELD_NAME_DATA = FieldName.of("data"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueSampledData"); + + private static final byte HASH_MARKER = 56; + + private static final SampledData EMPTY = new SampledData(ExtensionData.EMPTY, null, null, null, null, null, null, null); + + private final Quantity origin; + private final Decimal period; + private final Decimal factor; + private final Decimal lowerLimit; + private final Decimal upperLimit; + private final PositiveInt dimensions; + private final String data; + + private SampledData(ExtensionData extensionData, Quantity origin, Decimal period, Decimal factor, + Decimal lowerLimit, Decimal upperLimit, PositiveInt dimensions, String data) { + super(extensionData); + this.origin = origin; + this.period = period; + this.factor = factor; + this.lowerLimit = lowerLimit; + this.upperLimit = upperLimit; + this.dimensions = dimensions; + this.data = data; + } + + public static SampledData create(IPersistentMap m) { + return new SampledData(ExtensionData.fromMap(m), (Quantity) m.valAt(ORIGIN), (Decimal) m.valAt(PERIOD), + (Decimal) m.valAt(FACTOR), (Decimal) m.valAt(LOWER_LIMIT), (Decimal) m.valAt(UPPER_LIMIT), + (PositiveInt) m.valAt(DIMENSIONS), (String) m.valAt(DATA)); + } + + public Quantity origin() { + return origin; + } + + public Decimal period() { + return period; + } + + public Decimal factor() { + return factor; + } + + public Decimal lowerLimit() { + return lowerLimit; + } + + public Decimal upperLimit() { + return upperLimit; + } + + public PositiveInt dimensions() { + return dimensions; + } + + public String data() { + return data; + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE; + if (key == ORIGIN) return origin; + if (key == PERIOD) return period; + if (key == FACTOR) return factor; + if (key == LOWER_LIMIT) return lowerLimit; + if (key == UPPER_LIMIT) return upperLimit; + if (key == DIMENSIONS) return dimensions; + if (key == DATA) return data; + return super.valAt(key, notFound); + } + + @Override + public ISeq seq() { + ISeq seq = PersistentList.EMPTY; + seq = appendElement(seq, DATA, data); + seq = appendElement(seq, DIMENSIONS, dimensions); + seq = appendElement(seq, UPPER_LIMIT, upperLimit); + seq = appendElement(seq, LOWER_LIMIT, lowerLimit); + seq = appendElement(seq, FACTOR, factor); + seq = appendElement(seq, PERIOD, period); + seq = appendElement(seq, ORIGIN, origin); + return extensionData.append(seq); + } + + @Override + public SampledData empty() { + return EMPTY; + } + + @Override + public Iterator> iterator() { + return new BaseIterator(this, FIELDS); + } + + @Override + public SampledData assoc(Object key, Object val) { + if (key == ORIGIN) + return new SampledData(extensionData, (Quantity) val, period, factor, lowerLimit, upperLimit, dimensions, data); + if (key == PERIOD) + return new SampledData(extensionData, origin, (Decimal) val, factor, lowerLimit, upperLimit, dimensions, data); + if (key == FACTOR) + return new SampledData(extensionData, origin, period, (Decimal) val, lowerLimit, upperLimit, dimensions, data); + if (key == LOWER_LIMIT) + return new SampledData(extensionData, origin, period, factor, (Decimal) val, upperLimit, dimensions, data); + if (key == UPPER_LIMIT) + return new SampledData(extensionData, origin, period, factor, lowerLimit, (Decimal) val, dimensions, data); + if (key == DIMENSIONS) + return new SampledData(extensionData, origin, period, factor, lowerLimit, upperLimit, (PositiveInt) val, data); + if (key == DATA) + return new SampledData(extensionData, origin, period, factor, lowerLimit, upperLimit, dimensions, (String) val); + if (key == EXTENSION) + return new SampledData(extensionData.withExtension(val), origin, period, factor, lowerLimit, upperLimit, dimensions, data); + if (key == ID) + return new SampledData(extensionData.withId(val), origin, period, factor, lowerLimit, upperLimit, dimensions, data); + return this; + } + + @Override + public SampledData withMeta(IPersistentMap meta) { + return new SampledData(extensionData.withMeta(meta), origin, period, factor, lowerLimit, upperLimit, dimensions, data); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeAsJsonValue(JsonGenerator generator) throws IOException { + generator.writeStartObject(); + serializeJsonBase(generator); + if (origin != null) { + origin.serializeJsonField(generator, FIELD_NAME_ORIGIN); + } + if (period != null) { + period.serializeAsJsonProperty(generator, FIELD_NAME_PERIOD); + } + if (factor != null) { + factor.serializeAsJsonProperty(generator, FIELD_NAME_FACTOR); + } + if (lowerLimit != null) { + lowerLimit.serializeAsJsonProperty(generator, FIELD_NAME_LOWER_LIMIT); + } + if (upperLimit != null) { + upperLimit.serializeAsJsonProperty(generator, FIELD_NAME_UPPER_LIMIT); + } + if (dimensions != null) { + dimensions.serializeAsJsonProperty(generator, FIELD_NAME_DIMENSIONS); + } + if (data != null) { + data.serializeAsJsonProperty(generator, FIELD_NAME_DATA); + } + generator.writeEndObject(); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (origin != null) { + sink.putByte((byte) 2); + origin.hashInto(sink); + } + if (period != null) { + sink.putByte((byte) 3); + period.hashInto(sink); + } + if (factor != null) { + sink.putByte((byte) 4); + factor.hashInto(sink); + } + if (lowerLimit != null) { + sink.putByte((byte) 5); + lowerLimit.hashInto(sink); + } + if (upperLimit != null) { + sink.putByte((byte) 6); + upperLimit.hashInto(sink); + } + if (dimensions != null) { + sink.putByte((byte) 7); + dimensions.hashInto(sink); + } + if (data != null) { + sink.putByte((byte) 8); + data.hashInto(sink); + } + } + + @Override + public int memSize() { + return MEM_SIZE_OBJECT + extensionData.memSize() + Base.memSize(origin) + Base.memSize(period) + + Base.memSize(factor) + Base.memSize(lowerLimit) + Base.memSize(upperLimit) + + Base.memSize(dimensions) + Base.memSize(data); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof SampledData that && + extensionData.equals(that.extensionData) && + Objects.equals(origin, that.origin) && + Objects.equals(period, that.period) && + Objects.equals(factor, that.factor) && + Objects.equals(lowerLimit, that.lowerLimit) && + Objects.equals(upperLimit, that.upperLimit) && + Objects.equals(dimensions, that.dimensions) && + Objects.equals(data, that.data); + } + + @Override + public int hashCode() { + int result = extensionData.hashCode(); + result = 31 * result + Objects.hashCode(origin); + result = 31 * result + Objects.hashCode(period); + result = 31 * result + Objects.hashCode(factor); + result = 31 * result + Objects.hashCode(lowerLimit); + result = 31 * result + Objects.hashCode(upperLimit); + result = 31 * result + Objects.hashCode(dimensions); + result = 31 * result + Objects.hashCode(data); + return result; + } + + @Override + public java.lang.String toString() { + return "SampledData{" + + extensionData + + ", origin=" + origin + + ", period=" + period + + ", factor=" + factor + + ", lowerLimit=" + lowerLimit + + ", upperLimit=" + upperLimit + + ", dimensions=" + dimensions + + ", data=" + data + + '}'; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Signature.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Signature.java new file mode 100644 index 000000000..a2575b352 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Signature.java @@ -0,0 +1,293 @@ +package blaze.fhir.spec.type; + +import clojure.lang.IPersistentMap; +import clojure.lang.ISeq; +import clojure.lang.Keyword; +import clojure.lang.PersistentList; +import clojure.lang.PersistentVector; +import clojure.lang.RT; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; +import java.io.IOException; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; + +import static blaze.fhir.spec.type.Base.appendElement; +import static blaze.fhir.spec.type.Complex.serializeJsonComplexList; +import static java.util.Objects.requireNonNull; + +@SuppressWarnings("DuplicatedCode") +public final class Signature extends AbstractElement implements Complex, ExtensionValue { + + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - extension data reference + * 4 byte - type reference + * 4 byte - when reference + * 4 byte - who reference + * 4 byte - onBehalfOf reference + * 4 byte - targetFormat reference + * 4 byte - sigFormat reference + * 4 byte - data reference + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 32; + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "Signature"); + + private static final Keyword TYPE = RT.keyword(null, "type"); + private static final Keyword WHEN = RT.keyword(null, "when"); + private static final Keyword WHO = RT.keyword(null, "who"); + private static final Keyword ON_BEHALF_OF = RT.keyword(null, "onBehalfOf"); + private static final Keyword TARGET_FORMAT = RT.keyword(null, "targetFormat"); + private static final Keyword SIG_FORMAT = RT.keyword(null, "sigFormat"); + private static final Keyword DATA = RT.keyword(null, "data"); + + private static final Keyword[] FIELDS = {ID, EXTENSION, TYPE, WHEN, WHO, ON_BEHALF_OF, TARGET_FORMAT, SIG_FORMAT, DATA}; + + private static final FieldName FIELD_NAME_TYPE = FieldName.of("type"); + private static final FieldName FIELD_NAME_WHEN = FieldName.of("when"); + private static final FieldName FIELD_NAME_WHO = FieldName.of("who"); + private static final FieldName FIELD_NAME_ON_BEHALF_OF = FieldName.of("onBehalfOf"); + private static final FieldName FIELD_NAME_TARGET_FORMAT = FieldName.of("targetFormat"); + private static final FieldName FIELD_NAME_SIG_FORMAT = FieldName.of("sigFormat"); + private static final FieldName FIELD_NAME_DATA = FieldName.of("data"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueSignature"); + + private static final byte HASH_MARKER = 59; + + @SuppressWarnings("unchecked") + private static final Signature EMPTY = new Signature(ExtensionData.EMPTY, PersistentVector.EMPTY, null, null, null, + null, null, null); + + private final List type; + private final Instant when; + private final Reference who; + private final Reference onBehalfOf; + private final Code targetFormat; + private final Code sigFormat; + private final Base64Binary data; + + private Signature(ExtensionData extensionData, List type, Instant when, Reference who, + Reference onBehalfOf, Code targetFormat, Code sigFormat, Base64Binary data) { + super(extensionData); + this.type = requireNonNull(type); + this.when = when; + this.who = who; + this.onBehalfOf = onBehalfOf; + this.targetFormat = targetFormat; + this.sigFormat = sigFormat; + this.data = data; + } + + public static Signature create(IPersistentMap m) { + return new Signature(ExtensionData.fromMap(m), Base.listFrom(m, TYPE), (Instant) m.valAt(WHEN), + (Reference) m.valAt(WHO), (Reference) m.valAt(ON_BEHALF_OF), (Code) m.valAt(TARGET_FORMAT), + (Code) m.valAt(SIG_FORMAT), (Base64Binary) m.valAt(DATA)); + } + + public List type() { + return type; + } + + public Instant when() { + return when; + } + + public Reference who() { + return who; + } + + public Reference onBehalfOf() { + return onBehalfOf; + } + + public Code targetFormat() { + return targetFormat; + } + + public Code sigFormat() { + return sigFormat; + } + + public Base64Binary data() { + return data; + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE; + if (key == TYPE) return type; + if (key == WHEN) return when; + if (key == WHO) return who; + if (key == ON_BEHALF_OF) return onBehalfOf; + if (key == TARGET_FORMAT) return targetFormat; + if (key == SIG_FORMAT) return sigFormat; + if (key == DATA) return data; + return super.valAt(key, notFound); + } + + @Override + public ISeq seq() { + ISeq seq = PersistentList.EMPTY; + seq = appendElement(seq, DATA, data); + seq = appendElement(seq, SIG_FORMAT, sigFormat); + seq = appendElement(seq, TARGET_FORMAT, targetFormat); + seq = appendElement(seq, ON_BEHALF_OF, onBehalfOf); + seq = appendElement(seq, WHO, who); + seq = appendElement(seq, WHEN, when); + if (!type.isEmpty()) { + seq = appendElement(seq, TYPE, type); + } + return extensionData.append(seq); + } + + @Override + public Signature empty() { + return EMPTY; + } + + @Override + public Iterator> iterator() { + return new BaseIterator(this, FIELDS); + } + + @Override + public Signature assoc(Object key, Object val) { + if (key == TYPE) return new Signature(extensionData, Lists.nullToEmpty(val), when, who, onBehalfOf, targetFormat, sigFormat, data); + if (key == WHEN) return new Signature(extensionData, type, (Instant) val, who, onBehalfOf, targetFormat, sigFormat, data); + if (key == WHO) return new Signature(extensionData, type, when, (Reference) val, onBehalfOf, targetFormat, sigFormat, data); + if (key == ON_BEHALF_OF) return new Signature(extensionData, type, when, who, (Reference) val, targetFormat, sigFormat, data); + if (key == TARGET_FORMAT) return new Signature(extensionData, type, when, who, onBehalfOf, (Code) val, sigFormat, data); + if (key == SIG_FORMAT) return new Signature(extensionData, type, when, who, onBehalfOf, targetFormat, (Code) val, data); + if (key == DATA) return new Signature(extensionData, type, when, who, onBehalfOf, targetFormat, sigFormat, (Base64Binary) val); + if (key == EXTENSION) + return new Signature(extensionData.withExtension(val), type, when, who, onBehalfOf, targetFormat, sigFormat, data); + if (key == ID) return new Signature(extensionData.withId(val), type, when, who, onBehalfOf, targetFormat, sigFormat, data); + return this; + } + + @Override + public Signature withMeta(IPersistentMap meta) { + return new Signature(extensionData.withMeta(meta), type, when, who, onBehalfOf, targetFormat, sigFormat, data); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeAsJsonValue(JsonGenerator generator) throws IOException { + generator.writeStartObject(); + serializeJsonBase(generator); + if (!type.isEmpty()) { + serializeJsonComplexList(type, generator, FIELD_NAME_TYPE.normal()); + } + if (when != null) { + when.serializeAsJsonProperty(generator, FIELD_NAME_WHEN); + } + if (who != null) { + who.serializeJsonField(generator, FIELD_NAME_WHO); + } + if (onBehalfOf != null) { + onBehalfOf.serializeJsonField(generator, FIELD_NAME_ON_BEHALF_OF); + } + if (targetFormat != null) { + targetFormat.serializeAsJsonProperty(generator, FIELD_NAME_TARGET_FORMAT); + } + if (sigFormat != null) { + sigFormat.serializeAsJsonProperty(generator, FIELD_NAME_SIG_FORMAT); + } + if (data != null) { + data.serializeAsJsonProperty(generator, FIELD_NAME_DATA); + } + generator.writeEndObject(); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (!type.isEmpty()) { + sink.putByte((byte) 2); + Base.hashIntoList(type, sink); + } + if (when != null) { + sink.putByte((byte) 3); + when.hashInto(sink); + } + if (who != null) { + sink.putByte((byte) 4); + who.hashInto(sink); + } + if (onBehalfOf != null) { + sink.putByte((byte) 5); + onBehalfOf.hashInto(sink); + } + if (targetFormat != null) { + sink.putByte((byte) 6); + targetFormat.hashInto(sink); + } + if (sigFormat != null) { + sink.putByte((byte) 7); + sigFormat.hashInto(sink); + } + if (data != null) { + sink.putByte((byte) 8); + data.hashInto(sink); + } + } + + @Override + public int memSize() { + return MEM_SIZE_OBJECT + extensionData.memSize() + Base.memSize(type) + Base.memSize(when) + + Base.memSize(who) + Base.memSize(onBehalfOf) + Base.memSize(targetFormat) + + Base.memSize(sigFormat) + Base.memSize(data); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof Signature that && + extensionData.equals(that.extensionData) && + Objects.equals(type, that.type) && + Objects.equals(when, that.when) && + Objects.equals(who, that.who) && + Objects.equals(onBehalfOf, that.onBehalfOf) && + Objects.equals(targetFormat, that.targetFormat) && + Objects.equals(sigFormat, that.sigFormat) && + Objects.equals(data, that.data); + } + + @Override + public int hashCode() { + int result = extensionData.hashCode(); + result = 31 * result + Objects.hashCode(type); + result = 31 * result + Objects.hashCode(when); + result = 31 * result + Objects.hashCode(who); + result = 31 * result + Objects.hashCode(onBehalfOf); + result = 31 * result + Objects.hashCode(targetFormat); + result = 31 * result + Objects.hashCode(sigFormat); + result = 31 * result + Objects.hashCode(data); + return result; + } + + @Override + public java.lang.String toString() { + return "Signature{" + + extensionData + + ", type=" + type + + ", when=" + when + + ", who=" + who + + ", onBehalfOf=" + onBehalfOf + + ", targetFormat=" + targetFormat + + ", sigFormat=" + sigFormat + + ", data=" + data + + '}'; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/String.java b/modules/fhir-structure/java/blaze/fhir/spec/type/String.java new file mode 100644 index 000000000..c21cb4173 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/String.java @@ -0,0 +1,258 @@ +package blaze.fhir.spec.type; + +import blaze.Interner; +import blaze.Interners; +import blaze.fhir.spec.type.system.Strings; +import clojure.lang.IPersistentMap; +import clojure.lang.Keyword; +import clojure.lang.RT; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.io.SerializedString; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.util.Objects; + +import static java.util.Objects.requireNonNull; + +@SuppressWarnings("DuplicatedCode") +public sealed abstract class String extends PrimitiveElement permits String.Normal, String.Interned { + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "string"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueString"); + + private static final byte HASH_MARKER = 3; + + private String(ExtensionData extensionData) { + super(extensionData); + } + + private static String maybeIntern(ExtensionData extensionData, java.lang.String value) { + return extensionData.isInterned() && (value == null || value.length() <= 4) + ? Interned.intern(extensionData, value) + : new Normal(extensionData, value); + } + + public static String create(java.lang.String value) { + return value.length() <= 4 ? Interned.intern(ExtensionData.EMPTY, value) : new Normal(ExtensionData.EMPTY, value); + } + + public static String create(IPersistentMap m) { + return maybeIntern(ExtensionData.fromMap(m), (java.lang.String) m.valAt(VALUE)); + } + + public static String createForceIntern(java.lang.String value) { + return Interned.intern(ExtensionData.EMPTY, requireNonNull(value)); + } + + public static String createForceIntern(IPersistentMap m) { + var extensionData = ExtensionData.fromMap(m); + var value = (java.lang.String) m.valAt(VALUE); + return extensionData.isInterned() ? Interned.intern(extensionData, value) : new Normal(extensionData, value); + } + + /** + * Creates the hash of a FHIR string with {@code value} without creating the + * FHIR string first. + * + * @param sink the sink for the hash bytes + * @param value the string value to hash + */ + @SuppressWarnings("UnstableApiUsage") + public static void hashIntoValue(PrimitiveSink sink, java.lang.String value) { + sink.putByte(HASH_MARKER); + if (value != null) { + sink.putByte((byte) 2); + Strings.hashInto(value, sink); + } + } + + @Override + public abstract java.lang.String value(); + + @Override + public Object valAt(Object key, Object notFound) { + return key == FHIR_TYPE_KEY ? FHIR_TYPE : super.valAt(key, notFound); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (hasValue()) { + sink.putByte((byte) 2); + Strings.hashInto(value(), sink); + } + } + + @Override + public final boolean equals(Object o) { + if (this == o) return true; + return o instanceof String that && + Objects.equals(extensionData, that.extensionData) && + Objects.equals(value(), that.value()); + } + + @Override + public final int hashCode() { + return 31 * extensionData.hashCode() + Objects.hashCode(value()); + } + + @Override + public final java.lang.String toString() { + return "String{" + + extensionData + + ", value=" + (value() == null ? null : '\'' + value() + '\'') + + '}'; + } + + public static final class Normal extends String { + + private static final Normal EMPTY = new Normal(ExtensionData.EMPTY, null); + + private final java.lang.String value; + + private Normal(ExtensionData extensionData, java.lang.String value) { + super(extensionData); + this.value = value; + } + + public static Normal create(IPersistentMap m) { + return new Normal(ExtensionData.fromMap(m), (java.lang.String) m.valAt(VALUE)); + } + + @Override + public java.lang.String value() { + return value; + } + + @Override + public Object valAt(Object key, Object notFound) { + return key == FHIR_TYPE_KEY ? FHIR_TYPE : super.valAt(key, notFound); + } + + @Override + public Normal empty() { + return EMPTY; + } + + @Override + public String assoc(Object key, Object val) { + if (key == VALUE) return maybeIntern(extensionData, (java.lang.String) val); + if (key == EXTENSION) return maybeIntern(extensionData.withExtension(val), value); + if (key == ID) return maybeIntern(extensionData.withId(val), value); + return this; + } + + @Override + public String withMeta(IPersistentMap meta) { + return maybeIntern(extensionData.withMeta(meta), value); + } + + @Override + public void serializeJsonPrimitiveValue(JsonGenerator generator) throws IOException { + if (hasValue()) { + generator.writeString(value); + } else { + generator.writeNull(); + } + } + + @Override + public int memSize() { + return super.memSize() + Strings.memSize(value); + } + } + + public static final class Interned extends String { + + private static final Interned EMPTY = new Interned(ExtensionData.EMPTY, null); + private static final Interner INTERNER = Interners.weakInterner(k -> create(k.extensionData, k.value)); + + private final SerializedString value; + + private Interned(ExtensionData extensionData, SerializedString value) { + super(extensionData); + this.value = value; + } + + private static Interned create(ExtensionData extensionData, java.lang.String value) { + return new Interned(extensionData, value == null ? null : new SerializedString(value)); + } + + private static String maybeIntern(ExtensionData extensionData, java.lang.String value) { + return extensionData.isInterned() ? intern(extensionData, value) : new Normal(extensionData, value); + } + + private static Interned intern(ExtensionData extensionData, java.lang.String value) { + return INTERNER.intern(new InternerKey(extensionData, value)); + } + + public static Interned create(IPersistentMap m) { + var extensionData = ExtensionData.fromMap(m); + var value = (java.lang.String) m.valAt(VALUE); + if (extensionData.isInterned()) return intern(extensionData, value); + throw new IllegalArgumentException("Can't create an interned FHIR.String using non-interned extension data."); + } + + @Override + public boolean isInterned() { + return true; + } + + @Override + public java.lang.String value() { + return value == null ? null : value.getValue(); + } + + @Override + public Object valAt(Object key, Object notFound) { + return key == FHIR_TYPE_KEY ? FHIR_TYPE : super.valAt(key, notFound); + } + + @Override + public Interned empty() { + return EMPTY; + } + + @Override + public String assoc(Object key, Object val) { + if (key == VALUE) return maybeIntern(extensionData, (java.lang.String) val); + if (key == EXTENSION) return maybeIntern(extensionData.withExtension(val), value()); + if (key == ID) return maybeIntern(extensionData.withId(val), value()); + return this; + } + + @Override + public String withMeta(IPersistentMap meta) { + return maybeIntern(extensionData.withMeta(meta), value()); + } + + @Override + public void serializeJsonPrimitiveValue(JsonGenerator generator) throws IOException { + if (hasValue()) { + generator.writeString(value); + } else { + generator.writeNull(); + } + } + + @Override + public int memSize() { + return 0; + } + } + + private record InternerKey(ExtensionData extensionData, java.lang.String value) { + private InternerKey { + requireNonNull(extensionData); + } + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Time.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Time.java new file mode 100644 index 000000000..dd6560ebc --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Time.java @@ -0,0 +1,132 @@ +package blaze.fhir.spec.type; + +import blaze.Interner; +import blaze.Interners; +import blaze.fhir.spec.type.system.Times; +import clojure.lang.IPersistentMap; +import clojure.lang.Keyword; +import clojure.lang.RT; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.lang.String; +import java.time.LocalTime; +import java.util.Objects; + +@SuppressWarnings("DuplicatedCode") +public final class Time extends PrimitiveElement { + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "time"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueTime"); + + private static final byte HASH_MARKER = 12; + + private static final Interner INTERNER = Interners.weakInterner(k -> new Time(k, null)); + private static final Time EMPTY = new Time(ExtensionData.EMPTY, null); + + private final LocalTime value; + + private Time(ExtensionData extensionData, LocalTime value) { + super(extensionData); + this.value = value; + } + + private static Time maybeIntern(ExtensionData extensionData, LocalTime value) { + return extensionData.isInterned() && value == null ? INTERNER.intern(extensionData) : new Time(extensionData, value); + } + + public static Time create(LocalTime value) { + return value == null ? EMPTY : new Time(ExtensionData.EMPTY, value); + } + + public static Time create(IPersistentMap m) { + return maybeIntern(ExtensionData.fromMap(m), (LocalTime) m.valAt(VALUE)); + } + + public LocalTime value() { + return value; + } + + @Override + public Object valAt(Object key, Object notFound) { + return key == FHIR_TYPE_KEY ? FHIR_TYPE : super.valAt(key, notFound); + } + + @Override + public String valueAsString() { + return value == null ? null : Times.toString(value); + } + + @Override + public Time empty() { + return EMPTY; + } + + @Override + public Time assoc(Object key, Object val) { + if (key == VALUE) return maybeIntern(extensionData, (LocalTime) val); + if (key == EXTENSION) return maybeIntern(extensionData.withExtension(val), value); + if (key == ID) return maybeIntern(extensionData.withId(val), value); + return this; + } + + @Override + public Time withMeta(IPersistentMap meta) { + return maybeIntern(extensionData.withMeta(meta), value); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeJsonPrimitiveValue(JsonGenerator generator) throws IOException { + if (hasValue()) { + generator.writeString(valueAsString()); + } else { + generator.writeNull(); + } + } + + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (value != null) { + sink.putByte((byte) 2); + sink.putByte((byte) 7); + sink.putInt(value.getHour()); + sink.putInt(value.getMinute()); + sink.putInt(value.getSecond()); + sink.putInt(value.getNano()); + } + } + + @Override + public int memSize() { + return super.memSize() + Times.memSize(value); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof Time that && + extensionData.equals(that.extensionData) && + Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return 31 * extensionData.hashCode() + Objects.hashCode(value); + } + + @Override + public String toString() { + return "Time{" + extensionData + ", value=" + value + '}'; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Timing.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Timing.java new file mode 100644 index 000000000..91dde9d2c --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Timing.java @@ -0,0 +1,660 @@ +package blaze.fhir.spec.type; + +import clojure.lang.*; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.lang.String; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; + +import static blaze.fhir.spec.type.Base.appendElement; +import static java.util.Objects.requireNonNull; + +@SuppressWarnings("DuplicatedCode") +public final class Timing extends AbstractBackboneElement implements Complex, ExtensionValue { + + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - extension data reference + * 4 byte - modifierExtension reference + * 4 byte - event reference + * 4 byte - repeat reference + * 4 byte - code reference + * 4 byte - padding + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 24; + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "Timing"); + + private static final Keyword EVENT = RT.keyword(null, "event"); + private static final Keyword REPEAT = RT.keyword(null, "repeat"); + private static final Keyword CODE = RT.keyword(null, "code"); + + private static final Keyword[] FIELDS = {ID, EXTENSION, MODIFIER_EXTENSION, EVENT, REPEAT, CODE}; + + private static final FieldName FIELD_NAME_EVENT = FieldName.of("event"); + private static final FieldName FIELD_NAME_REPEAT = FieldName.of("repeat"); + private static final FieldName FIELD_NAME_CODE = FieldName.of("code"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueTiming"); + + private static final byte HASH_MARKER = 57; + + @SuppressWarnings("unchecked") + private static final Timing EMPTY = new Timing(ExtensionData.EMPTY, PersistentVector.EMPTY, PersistentVector.EMPTY, + null, null); + + private final List event; + private final Repeat repeat; + private final CodeableConcept code; + + private Timing(ExtensionData extensionData, List modifierExtension, List event, Repeat repeat, + CodeableConcept code) { + super(extensionData, modifierExtension); + this.event = requireNonNull(event); + this.repeat = repeat; + this.code = code; + } + + public static Timing create(IPersistentMap m) { + return new Timing(ExtensionData.fromMap(m), Base.listFrom(m, MODIFIER_EXTENSION), Base.listFrom(m, EVENT), + (Repeat) m.valAt(REPEAT), (CodeableConcept) m.valAt(CODE)); + } + + @Override + public boolean isInterned() { + return false; + } + + public List event() { + return event; + } + + public Repeat repeat() { + return repeat; + } + + public CodeableConcept code() { + return code; + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE; + if (key == EVENT) return event; + if (key == REPEAT) return repeat; + if (key == CODE) return code; + return super.valAt(key, notFound); + } + + @Override + public ISeq seq() { + ISeq seq = PersistentList.EMPTY; + seq = appendElement(seq, CODE, code); + seq = appendElement(seq, REPEAT, repeat); + seq = appendElement(seq, EVENT, event); + seq = appendElement(seq, MODIFIER_EXTENSION, modifierExtension); + return extensionData.append(seq); + } + + @Override + public Timing empty() { + return EMPTY; + } + + @Override + public Iterator> iterator() { + return new BaseIterator(this, FIELDS); + } + + @Override + public Timing assoc(Object key, Object val) { + if (key == EVENT) return new Timing(extensionData, modifierExtension, Lists.nullToEmpty(val), repeat, code); + if (key == REPEAT) return new Timing(extensionData, modifierExtension, event, (Repeat) val, code); + if (key == CODE) return new Timing(extensionData, modifierExtension, event, repeat, (CodeableConcept) val); + if (key == MODIFIER_EXTENSION) return new Timing(extensionData, Lists.nullToEmpty(val), event, repeat, code); + if (key == EXTENSION) + return new Timing(extensionData.withExtension(val), modifierExtension, event, repeat, code); + if (key == ID) return new Timing(extensionData.withId(val), modifierExtension, event, repeat, code); + return this; + } + + @Override + public Timing withMeta(IPersistentMap meta) { + return new Timing(extensionData.withMeta(meta), modifierExtension, event, repeat, code); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeAsJsonValue(JsonGenerator generator) throws IOException { + generator.writeStartObject(); + serializeJsonBase(generator); + if (!event.isEmpty()) { + Primitive.serializeJsonPrimitiveList(event, generator, FIELD_NAME_EVENT); + } + if (repeat != null) { + repeat.serializeJsonField(generator, FIELD_NAME_REPEAT); + } + if (code != null) { + code.serializeJsonField(generator, FIELD_NAME_CODE); + } + generator.writeEndObject(); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (!modifierExtension.isEmpty()) { + sink.putByte((byte) 2); + Base.hashIntoList(modifierExtension, sink); + } + if (!event.isEmpty()) { + sink.putByte((byte) 3); + Base.hashIntoList(event, sink); + } + if (repeat != null) { + sink.putByte((byte) 4); + repeat.hashInto(sink); + } + if (code != null) { + sink.putByte((byte) 5); + code.hashInto(sink); + } + } + + @Override + public int memSize() { + return MEM_SIZE_OBJECT + extensionData.memSize() + Base.memSize(modifierExtension) + Base.memSize(event) + + Base.memSize(repeat) + Base.memSize(code); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof Timing that && + extensionData.equals(that.extensionData) && + modifierExtension.equals(that.modifierExtension) && + Objects.equals(event, that.event) && + Objects.equals(repeat, that.repeat) && + Objects.equals(code, that.code); + } + + @Override + public int hashCode() { + int result = extensionData.hashCode(); + result = 31 * result + modifierExtension.hashCode(); + result = 31 * result + Objects.hashCode(event); + result = 31 * result + Objects.hashCode(repeat); + result = 31 * result + Objects.hashCode(code); + return result; + } + + @Override + public String toString() { + return "Timing{" + + extensionData + + ", modifierExtension=" + modifierExtension + + ", event=" + event + + ", repeat=" + repeat + + ", code=" + code + + '}'; + } + + public static class Repeat extends AbstractElement implements Complex { + + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - extension data reference + * 4 byte - bounds reference + * 4 byte - count reference + * 4 byte - countMax reference + * 4 byte - duration reference + * 4 byte - durationMax reference + * 4 byte - durationUnit reference + * 4 byte - frequency reference + * 4 byte - frequencyMax reference + * 4 byte - period reference + * 4 byte - periodMax reference + * 4 byte - periodUnit reference + * 4 byte - dayOfWeek reference + * 4 byte - timeOfDay reference + * 4 byte - when reference + * 4 byte - offset reference + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 64; + + private static final Keyword FHIR_TYPE = RT.keyword("fhir.Timing", "repeat"); + + private static final Keyword BOUNDS = RT.keyword(null, "bounds"); + private static final Keyword COUNT = RT.keyword(null, "count"); + private static final Keyword COUNT_MAX = RT.keyword(null, "countMax"); + private static final Keyword DURATION = RT.keyword(null, "duration"); + private static final Keyword DURATION_MAX = RT.keyword(null, "durationMax"); + private static final Keyword DURATION_UNIT = RT.keyword(null, "durationUnit"); + private static final Keyword FREQUENCY = RT.keyword(null, "frequency"); + private static final Keyword FREQUENCY_MAX = RT.keyword(null, "frequencyMax"); + private static final Keyword PERIOD = RT.keyword(null, "period"); + private static final Keyword PERIOD_MAX = RT.keyword(null, "periodMax"); + private static final Keyword PERIOD_UNIT = RT.keyword(null, "periodUnit"); + private static final Keyword DAY_OF_WEEK = RT.keyword(null, "dayOfWeek"); + private static final Keyword TIME_OF_DAY = RT.keyword(null, "timeOfDay"); + private static final Keyword WHEN = RT.keyword(null, "when"); + private static final Keyword OFFSET = RT.keyword(null, "offset"); + + private static final Keyword[] FIELDS = {ID, EXTENSION, BOUNDS, COUNT, COUNT_MAX, DURATION, DURATION_MAX, + DURATION_UNIT, FREQUENCY, FREQUENCY_MAX, PERIOD, PERIOD_MAX, PERIOD_UNIT, DAY_OF_WEEK, TIME_OF_DAY, + WHEN, OFFSET}; + + private static final FieldName FIELD_NAME_BOUNDS_DURATION = FieldName.of("boundsDuration"); + private static final FieldName FIELD_NAME_BOUNDS_RANGE = FieldName.of("boundsRange"); + private static final FieldName FIELD_NAME_BOUNDS_PERIOD = FieldName.of("boundsPeriod"); + private static final FieldName FIELD_NAME_COUNT = FieldName.of("count"); + private static final FieldName FIELD_NAME_COUNT_MAX = FieldName.of("countMax"); + private static final FieldName FIELD_NAME_DURATION = FieldName.of("duration"); + private static final FieldName FIELD_NAME_DURATION_MAX = FieldName.of("durationMax"); + private static final FieldName FIELD_NAME_DURATION_UNIT = FieldName.of("durationUnit"); + private static final FieldName FIELD_NAME_FREQUENCY = FieldName.of("frequency"); + private static final FieldName FIELD_NAME_FREQUENCY_MAX = FieldName.of("frequencyMax"); + private static final FieldName FIELD_NAME_PERIOD = FieldName.of("period"); + private static final FieldName FIELD_NAME_PERIOD_MAX = FieldName.of("periodMax"); + private static final FieldName FIELD_NAME_PERIOD_UNIT = FieldName.of("periodUnit"); + private static final FieldName FIELD_NAME_DAY_OF_WEEK = FieldName.of("dayOfWeek"); + private static final FieldName FIELD_NAME_TIME_OF_DAY = FieldName.of("timeOfDay"); + private static final FieldName FIELD_NAME_WHEN = FieldName.of("when"); + private static final FieldName FIELD_NAME_OFFSET = FieldName.of("offset"); + + private static final byte HASH_MARKER = 58; + + @SuppressWarnings("unchecked") + private static final Repeat EMPTY = new Repeat(ExtensionData.EMPTY, null, null, null, null, null, null, null, + null, null, null, null, PersistentVector.EMPTY, PersistentVector.EMPTY, PersistentVector.EMPTY, null); + + private final Element bounds; + private final PositiveInt count; + private final PositiveInt countMax; + private final Decimal duration; + private final Decimal durationMax; + private final Code durationUnit; + private final PositiveInt frequency; + private final PositiveInt frequencyMax; + private final Decimal period; + private final Decimal periodMax; + private final Code periodUnit; + private final List dayOfWeek; + private final List

+ * 8 byte - object header + * 4 byte - extension data reference + * 4 byte - type reference + * 4 byte - name reference + * 4 byte - timing reference + * 4 byte - data reference + * 4 byte - condition reference + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 24; + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "TriggerDefinition"); + + private static final Keyword TYPE = RT.keyword(null, "type"); + private static final Keyword NAME = RT.keyword(null, "name"); + private static final Keyword TIMING = RT.keyword(null, "timing"); + private static final Keyword DATA = RT.keyword(null, "data"); + private static final Keyword CONDITION = RT.keyword(null, "condition"); + + private static final Keyword[] FIELDS = {ID, EXTENSION, TYPE, NAME, TIMING, DATA, CONDITION}; + + private static final FieldName FIELD_NAME_TYPE = FieldName.of("type"); + private static final FieldName FIELD_NAME_NAME = FieldName.of("name"); + private static final FieldName FIELD_NAME_TIMING_TIMING = FieldName.of("timingTiming"); + private static final FieldName FIELD_NAME_TIMING_REFERENCE = FieldName.of("timingReference"); + private static final FieldName FIELD_NAME_TIMING_DATE = FieldName.of("timingDate"); + private static final FieldName FIELD_NAME_TIMING_DATE_TIME = FieldName.of("timingDateTime"); + private static final FieldName FIELD_NAME_DATA = FieldName.of("data"); + private static final FieldName FIELD_NAME_CONDITION = FieldName.of("condition"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueTriggerDefinition"); + + private static final byte HASH_MARKER = 68; + + @SuppressWarnings("unchecked") + private static final TriggerDefinition EMPTY = new TriggerDefinition(ExtensionData.EMPTY, null, null, null, + PersistentVector.EMPTY, null); + + private final Code type; + private final String name; + private final Element timing; + private final List data; + private final Expression condition; + + private TriggerDefinition(ExtensionData extensionData, Code type, String name, Element timing, + List data, Expression condition) { + super(extensionData); + this.type = type; + this.name = name; + this.timing = timing; + this.data = data; + this.condition = condition; + } + + public static TriggerDefinition create(IPersistentMap m) { + return new TriggerDefinition(ExtensionData.fromMap(m), (Code) m.valAt(TYPE), (String) m.valAt(NAME), + (Element) m.valAt(TIMING), Base.listFrom(m, DATA), (Expression) m.valAt(CONDITION)); + } + + public Code type() { + return type; + } + + public String name() { + return name; + } + + public Element timing() { + return timing; + } + + public List data() { + return data; + } + + public Expression condition() { + return condition; + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE; + if (key == TYPE) return type; + if (key == NAME) return name; + if (key == TIMING) return timing; + if (key == DATA) return data; + if (key == CONDITION) return condition; + return super.valAt(key, notFound); + } + + @Override + public ISeq seq() { + ISeq seq = PersistentList.EMPTY; + seq = appendElement(seq, CONDITION, condition); + seq = appendElement(seq, DATA, data); + seq = appendElement(seq, TIMING, timing); + seq = appendElement(seq, NAME, name); + seq = appendElement(seq, TYPE, type); + return extensionData.append(seq); + } + + @Override + public TriggerDefinition empty() { + return EMPTY; + } + + @Override + public Iterator> iterator() { + return new BaseIterator(this, FIELDS); + } + + @Override + @SuppressWarnings("unchecked") + public TriggerDefinition assoc(Object key, Object val) { + if (key == TYPE) + return new TriggerDefinition(extensionData, (Code) val, name, timing, data, condition); + if (key == NAME) + return new TriggerDefinition(extensionData, type, (String) val, timing, data, condition); + if (key == TIMING) + return new TriggerDefinition(extensionData, type, name, (Element) val, data, condition); + if (key == DATA) + return new TriggerDefinition(extensionData, type, name, timing, (List) val, condition); + if (key == CONDITION) + return new TriggerDefinition(extensionData, type, name, timing, data, (Expression) val); + if (key == EXTENSION) + return new TriggerDefinition(extensionData.withExtension(val), type, name, timing, data, condition); + if (key == ID) + return new TriggerDefinition(extensionData.withId(val), type, name, timing, data, condition); + return this; + } + + @Override + public TriggerDefinition withMeta(IPersistentMap meta) { + return new TriggerDefinition(extensionData.withMeta(meta), type, name, timing, data, condition); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeAsJsonValue(JsonGenerator generator) throws IOException { + generator.writeStartObject(); + serializeJsonBase(generator); + if (type != null) { + type.serializeAsJsonProperty(generator, FIELD_NAME_TYPE); + } + if (name != null) { + name.serializeAsJsonProperty(generator, FIELD_NAME_NAME); + } + if (timing != null) { + switch (timing) { + case Timing timingTiming -> + timingTiming.serializeJsonField(generator, FIELD_NAME_TIMING_TIMING); + case Reference timingReference -> + timingReference.serializeJsonField(generator, FIELD_NAME_TIMING_REFERENCE); + case Date timingDate -> + timingDate.serializeAsJsonProperty(generator, FIELD_NAME_TIMING_DATE); + case DateTime timingDateTime -> + timingDateTime.serializeAsJsonProperty(generator, FIELD_NAME_TIMING_DATE_TIME); + default -> { + } + } + } + if (!data.isEmpty()) { + serializeJsonComplexList(data, generator, FIELD_NAME_DATA.normal()); + } + if (condition != null) { + condition.serializeJsonField(generator, FIELD_NAME_CONDITION); + } + generator.writeEndObject(); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (type != null) { + sink.putByte((byte) 2); + type.hashInto(sink); + } + if (name != null) { + sink.putByte((byte) 3); + name.hashInto(sink); + } + if (timing != null) { + sink.putByte((byte) 4); + timing.hashInto(sink); + } + if (!data.isEmpty()) { + sink.putByte((byte) 5); + Base.hashIntoList(data, sink); + } + if (condition != null) { + sink.putByte((byte) 6); + condition.hashInto(sink); + } + } + + @Override + public int memSize() { + return MEM_SIZE_OBJECT + extensionData.memSize() + Base.memSize(type) + Base.memSize(name) + + Base.memSize(timing) + Base.memSize(data) + Base.memSize(condition); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof TriggerDefinition that && + extensionData.equals(that.extensionData) && + Objects.equals(type, that.type) && + Objects.equals(name, that.name) && + Objects.equals(timing, that.timing) && + Objects.equals(data, that.data) && + Objects.equals(condition, that.condition); + } + + @Override + public int hashCode() { + int result = extensionData.hashCode(); + result = 31 * result + Objects.hashCode(type); + result = 31 * result + Objects.hashCode(name); + result = 31 * result + Objects.hashCode(timing); + result = 31 * result + Objects.hashCode(data); + result = 31 * result + Objects.hashCode(condition); + return result; + } + + @Override + public java.lang.String toString() { + return "TriggerDefinition{" + + "extensionData=" + extensionData + + ", type=" + type + + ", name=" + name + + ", timing=" + timing + + ", data=" + data + + ", condition=" + condition + + '}'; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/UnsignedInt.java b/modules/fhir-structure/java/blaze/fhir/spec/type/UnsignedInt.java new file mode 100644 index 000000000..5cc7f2ee0 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/UnsignedInt.java @@ -0,0 +1,134 @@ +package blaze.fhir.spec.type; + +import blaze.Interner; +import blaze.Interners; +import blaze.fhir.spec.type.system.Integers; +import clojure.lang.IPersistentMap; +import clojure.lang.Keyword; +import clojure.lang.RT; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.lang.Integer; +import java.lang.String; +import java.util.Objects; + +@SuppressWarnings("DuplicatedCode") +public final class UnsignedInt extends PrimitiveElement { + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "unsignedInt"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueUnsignedInt"); + + private static final byte HASH_MARKER = 17; + + private static final Interner INTERNER = Interners.weakInterner(k -> new UnsignedInt(k, -1)); + private static final UnsignedInt EMPTY = new UnsignedInt(ExtensionData.EMPTY, -1); + + private final int value; + + private UnsignedInt(ExtensionData extensionData, int value) { + super(extensionData); + this.value = value; + } + + private static UnsignedInt maybeIntern(ExtensionData extensionData, int value) { + return extensionData.isInterned() && value < 0 + ? INTERNER.intern(extensionData) + : new UnsignedInt(extensionData, value); + } + + private static int toUnsignedInt(Number value) { + long val = value.longValue(); + if (val < 0 || (int) val != val) { + throw new IllegalArgumentException("Invalid unsignedInt value `%d`.".formatted(val)); + } + return (int) val; + } + + public static UnsignedInt create(Number value) { + return value == null ? EMPTY : new UnsignedInt(ExtensionData.EMPTY, toUnsignedInt(value)); + } + + public static UnsignedInt create(IPersistentMap m) { + var value = (Number) m.valAt(VALUE); + return maybeIntern(ExtensionData.fromMap(m), value == null ? -1 : toUnsignedInt(value)); + } + + @Override + public boolean hasValue() { + return value >= 0; + } + + @Override + public Integer value() { + return hasValue() ? value : null; + } + + @Override + public Object valAt(Object key, Object notFound) { + return key == FHIR_TYPE_KEY ? FHIR_TYPE : super.valAt(key, notFound); + } + + @Override + public UnsignedInt empty() { + return EMPTY; + } + + @Override + public UnsignedInt assoc(Object key, Object val) { + if (key == VALUE) return maybeIntern(extensionData, val == null ? -1 : toUnsignedInt((Number) val)); + if (key == EXTENSION) return maybeIntern(extensionData.withExtension(val), value); + if (key == ID) return maybeIntern(extensionData.withId(val), value); + return this; + } + + @Override + public UnsignedInt withMeta(IPersistentMap meta) { + return maybeIntern(extensionData.withMeta(meta), value); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeJsonPrimitiveValue(JsonGenerator generator) throws IOException { + if (hasValue()) { + generator.writeNumber(value); + } else { + generator.writeNull(); + } + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (hasValue()) { + sink.putByte((byte) 2); + Integers.hashInto(value, sink); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof UnsignedInt that && + extensionData.equals(that.extensionData) && + Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return 31 * extensionData.hashCode() + Objects.hashCode(value); + } + + @Override + public String toString() { + return "UnsignedInt{" + extensionData + ", value=" + value + '}'; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Uri.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Uri.java new file mode 100644 index 000000000..093634147 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Uri.java @@ -0,0 +1,259 @@ +package blaze.fhir.spec.type; + +import blaze.Interner; +import blaze.Interners; +import blaze.fhir.spec.type.system.Strings; +import clojure.lang.IPersistentMap; +import clojure.lang.Keyword; +import clojure.lang.RT; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.io.SerializedString; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.lang.String; +import java.util.Objects; + +import static java.util.Objects.requireNonNull; + +@SuppressWarnings("DuplicatedCode") +public sealed abstract class Uri extends PrimitiveElement permits Uri.Normal, Uri.Interned { + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "uri"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueUri"); + + private static final byte HASH_MARKER = 5; + + private Uri(ExtensionData extensionData) { + super(extensionData); + } + + private static Uri maybeIntern(ExtensionData extensionData, String value) { + return extensionData.isInterned() && (value == null || value.length() <= 4) + ? Interned.intern(extensionData, value) + : new Normal(extensionData, value); + } + + public static Uri create(String value) { + return value.length() <= 4 ? Interned.intern(ExtensionData.EMPTY, value) : new Normal(ExtensionData.EMPTY, value); + } + + public static Uri create(IPersistentMap m) { + return maybeIntern(ExtensionData.fromMap(m), (String) m.valAt(VALUE)); + } + + public static Uri createForceIntern(String value) { + return Interned.intern(ExtensionData.EMPTY, requireNonNull(value)); + } + + public static Uri createForceIntern(IPersistentMap m) { + var extensionData = ExtensionData.fromMap(m); + var value = (String) m.valAt(VALUE); + return extensionData.isInterned() ? Interned.intern(extensionData, value) : new Normal(extensionData, value); + } + + /** + * Creates the hash of a FHIR string with {@code value} without creating the + * FHIR string first. + * + * @param sink the sink for the hash bytes + * @param value the string value to hash + */ + @SuppressWarnings("UnstableApiUsage") + public static void hashIntoValue(PrimitiveSink sink, String value) { + sink.putByte(HASH_MARKER); + if (value != null) { + sink.putByte((byte) 2); + Strings.hashInto(value, sink); + } + } + + @Override + public abstract String value(); + + @Override + public Object valAt(Object key, Object notFound) { + return key == FHIR_TYPE_KEY ? FHIR_TYPE : super.valAt(key, notFound); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (hasValue()) { + sink.putByte((byte) 2); + Strings.hashInto(value(), sink); + } + } + + @Override + public final boolean equals(Object o) { + if (this == o) return true; + return o instanceof Uri that && + Objects.equals(extensionData, that.extensionData) && + Objects.equals(value(), that.value()); + } + + @Override + public final int hashCode() { + return 31 * extensionData.hashCode() + Objects.hashCode(value()); + } + + @Override + public final String toString() { + return "Uri{" + + extensionData + + ", value=" + (value() == null ? null : '\'' + value() + '\'') + + '}'; + } + + public static final class Normal extends Uri { + + private static final Normal EMPTY = new Normal(ExtensionData.EMPTY, null); + + private final String value; + + private Normal(ExtensionData extensionData, String value) { + super(extensionData); + this.value = value; + } + + public static Normal create(IPersistentMap m) { + return new Normal(ExtensionData.fromMap(m), (String) m.valAt(VALUE)); + } + + @Override + public String value() { + return value; + } + + @Override + public Object valAt(Object key, Object notFound) { + return key == FHIR_TYPE_KEY ? FHIR_TYPE : super.valAt(key, notFound); + } + + @Override + public Normal empty() { + return EMPTY; + } + + @Override + public Uri assoc(Object key, Object val) { + if (key == VALUE) return maybeIntern(extensionData, (String) val); + if (key == EXTENSION) return maybeIntern(extensionData.withExtension(val), value); + if (key == ID) return maybeIntern(extensionData.withId(val), value); + return this; + } + + @Override + public Uri withMeta(IPersistentMap meta) { + return maybeIntern(extensionData.withMeta(meta), value); + } + + @Override + public void serializeJsonPrimitiveValue(JsonGenerator generator) throws IOException { + if (hasValue()) { + generator.writeString(value); + } else { + generator.writeNull(); + } + } + + @Override + public int memSize() { + return super.memSize() + Strings.memSize(value); + } + } + + public static final class Interned extends Uri { + + private static final Interned EMPTY = new Interned(ExtensionData.EMPTY, null); + private static final Interner INTERNER = Interners.weakInterner(k -> create(k.extensionData, k.value)); + + private final SerializedString value; + + private Interned(ExtensionData extensionData, SerializedString value) { + super(extensionData); + this.value = value; + } + + private static Interned create(ExtensionData extensionData, String value) { + return new Interned(extensionData, value == null ? null : new SerializedString(value)); + } + + private static Uri maybeIntern(ExtensionData extensionData, String value) { + return extensionData.isInterned() ? intern(extensionData, value) : new Normal(extensionData, value); + } + + private static Interned intern(ExtensionData extensionData, String value) { + return INTERNER.intern(new InternerKey(extensionData, value)); + } + + public static Interned create(IPersistentMap m) { + var extensionData = ExtensionData.fromMap(m); + var value = (String) m.valAt(VALUE); + if (extensionData.isInterned()) return intern(extensionData, value); + throw new IllegalArgumentException("Can't create an interned FHIR.Uri using non-interned extension data."); + } + + @Override + public boolean isInterned() { + return true; + } + + @Override + public String value() { + return value == null ? null : value.getValue(); + } + + @Override + public Object valAt(Object key, Object notFound) { + return key == FHIR_TYPE_KEY ? FHIR_TYPE : super.valAt(key, notFound); + } + + @Override + public Interned empty() { + return EMPTY; + } + + @Override + public Uri assoc(Object key, Object val) { + if (key == VALUE) return maybeIntern(extensionData, (String) val); + if (key == EXTENSION) return maybeIntern(extensionData.withExtension(val), value()); + if (key == ID) return maybeIntern(extensionData.withId(val), value()); + return this; + } + + @Override + public Uri withMeta(IPersistentMap meta) { + return maybeIntern(extensionData.withMeta(meta), value()); + } + + @Override + public void serializeJsonPrimitiveValue(JsonGenerator generator) throws IOException { + if (hasValue()) { + generator.writeString(value); + } else { + generator.writeNull(); + } + } + + @Override + public int memSize() { + return 0; + } + } + + private record InternerKey(ExtensionData extensionData, String value) { + private InternerKey { + requireNonNull(extensionData); + } + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Url.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Url.java new file mode 100644 index 000000000..896e73392 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Url.java @@ -0,0 +1,124 @@ +package blaze.fhir.spec.type; + +import blaze.Interner; +import blaze.Interners; +import blaze.fhir.spec.type.system.Strings; +import clojure.lang.IPersistentMap; +import clojure.lang.Keyword; +import clojure.lang.RT; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.lang.String; +import java.util.Objects; + +@SuppressWarnings("DuplicatedCode") +public final class Url extends PrimitiveElement { + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "url"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueUrl"); + + private static final byte HASH_MARKER = 6; + + private static final Interner INTERNER = Interners.weakInterner(k -> new Url(k, null)); + private static final Url EMPTY = new Url(ExtensionData.EMPTY, null); + + private final String value; + + private Url(ExtensionData extensionData, String value) { + super(extensionData); + this.value = value; + } + + private static Url maybeIntern(ExtensionData extensionData, String value) { + return extensionData.isInterned() && value == null ? INTERNER.intern(extensionData) : new Url(extensionData, value); + } + + public static Url create(String value) { + return value == null ? EMPTY : new Url(ExtensionData.EMPTY, value); + } + + public static Url create(IPersistentMap m) { + return maybeIntern(ExtensionData.fromMap(m), (String) m.valAt(VALUE)); + } + + public String value() { + return value; + } + + @Override + public Object valAt(Object key, Object notFound) { + return key == FHIR_TYPE_KEY ? FHIR_TYPE : super.valAt(key, notFound); + } + + @Override + public Url empty() { + return EMPTY; + } + + @Override + public Url assoc(Object key, Object val) { + if (key == VALUE) return maybeIntern(extensionData, (String) val); + if (key == EXTENSION) return maybeIntern(extensionData.withExtension(val), value); + if (key == ID) return maybeIntern(extensionData.withId(val), value); + return this; + } + + @Override + public Url withMeta(IPersistentMap meta) { + return maybeIntern(extensionData.withMeta(meta), value); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeJsonPrimitiveValue(JsonGenerator generator) throws IOException { + if (hasValue()) { + generator.writeString(value); + } else { + generator.writeNull(); + } + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (value != null) { + sink.putByte((byte) 2); + Strings.hashInto(value, sink); + } + } + + @Override + public int memSize() { + return super.memSize() + Strings.memSize(value); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof Url that && + extensionData.equals(that.extensionData) && + Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return 31 * extensionData.hashCode() + Objects.hashCode(value); + } + + @Override + public String toString() { + return "Url{" + + extensionData + + ", value=" + (value == null ? null : '\'' + value + '\'') + + '}'; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/UsageContext.java b/modules/fhir-structure/java/blaze/fhir/spec/type/UsageContext.java new file mode 100644 index 000000000..06bc8267b --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/UsageContext.java @@ -0,0 +1,184 @@ +package blaze.fhir.spec.type; + +import clojure.lang.*; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.util.Iterator; +import java.util.Objects; + +import static blaze.fhir.spec.type.Base.appendElement; + +@SuppressWarnings("DuplicatedCode") +public final class UsageContext extends AbstractElement implements Complex, ExtensionValue { + + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - extension data reference + * 4 byte - code reference + * 4 byte - value reference + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 20; + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "UsageContext"); + + private static final Keyword CODE = RT.keyword(null, "code"); + private static final Keyword VALUE = RT.keyword(null, "value"); + + private static final Keyword[] FIELDS = {ID, EXTENSION, CODE, VALUE}; + + private static final FieldName FIELD_NAME_CODE = FieldName.of("code"); + private static final FieldName FIELD_NAME_VALUE_CODEABLE_CONCEPT = FieldName.of("valueCodeableConcept"); + private static final FieldName FIELD_NAME_VALUE_QUANTITY = FieldName.of("valueQuantity"); + private static final FieldName FIELD_NAME_VALUE_RANGE = FieldName.of("valueRange"); + private static final FieldName FIELD_NAME_VALUE_REFERENCE = FieldName.of("valueReference"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueUsageContext"); + + private static final byte HASH_MARKER = 67; + + private static final UsageContext EMPTY = new UsageContext(ExtensionData.EMPTY, null, null); + + private final Coding code; + private final Element value; + + private UsageContext(ExtensionData extensionData, Coding code, Element value) { + super(extensionData); + this.code = code; + this.value = value; + } + + public static UsageContext create(IPersistentMap m) { + return new UsageContext(ExtensionData.fromMap(m), (Coding) m.valAt(CODE), (Element) m.valAt(VALUE)); + } + + public Coding code() { + return code; + } + + public Element value() { + return value; + } + + @Override + public Object valAt(Object key, Object notFound) { + if (key == FHIR_TYPE_KEY) return FHIR_TYPE; + if (key == CODE) return code; + if (key == VALUE) return value; + return super.valAt(key, notFound); + } + + @Override + public ISeq seq() { + ISeq seq = PersistentList.EMPTY; + seq = appendElement(seq, VALUE, value); + seq = appendElement(seq, CODE, code); + return extensionData.append(seq); + } + + @Override + public UsageContext empty() { + return EMPTY; + } + + @Override + public Iterator> iterator() { + return new BaseIterator(this, FIELDS); + } + + @Override + public UsageContext assoc(Object key, Object val) { + if (key == CODE) + return new UsageContext(extensionData, (Coding) val, value); + if (key == VALUE) + return new UsageContext(extensionData, code, (Element) val); + if (key == EXTENSION) + return new UsageContext(extensionData.withExtension(val), code, value); + if (key == ID) + return new UsageContext(extensionData.withId(val), code, value); + return this; + } + + @Override + public UsageContext withMeta(IPersistentMap meta) { + return new UsageContext(extensionData.withMeta(meta), code, value); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeAsJsonValue(JsonGenerator generator) throws IOException { + generator.writeStartObject(); + serializeJsonBase(generator); + if (code != null) { + code.serializeJsonField(generator, FIELD_NAME_CODE); + } + if (value != null) { + switch (value) { + case CodeableConcept valueCodeableConcept -> + valueCodeableConcept.serializeJsonField(generator, FIELD_NAME_VALUE_CODEABLE_CONCEPT); + case Quantity valueQuantity -> + valueQuantity.serializeJsonField(generator, FIELD_NAME_VALUE_QUANTITY); + case Range valueRange -> + valueRange.serializeJsonField(generator, FIELD_NAME_VALUE_RANGE); + case Reference valueReference -> + valueReference.serializeJsonField(generator, FIELD_NAME_VALUE_REFERENCE); + default -> { + } + } + } + generator.writeEndObject(); + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (code != null) { + sink.putByte((byte) 2); + code.hashInto(sink); + } + if (value != null) { + sink.putByte((byte) 3); + value.hashInto(sink); + } + } + + @Override + public int memSize() { + return MEM_SIZE_OBJECT + extensionData.memSize() + Base.memSize(code) + Base.memSize(value); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof UsageContext that && + extensionData.equals(that.extensionData) && + Objects.equals(code, that.code) && + Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + int result = extensionData.hashCode(); + result = 31 * result + Objects.hashCode(code); + result = 31 * result + Objects.hashCode(value); + return result; + } + + @Override + public java.lang.String toString() { + return "UsageContext{" + + "extensionData=" + extensionData + + ", code=" + code + + ", value=" + value + + '}'; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Uuid.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Uuid.java new file mode 100644 index 000000000..e304ccad7 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Uuid.java @@ -0,0 +1,128 @@ +package blaze.fhir.spec.type; + +import blaze.Interner; +import blaze.Interners; +import clojure.lang.IPersistentMap; +import clojure.lang.Keyword; +import clojure.lang.RT; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.lang.String; +import java.util.Objects; +import java.util.UUID; + +@SuppressWarnings("DuplicatedCode") +public final class Uuid extends PrimitiveElement { + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "uuid"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueUuid"); + + private static final byte HASH_MARKER = 19; + + private static final Interner INTERNER = Interners.weakInterner(k -> new Uuid(k, null)); + private static final Uuid EMPTY = new Uuid(ExtensionData.EMPTY, null); + private static final int MEM_SIZE_UUID = 24; + + private final UUID value; + + private Uuid(ExtensionData extensionData, UUID value) { + super(extensionData); + this.value = value; + } + + private static Uuid maybeIntern(ExtensionData extensionData, UUID value) { + return extensionData.isInterned() && value == null ? INTERNER.intern(extensionData) : new Uuid(extensionData, value); + } + + public static Uuid create(String value) { + return value == null ? EMPTY : new Uuid(ExtensionData.EMPTY, UUID.fromString(value.substring(9))); + } + + public static Uuid create(IPersistentMap m) { + String value = (String) m.valAt(VALUE); + return maybeIntern(ExtensionData.fromMap(m), value == null ? null : UUID.fromString(value.substring(9))); + } + + public String value() { + return value == null ? null : "urn:uuid:" + value; + } + + @Override + public Object valAt(Object key, Object notFound) { + return key == FHIR_TYPE_KEY ? FHIR_TYPE : super.valAt(key, notFound); + } + + @Override + public Uuid empty() { + return EMPTY; + } + + @Override + public Uuid assoc(Object key, Object val) { + if (key == VALUE) + return maybeIntern(extensionData, val == null ? null : UUID.fromString(((String) val).substring(9))); + if (key == EXTENSION) return maybeIntern(extensionData.withExtension(val), value); + if (key == ID) return maybeIntern(extensionData.withId(val), value); + return this; + } + + @Override + public Uuid withMeta(IPersistentMap meta) { + return maybeIntern(extensionData.withMeta(meta), value); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeJsonPrimitiveValue(JsonGenerator generator) throws IOException { + if (hasValue()) { + generator.writeString(value()); + } else { + generator.writeNull(); + } + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (value != null) { + sink.putByte((byte) 2); + sink.putLong(value.getMostSignificantBits()); + sink.putLong(value.getLeastSignificantBits()); + } + } + + @Override + public int memSize() { + return isInterned() ? 0 : MEM_SIZE_OBJECT + extensionData.memSize() + (value == null ? 0 : MEM_SIZE_UUID); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof Uuid that && + extensionData.equals(that.extensionData) && + Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return 31 * extensionData.hashCode() + Objects.hashCode(value); + } + + @Override + public String toString() { + return "Uuid{" + + extensionData + + ", value=" + (value == null ? null : "'" + value() + '\'') + + '}'; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/Xhtml.java b/modules/fhir-structure/java/blaze/fhir/spec/type/Xhtml.java new file mode 100644 index 000000000..505d4d079 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/Xhtml.java @@ -0,0 +1,124 @@ +package blaze.fhir.spec.type; + +import blaze.Interner; +import blaze.Interners; +import blaze.fhir.spec.type.system.Strings; +import clojure.lang.IPersistentMap; +import clojure.lang.Keyword; +import clojure.lang.RT; +import com.fasterxml.jackson.core.JsonGenerator; +import com.google.common.hash.PrimitiveSink; + +import java.io.IOException; +import java.lang.String; +import java.util.Objects; + +@SuppressWarnings("DuplicatedCode") +public final class Xhtml extends PrimitiveElement { + + private static final Keyword FHIR_TYPE = RT.keyword("fhir", "xhtml"); + + private static final FieldName FIELD_NAME_EXTENSION_VALUE = FieldName.of("valueXhtml"); + + private static final byte HASH_MARKER = 20; + + private static final Interner INTERNER = Interners.weakInterner(k -> new Xhtml(k, null)); + private static final Xhtml EMPTY = new Xhtml(ExtensionData.EMPTY, null); + + private final String value; + + private Xhtml(ExtensionData extensionData, String value) { + super(extensionData); + this.value = value; + } + + private static Xhtml maybeIntern(ExtensionData extensionData, String value) { + return extensionData.isInterned() && value == null ? INTERNER.intern(extensionData) : new Xhtml(extensionData, value); + } + + public static Xhtml create(String value) { + return value == null ? EMPTY : new Xhtml(ExtensionData.EMPTY, value); + } + + public static Xhtml create(IPersistentMap m) { + return maybeIntern(ExtensionData.fromMap(m), (String) m.valAt(VALUE)); + } + + public String value() { + return value; + } + + @Override + public Object valAt(Object key, Object notFound) { + return key == FHIR_TYPE_KEY ? FHIR_TYPE : super.valAt(key, notFound); + } + + @Override + public Xhtml empty() { + return EMPTY; + } + + @Override + public Xhtml assoc(Object key, Object val) { + if (key == VALUE) return maybeIntern(extensionData, (String) val); + if (key == EXTENSION) return maybeIntern(extensionData.withExtension(val), value); + if (key == ID) return maybeIntern(extensionData.withId(val), value); + return this; + } + + @Override + public Xhtml withMeta(IPersistentMap meta) { + return maybeIntern(extensionData.withMeta(meta), value); + } + + @Override + public FieldName fieldNameExtensionValue() { + return FIELD_NAME_EXTENSION_VALUE; + } + + @Override + public void serializeJsonPrimitiveValue(JsonGenerator generator) throws IOException { + if (hasValue()) { + generator.writeString(value); + } else { + generator.writeNull(); + } + } + + @Override + @SuppressWarnings("UnstableApiUsage") + public void hashInto(PrimitiveSink sink) { + sink.putByte(HASH_MARKER); + extensionData.hashInto(sink); + if (value != null) { + sink.putByte((byte) 2); + Strings.hashInto(value, sink); + } + } + + @Override + public int memSize() { + return super.memSize() + Strings.memSize(value); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof Xhtml that && + extensionData.equals(that.extensionData) && + Objects.equals(value, that.value); + } + + @Override + public int hashCode() { + return 31 * extensionData.hashCode() + Objects.hashCode(value); + } + + @Override + public String toString() { + return "Xhtml{" + + extensionData + + ", value=" + (value == null ? null : '\'' + value + '\'') + + '}'; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/system/Booleans.java b/modules/fhir-structure/java/blaze/fhir/spec/type/system/Booleans.java new file mode 100644 index 000000000..4031b5d88 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/system/Booleans.java @@ -0,0 +1,12 @@ +package blaze.fhir.spec.type.system; + +import com.google.common.hash.PrimitiveSink; + +public interface Booleans { + + @SuppressWarnings("UnstableApiUsage") + static void hashInto(boolean b, PrimitiveSink sink) { + sink.putByte((byte) 0); + sink.putBoolean(b); + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/system/Date.java b/modules/fhir-structure/java/blaze/fhir/spec/type/system/Date.java index 48426a682..a97adc57d 100644 --- a/modules/fhir-structure/java/blaze/fhir/spec/type/system/Date.java +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/system/Date.java @@ -1,15 +1,15 @@ package blaze.fhir.spec.type.system; import clojure.lang.Keyword; +import clojure.lang.RT; import java.time.DateTimeException; -import java.time.format.DateTimeParseException; import java.time.temporal.Temporal; import java.time.temporal.ValueRange; public interface Date extends JavaSystemType, Temporal { - Keyword TYPE = Keyword.intern("system", "date"); + Keyword TYPE = RT.keyword("system", "date"); ValueRange YEAR_RANGE = ValueRange.of(1, 9999); diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/system/DateDate.java b/modules/fhir-structure/java/blaze/fhir/spec/type/system/DateDate.java index a0acd581d..d226e8fee 100644 --- a/modules/fhir-structure/java/blaze/fhir/spec/type/system/DateDate.java +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/system/DateDate.java @@ -7,17 +7,27 @@ import java.time.LocalDateTime; import java.time.Month; import java.time.chrono.IsoChronology; -import java.time.format.DateTimeParseException; import java.time.temporal.ChronoField; import java.time.temporal.Temporal; import java.time.temporal.TemporalField; import java.time.temporal.TemporalUnit; +import static blaze.fhir.spec.type.Base.MEM_SIZE_OBJECT_HEADER; import static java.time.temporal.ChronoField.*; @SuppressWarnings("UnstableApiUsage") public final class DateDate implements Date, Comparable { + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - year + * 2 byte - month + * 2 byte - day + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 8; + /** * The number of days in a 400 year cycle. */ @@ -149,6 +159,11 @@ public void hashInto(PrimitiveSink sink) { sink.putInt(day); } + @Override + public int memSize() { + return MEM_SIZE_OBJECT; + } + public DateTimeDate toDateTime() { return DateTimeDate.of(year, month, day); } @@ -309,14 +324,9 @@ public int compareTo(DateDate other) { } @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj instanceof DateDate) { - return compareTo((DateDate) obj) == 0; - } - return false; + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof DateDate that && year == that.year && month == that.month && day == that.day; } @Override diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/system/DateTime.java b/modules/fhir-structure/java/blaze/fhir/spec/type/system/DateTime.java index 5dbeef816..1567e6e44 100644 --- a/modules/fhir-structure/java/blaze/fhir/spec/type/system/DateTime.java +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/system/DateTime.java @@ -1,6 +1,7 @@ package blaze.fhir.spec.type.system; import clojure.lang.Keyword; +import clojure.lang.RT; import java.time.*; import java.time.format.DateTimeParseException; @@ -9,7 +10,7 @@ public interface DateTime extends JavaSystemType, Temporal { - Keyword TYPE = Keyword.intern("system", "date-time"); + Keyword TYPE = RT.keyword("system", "date-time"); ValueRange YEAR_RANGE = ValueRange.of(1, 9999); diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/system/DateTimeDate.java b/modules/fhir-structure/java/blaze/fhir/spec/type/system/DateTimeDate.java index 36a569b80..6f30ecd9e 100644 --- a/modules/fhir-structure/java/blaze/fhir/spec/type/system/DateTimeDate.java +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/system/DateTimeDate.java @@ -11,11 +11,22 @@ import java.time.temporal.TemporalField; import java.time.temporal.TemporalUnit; +import static blaze.fhir.spec.type.Base.MEM_SIZE_OBJECT_HEADER; import static java.time.temporal.ChronoField.*; @SuppressWarnings("UnstableApiUsage") public final class DateTimeDate implements DateTime, Comparable { + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - year + * 2 byte - month + * 2 byte - day + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 8; + private final int year; private final short month; private final short day; @@ -92,6 +103,11 @@ public void hashInto(PrimitiveSink sink) { sink.putInt(day); } + @Override + public int memSize() { + return MEM_SIZE_OBJECT; + } + public DateDate toDate() { return DateDate.of(year, month, day); } @@ -166,14 +182,9 @@ public int compareTo(DateTimeDate other) { } @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj instanceof DateTimeDate) { - return compareTo((DateTimeDate) obj) == 0; - } - return false; + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof DateTimeDate that && year == that.year && month == that.month && day == that.day; } @Override diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/system/DateTimeYear.java b/modules/fhir-structure/java/blaze/fhir/spec/type/system/DateTimeYear.java index c48716072..766d1a3c8 100644 --- a/modules/fhir-structure/java/blaze/fhir/spec/type/system/DateTimeYear.java +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/system/DateTimeYear.java @@ -8,11 +8,21 @@ import java.time.temporal.TemporalField; import java.time.temporal.TemporalUnit; +import static blaze.fhir.spec.type.Base.MEM_SIZE_OBJECT_HEADER; import static java.time.temporal.ChronoField.YEAR; @SuppressWarnings("UnstableApiUsage") public final class DateTimeYear implements DateTime, Comparable { + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - year + * 4 byte - padding + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 8; + private final int year; private DateTimeYear(int year) { @@ -46,6 +56,11 @@ public void hashInto(PrimitiveSink sink) { sink.putInt(year); } + @Override + public int memSize() { + return MEM_SIZE_OBJECT; + } + public DateYear toDate() { return DateYear.of(year); } @@ -114,14 +129,9 @@ public int compareTo(DateTimeYear other) { } @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj instanceof DateTimeYear) { - return year == ((DateTimeYear) obj).year; - } - return false; + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof DateTimeYear that && year == that.year; } @Override diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/system/DateTimeYearMonth.java b/modules/fhir-structure/java/blaze/fhir/spec/type/system/DateTimeYearMonth.java index 86af98455..84835fa97 100644 --- a/modules/fhir-structure/java/blaze/fhir/spec/type/system/DateTimeYearMonth.java +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/system/DateTimeYearMonth.java @@ -9,12 +9,22 @@ import java.time.temporal.TemporalField; import java.time.temporal.TemporalUnit; +import static blaze.fhir.spec.type.Base.MEM_SIZE_OBJECT_HEADER; import static java.time.temporal.ChronoField.MONTH_OF_YEAR; import static java.time.temporal.ChronoField.YEAR; @SuppressWarnings("UnstableApiUsage") public final class DateTimeYearMonth implements DateTime, Comparable { + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - year + * 4 byte - month + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 8; + private final int year; private final int month; @@ -101,6 +111,11 @@ public void hashInto(PrimitiveSink sink) { sink.putInt(month); } + @Override + public int memSize() { + return MEM_SIZE_OBJECT; + } + public DateYearMonth toDate() { return DateYearMonth.of(year, month); } @@ -173,15 +188,9 @@ public int compareTo(DateTimeYearMonth other) { } @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj instanceof DateTimeYearMonth) { - return year == ((DateTimeYearMonth) obj).year && - month == ((DateTimeYearMonth) obj).month; - } - return false; + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof DateTimeYearMonth that && year == that.year && month == that.month; } @Override diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/system/DateTimes.java b/modules/fhir-structure/java/blaze/fhir/spec/type/system/DateTimes.java new file mode 100644 index 000000000..792548dbb --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/system/DateTimes.java @@ -0,0 +1,71 @@ +package blaze.fhir.spec.type.system; + +import com.google.common.hash.PrimitiveSink; + +import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.chrono.IsoChronology; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.format.ResolverStyle; +import java.time.temporal.Temporal; + +import static java.time.format.DateTimeFormatter.ISO_LOCAL_DATE; + +public interface DateTimes { + + DateTimeFormatter LOCAL_DATE_TIME = new DateTimeFormatterBuilder() + .parseCaseInsensitive() + .append(ISO_LOCAL_DATE) + .appendLiteral('T') + .append(Times.LOCAL_TIME) + .toFormatter() + .withResolverStyle(ResolverStyle.STRICT) + .withChronology(IsoChronology.INSTANCE); + + DateTimeFormatter DATE_TIME = new DateTimeFormatterBuilder() + .append(LOCAL_DATE_TIME) + .appendOffsetId() + .toFormatter() + .withResolverStyle(ResolverStyle.STRICT) + .withChronology(IsoChronology.INSTANCE); + + @SuppressWarnings("UnstableApiUsage") + static void hashInto(Temporal value, PrimitiveSink sink) { + if (value instanceof JavaSystemType systemType) { + systemType.hashInto(sink); + } + if (value instanceof LocalDateTime dateTime) { + sink.putByte((byte) 6); + sink.putInt(dateTime.getYear()); + sink.putInt(dateTime.getMonthValue()); + sink.putInt(dateTime.getDayOfMonth()); + sink.putInt(dateTime.getHour()); + sink.putInt(dateTime.getMinute()); + sink.putInt(dateTime.getSecond()); + sink.putInt(dateTime.getNano()); + } + if (value instanceof OffsetDateTime dateTime) { + hashInto(dateTime.toLocalDateTime(), sink); + sink.putInt(dateTime.getOffset().getTotalSeconds()); + } + } + + static int memSize(Temporal value) { + if (value == null) return 0; + return switch (value) { + case JavaSystemType systemType -> systemType.memSize(); + case LocalDateTime x -> 48; + case OffsetDateTime x -> 64; + default -> 0; + }; + } + + static String toString(Temporal value) { + return switch (value) { + case LocalDateTime dateTime -> LOCAL_DATE_TIME.format(dateTime); + case OffsetDateTime dateTime -> DATE_TIME.format(dateTime); + default -> value.toString(); + }; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/system/DateYear.java b/modules/fhir-structure/java/blaze/fhir/spec/type/system/DateYear.java index eb8f9ab78..db6f14362 100644 --- a/modules/fhir-structure/java/blaze/fhir/spec/type/system/DateYear.java +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/system/DateYear.java @@ -8,11 +8,21 @@ import java.time.temporal.TemporalField; import java.time.temporal.TemporalUnit; +import static blaze.fhir.spec.type.Base.MEM_SIZE_OBJECT_HEADER; import static java.time.temporal.ChronoField.YEAR; @SuppressWarnings("UnstableApiUsage") public final class DateYear implements Date, Comparable { + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - year + * 4 byte - padding + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 8; + private final int year; private DateYear(int year) { @@ -46,6 +56,11 @@ public void hashInto(PrimitiveSink sink) { sink.putInt(year); } + @Override + public int memSize() { + return MEM_SIZE_OBJECT; + } + public DateTimeYear toDateTime() { return DateTimeYear.of(year); } @@ -110,14 +125,9 @@ public int compareTo(DateYear other) { } @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj instanceof DateYear) { - return year == ((DateYear) obj).year; - } - return false; + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof DateYear that && year == that.year; } @Override diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/system/DateYearMonth.java b/modules/fhir-structure/java/blaze/fhir/spec/type/system/DateYearMonth.java index 75c9ea698..79bc2c923 100644 --- a/modules/fhir-structure/java/blaze/fhir/spec/type/system/DateYearMonth.java +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/system/DateYearMonth.java @@ -9,12 +9,22 @@ import java.time.temporal.TemporalField; import java.time.temporal.TemporalUnit; +import static blaze.fhir.spec.type.Base.MEM_SIZE_OBJECT_HEADER; import static java.time.temporal.ChronoField.MONTH_OF_YEAR; import static java.time.temporal.ChronoField.YEAR; @SuppressWarnings("UnstableApiUsage") public final class DateYearMonth implements Date, Comparable { + /** + * Memory size. + *

+ * 8 byte - object header + * 4 byte - year + * 4 byte - month + */ + private static final int MEM_SIZE_OBJECT = MEM_SIZE_OBJECT_HEADER + 8; + private final int year; private final int month; @@ -101,6 +111,11 @@ public void hashInto(PrimitiveSink sink) { sink.putInt(month); } + @Override + public int memSize() { + return MEM_SIZE_OBJECT; + } + public DateTimeYearMonth toDateTime() { return DateTimeYearMonth.of(year, month); } @@ -173,15 +188,9 @@ public int compareTo(DateYearMonth other) { } @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj instanceof DateYearMonth) { - return year == ((DateYearMonth) obj).year && - month == ((DateYearMonth) obj).month; - } - return false; + public boolean equals(Object o) { + if (this == o) return true; + return o instanceof DateYearMonth that && year == that.year && month == that.month; } @Override diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/system/Decimals.java b/modules/fhir-structure/java/blaze/fhir/spec/type/system/Decimals.java new file mode 100644 index 000000000..a4751764f --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/system/Decimals.java @@ -0,0 +1,23 @@ +package blaze.fhir.spec.type.system; + +import com.google.common.hash.PrimitiveSink; + +import java.math.BigDecimal; + +import static java.nio.charset.StandardCharsets.UTF_8; + +public interface Decimals { + + int MEM_SIZE_OBJECT = 32; + + @SuppressWarnings("UnstableApiUsage") + static void hashInto(BigDecimal value, PrimitiveSink sink) { + sink.putByte((byte) 4); + sink.putString(value.toString(), UTF_8); + } + + static int memSize(BigDecimal value) { + // TODO: works up to long representation + return value == null ? 0 : MEM_SIZE_OBJECT; + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/system/Integers.java b/modules/fhir-structure/java/blaze/fhir/spec/type/system/Integers.java new file mode 100644 index 000000000..8cc12a920 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/system/Integers.java @@ -0,0 +1,12 @@ +package blaze.fhir.spec.type.system; + +import com.google.common.hash.PrimitiveSink; + +public interface Integers { + + @SuppressWarnings("UnstableApiUsage") + static void hashInto(int value, PrimitiveSink sink) { + sink.putByte((byte) 2); + sink.putInt(value); + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/system/JavaSystemType.java b/modules/fhir-structure/java/blaze/fhir/spec/type/system/JavaSystemType.java index e6dc33026..a872dd295 100644 --- a/modules/fhir-structure/java/blaze/fhir/spec/type/system/JavaSystemType.java +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/system/JavaSystemType.java @@ -3,10 +3,14 @@ import clojure.lang.Keyword; import com.google.common.hash.PrimitiveSink; -@SuppressWarnings("UnstableApiUsage") public interface JavaSystemType { Keyword type(); + @SuppressWarnings("UnstableApiUsage") void hashInto(PrimitiveSink sink); + + default int memSize() { + return 0; // TODO: implement + } } diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/system/Longs.java b/modules/fhir-structure/java/blaze/fhir/spec/type/system/Longs.java new file mode 100644 index 000000000..dba636a39 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/system/Longs.java @@ -0,0 +1,12 @@ +package blaze.fhir.spec.type.system; + +import com.google.common.hash.PrimitiveSink; + +public interface Longs { + + @SuppressWarnings("UnstableApiUsage") + static void hashInto(long value, PrimitiveSink sink) { + sink.putByte((byte) 3); + sink.putLong(value); + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/system/Strings.java b/modules/fhir-structure/java/blaze/fhir/spec/type/system/Strings.java new file mode 100644 index 000000000..745ee84a2 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/system/Strings.java @@ -0,0 +1,27 @@ +package blaze.fhir.spec.type.system; + +import com.fasterxml.jackson.core.io.SerializedString; +import com.google.common.hash.PrimitiveSink; + +import static java.nio.charset.StandardCharsets.UTF_8; + +public interface Strings { + + int MEM_SIZE_OBJECT = 40; + int MEM_SIZE_SERIALIZED_STRING_OBJECT = 32; + + @SuppressWarnings("UnstableApiUsage") + static void hashInto(String value, PrimitiveSink sink) { + sink.putByte((byte) 1); + sink.putString(value, UTF_8); + } + + static int memSize(String value) { + // TODO: only works for latin strings + return value == null ? 0 : MEM_SIZE_OBJECT + ((value.length() + 3) & ~7); + } + + static int memSize(SerializedString value) { + return value == null ? 0 : MEM_SIZE_SERIALIZED_STRING_OBJECT + memSize(value.getValue()); + } +} diff --git a/modules/fhir-structure/java/blaze/fhir/spec/type/system/Times.java b/modules/fhir-structure/java/blaze/fhir/spec/type/system/Times.java new file mode 100644 index 000000000..b7251c485 --- /dev/null +++ b/modules/fhir-structure/java/blaze/fhir/spec/type/system/Times.java @@ -0,0 +1,30 @@ +package blaze.fhir.spec.type.system; + +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.format.ResolverStyle; + +import static java.time.temporal.ChronoField.*; + +public interface Times { + + DateTimeFormatter LOCAL_TIME = new DateTimeFormatterBuilder() + .appendValue(HOUR_OF_DAY, 2) + .appendLiteral(':') + .appendValue(MINUTE_OF_HOUR, 2) + .appendLiteral(':') + .appendValue(SECOND_OF_MINUTE, 2) + .optionalStart() + .appendFraction(NANO_OF_SECOND, 0, 9, true) + .toFormatter() + .withResolverStyle(ResolverStyle.STRICT); + + static int memSize(LocalTime value) { + return value == null ? 0 : 16; + } + + static String toString(LocalTime value) { + return LOCAL_TIME.format(value); + } +} diff --git a/modules/fhir-structure/resources/data_readers.clj b/modules/fhir-structure/resources/data_readers.clj index e8ce2eeff..27e5d700c 100644 --- a/modules/fhir-structure/resources/data_readers.clj +++ b/modules/fhir-structure/resources/data_readers.clj @@ -5,8 +5,10 @@ fhir/integer blaze.fhir.spec.type/integer fhir/long blaze.fhir.spec.type/long fhir/string blaze.fhir.spec.type/string + fhir/string-interned blaze.fhir.spec.type/string-interned fhir/decimal blaze.fhir.spec.type/decimal fhir/uri blaze.fhir.spec.type/uri + fhir/uri-interned blaze.fhir.spec.type/uri-interned fhir/url blaze.fhir.spec.type/url fhir/canonical blaze.fhir.spec.type/canonical fhir/base64Binary blaze.fhir.spec.type/base64Binary @@ -21,20 +23,45 @@ fhir/unsignedInt blaze.fhir.spec.type/unsignedInt fhir/positiveInt blaze.fhir.spec.type/positiveInt fhir/uuid blaze.fhir.spec.type/uuid - fhir/xhtml blaze.fhir.spec.type/->Xhtml + fhir/xhtml blaze.fhir.spec.type/xhtml + fhir/Address blaze.fhir.spec.type/address + fhir/Age blaze.fhir.spec.type/age + fhir/Annotation blaze.fhir.spec.type/annotation fhir/Attachment blaze.fhir.spec.type/attachment - fhir/Extension blaze.fhir.spec.type/extension - fhir/Coding blaze.fhir.spec.type/coding fhir/CodeableConcept blaze.fhir.spec.type/codeable-concept + fhir/Coding blaze.fhir.spec.type/coding + fhir/ContactDetail blaze.fhir.spec.type/contact-detail + fhir/ContactPoint blaze.fhir.spec.type/contact-point + fhir/Contributor blaze.fhir.spec.type/contributor + fhir/Count blaze.fhir.spec.type/count + fhir/DataRequirement blaze.fhir.spec.type/data-requirement + fhir.DataRequirement/codeFilter blaze.fhir.spec.type/data-requirement-code-filter + fhir.DataRequirement/dateFilter blaze.fhir.spec.type/data-requirement-date-filter + fhir.DataRequirement/sort blaze.fhir.spec.type/data-requirement-sort + fhir/Distance blaze.fhir.spec.type/distance + fhir/Dosage blaze.fhir.spec.type/dosage + fhir.Dosage/doseAndRate blaze.fhir.spec.type/dosage-dose-and-rate + fhir/Duration blaze.fhir.spec.type/duration + fhir/Expression blaze.fhir.spec.type/expression + fhir/Extension blaze.fhir.spec.type/extension + fhir/HumanName blaze.fhir.spec.type/human-name + fhir/Identifier blaze.fhir.spec.type/identifier + fhir/Meta blaze.fhir.spec.type/meta + fhir/Money blaze.fhir.spec.type/money + fhir/ParameterDefinition blaze.fhir.spec.type/parameter-definition + fhir/Period blaze.fhir.spec.type/period fhir/Quantity blaze.fhir.spec.type/quantity + fhir/Range blaze.fhir.spec.type/range fhir/Ratio blaze.fhir.spec.type/ratio - fhir/Period blaze.fhir.spec.type/period - fhir/Identifier blaze.fhir.spec.type/identifier - fhir/HumanName blaze.fhir.spec.type/human-name - fhir/Address blaze.fhir.spec.type/address fhir/Reference blaze.fhir.spec.type/reference - fhir/Meta blaze.fhir.spec.type/meta - fhir/BundleEntrySearch blaze.fhir.spec.type/bundle-entry-search + fhir/RelatedArtifact blaze.fhir.spec.type/related-artifact + fhir/SampledData blaze.fhir.spec.type/sampled-data + fhir/Signature blaze.fhir.spec.type/signature + fhir/Timing blaze.fhir.spec.type/timing + fhir.Timing/repeat blaze.fhir.spec.type/timing-repeat + fhir/TriggerDefinition blaze.fhir.spec.type/trigger-definition + fhir/UsageContext blaze.fhir.spec.type/usage-context + fhir.Bundle.entry/search blaze.fhir.spec.type/bundle-entry-search system/date blaze.fhir.spec.type.system/parse-date system/date-time blaze.fhir.spec.type.system/parse-date-time system/time blaze.fhir.spec.type.system/parse-time} diff --git a/modules/fhir-structure/src/blaze/fhir/hash.clj b/modules/fhir-structure/src/blaze/fhir/hash.clj index 9dc5f64d1..3a564d550 100644 --- a/modules/fhir-structure/src/blaze/fhir/hash.clj +++ b/modules/fhir-structure/src/blaze/fhir/hash.clj @@ -1,9 +1,9 @@ (ns blaze.fhir.hash (:require - [blaze.byte-buffer :as bb] - [blaze.fhir.spec.type :as type]) + [blaze.byte-buffer :as bb]) (:import [blaze.fhir Hash] + [blaze.fhir.spec.type Base] [com.fasterxml.jackson.core JsonGenerator] [com.fasterxml.jackson.databind.module SimpleModule] [com.fasterxml.jackson.databind.ser.std StdSerializer] @@ -70,7 +70,7 @@ resource, overwriting it." ^Hash [resource] (let [hasher (.newHasher (Hashing/sha256))] - (type/hash-into resource hasher) + (Base/hashInto resource hasher) (from-byte-buffer! (bb/wrap (.asBytes (.hash hasher)))))) (defmethod print-method Hash [^Hash hash ^Writer w] diff --git a/modules/fhir-structure/src/blaze/fhir/spec.clj b/modules/fhir-structure/src/blaze/fhir/spec.clj index 9c9240253..ee04fc9ca 100644 --- a/modules/fhir-structure/src/blaze/fhir/spec.clj +++ b/modules/fhir-structure/src/blaze/fhir/spec.clj @@ -1,12 +1,11 @@ (ns blaze.fhir.spec (:refer-clojure :exclude [str]) (:require - [blaze.anomaly :as ba] + [blaze.anomaly :as ba :refer [when-ok]] [blaze.fhir.hash.spec] [blaze.fhir.spec.impl :as impl] [blaze.fhir.spec.resource :as res] [blaze.fhir.spec.spec] - [blaze.fhir.spec.type :as type] [blaze.util :refer [str]] [clojure.alpha.spec :as s2] [clojure.spec.alpha :as s] @@ -14,6 +13,7 @@ [clojure.walk :as walk] [cognitect.anomalies :as anom]) (:import + [blaze.fhir.spec.type Primitive] [java.io ByteArrayOutputStream] [java.nio.charset StandardCharsets] [java.util.regex Pattern])) @@ -46,23 +46,10 @@ (defn type-exists? [type] (some? (s2/get-spec (keyword "fhir" type)))) -(defn fhir-type - "Returns the FHIR type of `x` as keyword with the namespace `fhir` or nil if - `x` has no FHIR type." - [x] - (type/type x)) - -(defn primitive? - "Primitive FHIR type like `id`." - [spec] - (and (keyword? spec) - (= "fhir" (namespace spec)) - (Character/isLowerCase ^char (first (name spec))))) - (defn primitive-val? "Returns true if `x` is a primitive FHIR value." [x] - (primitive? (fhir-type x))) + (instance? Primitive x)) (defn write-json "Writes `value` to output stream `out` closing it if done." @@ -72,23 +59,24 @@ (defn write-json-as-bytes [context value] (let [out (ByteArrayOutputStream.)] - (write-json context out value) - (.toByteArray out))) + (when-ok [_ (write-json context out value)] + (.toByteArray out)))) (defn write-json-as-string [context value] - (String. ^bytes (write-json-as-bytes context value) StandardCharsets/UTF_8)) + (when-ok [bytes (write-json-as-bytes context value)] + (String. ^bytes bytes StandardCharsets/UTF_8))) (defn write-cbor [context x] (let [out (ByteArrayOutputStream.)] - (res/write-cbor context out x) - (.toByteArray out))) + (when-ok [_ (res/write-cbor context out x)] + (.toByteArray out)))) (defn unform-xml "Returns the XML representation of `resource`." [resource] - (let [key (keyword "fhir.xml" (name (type/type resource)))] + (let [key (keyword "fhir.xml" (name (:fhir/type resource)))] (if-let [spec (s2/get-spec key)] (s2/unform spec resource) (throw (ex-info (format "Missing spec: %s" key) {:key key}))))) diff --git a/modules/fhir-structure/src/blaze/fhir/spec/impl.clj b/modules/fhir-structure/src/blaze/fhir/spec/impl.clj index 11ee91a59..2c28e44ae 100644 --- a/modules/fhir-structure/src/blaze/fhir/spec/impl.clj +++ b/modules/fhir-structure/src/blaze/fhir/spec/impl.clj @@ -2,11 +2,11 @@ (:refer-clojure :exclude [str]) (:require [blaze.anomaly :as ba :refer [throw-anom]] - [blaze.fhir.spec.impl.intern :as intern] [blaze.fhir.spec.impl.specs :as specs] [blaze.fhir.spec.impl.xml :as xml] [blaze.fhir.spec.type :as type] [blaze.fhir.spec.type.string-util :as su] + [blaze.fhir.spec.type.system :as system] [blaze.util :refer [str]] [clojure.alpha.spec :as s] [clojure.data.xml.name :as xml-name] @@ -47,13 +47,6 @@ (def id-matcher-form `(fn [~'s] (.matches (re-matcher #"[A-Za-z0-9\-\.]{1,64}" ~'s)))) -(def ^{:arglists '([s])} intern-string - "Returns an interned String without using String/intern." - (intern/intern-value identity)) - -(def uri-matcher-form - `(specs/regex #"(?U)[\p{Print}&&[^\p{Blank}]]*" intern-string)) - (def conform-xml-value "Takes the value out of an XML element." (comp :value :attrs)) @@ -68,15 +61,9 @@ nil `(s/and string? ~id-matcher-form) :xml `(s/and xml/element? (s/conformer conform-xml-value unform-xml-value) ~id-matcher-form))) -(defn uri-string-spec [modifier] - (case modifier - (nil :xmlAttr) `(s/and string? ~uri-matcher-form) - :xml `(s/and xml/element? (s/conformer conform-xml-value unform-xml-value) ~uri-matcher-form))) - (defn- string-spec [modifier type] (case (find-fhir-type type) "id" (id-string-spec modifier) - "uri" (uri-string-spec modifier) (if-let [regex (type-regex type)] `(s/and string? (fn [~'s] (.matches (re-matcher ~regex ~'s)))) `string?))) @@ -158,8 +145,257 @@ :max max :spec-form (case path - ("Quantity.unit" "Coding.version" "Coding.display" "CodeableConcept.text") - (xml/primitive-xml-form `type/xml->InternedString) + ("Address.city" + "Address.district" + "Address.state" + "Address.postalCode" + "Address.country" + "Age.unit" + "Bundle.link.relation" + "Bundle.response.status" + "CodeableConcept.text" + "Coding.version" + "Coding.display" + "Count.unit" + "Distance.unit" + "Duration.unit" + "HumanName.family" + "HumanName.prefix" + "HumanName.suffix" + "Quantity.unit") + (xml/primitive-xml-form `type/string-interned `identity) + ("Resource.implicitRules" + "Account.implicitRules" + "ActivityDefinition.implicitRules" + "ActivityDefinition.url" + "AdverseEvent.implicitRules" + "Age.system" + "AllergyIntolerance.implicitRules" + "Appointment.implicitRules" + "AppointmentResponse.implicitRules" + "AuditEvent.implicitRules" + "AuditEvent.agent.policy" + "Basic.implicitRules" + "Binary.implicitRules" + "BiologicallyDerivedProduct.implicitRules" + "BodyStructure.implicitRules" + "Bundle.implicitRules" + "CapabilityStatement.implicitRules" + "CapabilityStatement.url" + "CarePlan.implicitRules" + "CarePlan.instantiatesUri" + "CarePlan.activity.detail.instantiatesUri" + "CareTeam.implicitRules" + "CatalogEntry.implicitRules" + "ChargeItem.implicitRules" + "ChargeItem.definitionUri" + "ChargeItemDefinition.implicitRules" + "ChargeItemDefinition.url" + "ChargeItemDefinition.derivedFromUri" + "Claim.implicitRules" + "ClaimResponse.implicitRules" + "ClinicalImpression.implicitRules" + "ClinicalImpression.protocol" + "CodeSystem.implicitRules" + "CodeSystem.url" + "CodeSystem.property.uri" + "Communication.implicitRules" + "Communication.instantiatesUri" + "CommunicationRequest.implicitRules" + "CompartmentDefinition.implicitRules" + "CompartmentDefinition.url" + "Composition.implicitRules" + "ConceptMap.implicitRules" + "ConceptMap.url" + "Condition.implicitRules" + "Consent.implicitRules" + "Consent.policy.authority" + "Consent.policy.uri" + "Contract.implicitRules" + "Contract.url" + "Contract.instantiatesUri" + "Count.system" + "Coverage.implicitRules" + "CoverageEligibilityRequest.implicitRules" + "CoverageEligibilityResponse.implicitRules" + "CoverageEligibilityResponse.insurance.item.authorizationUrl" + "DetectedIssue.implicitRules" + "Device.implicitRules" + "Device.udiCarrier.issuer" + "Device.udiCarrier.jurisdiction" + "Device.url" + "DeviceDefinition.implicitRules" + "DeviceDefinition.udiDeviceIdentifier.issuer" + "DeviceDefinition.udiDeviceIdentifier.jurisdiction" + "DeviceDefinition.url" + "DeviceDefinition.onlineInformation" + "DeviceMetric.implicitRules" + "DeviceRequest.implicitRules" + "DeviceRequest.instantiatesUri" + "DeviceUseStatement.implicitRules" + "DiagnosticReport.implicitRules" + "Distance.system" + "DocumentManifest.implicitRules" + "DocumentManifest.source" + "DocumentReference.implicitRules" + "DomainResource.implicitRules" + "Duration.system" + "EffectEvidenceSynthesis.implicitRules" + "EffectEvidenceSynthesis.url" + "Encounter.implicitRules" + "Endpoint.implicitRules" + "EnrollmentRequest.implicitRules" + "EnrollmentResponse.implicitRules" + "EpisodeOfCare.implicitRules" + "EventDefinition.implicitRules" + "EventDefinition.url" + "Evidence.implicitRules" + "Evidence.url" + "EvidenceVariable.implicitRules" + "EvidenceVariable.url" + "ExampleScenario.implicitRules" + "ExampleScenario.url" + "ExplanationOfBenefit.implicitRules" + "FamilyMemberHistory.implicitRules" + "FamilyMemberHistory.instantiatesUri" + "Flag.implicitRules" + "Goal.implicitRules" + "GraphDefinition.implicitRules" + "GraphDefinition.url" + "Group.implicitRules" + "GuidanceResponse.implicitRules" + "HealthcareService.implicitRules" + "ImagingStudy.implicitRules" + "Immunization.implicitRules" + "Immunization.education.reference" + "ImmunizationEvaluation.implicitRules" + "ImmunizationRecommendation.implicitRules" + "ImplementationGuide.implicitRules" + "ImplementationGuide.url" + "InsurancePlan.implicitRules" + "Invoice.implicitRules" + "Library.implicitRules" + "Library.url" + "Linkage.implicitRules" + "List.implicitRules" + "Location.implicitRules" + "Measure.implicitRules" + "Measure.url" + "MeasureReport.implicitRules" + "Media.implicitRules" + "Medication.implicitRules" + "MedicationAdministration.implicitRules" + "MedicationAdministration.instantiates" + "MedicationDispense.implicitRules" + "MedicationKnowledge.implicitRules" + "MedicationRequest.implicitRules" + "MedicationRequest.instantiatesUri" + "MedicationStatement.implicitRules" + "MedicinalProduct.implicitRules" + "MedicinalProductAuthorization.implicitRules" + "MedicinalProductContraindication.implicitRules" + "MedicinalProductIndication.implicitRules" + "MedicinalProductIngredient.implicitRules" + "MedicinalProductInteraction.implicitRules" + "MedicinalProductManufactured.implicitRules" + "MedicinalProductPackaged.implicitRules" + "MedicinalProductPharmaceutical.implicitRules" + "MedicinalProductUndesirableEffect.implicitRules" + "MessageDefinition.implicitRules" + "MessageDefinition.url" + "MessageHeader.implicitRules" + "MolecularSequence.implicitRules" + "MolecularSequence.repository.url" + "NamingSystem.implicitRules" + "NutritionOrder.implicitRules" + "NutritionOrder.instantiatesUri" + "NutritionOrder.instantiates" + "Observation.implicitRules" + "ObservationDefinition.implicitRules" + "OperationDefinition.implicitRules" + "OperationDefinition.url" + "OperationOutcome.implicitRules" + "Organization.implicitRules" + "OrganizationAffiliation.implicitRules" + "Parameters.implicitRules" + "Patient.implicitRules" + "PaymentNotice.implicitRules" + "PaymentReconciliation.implicitRules" + "Person.implicitRules" + "PlanDefinition.implicitRules" + "PlanDefinition.url" + "Practitioner.implicitRules" + "PractitionerRole.implicitRules" + "Procedure.implicitRules" + "Procedure.instantiatesUri" + "Provenance.implicitRules" + "Provenance.policy" + "Questionnaire.implicitRules" + "Questionnaire.url" + "Questionnaire.item.definition" + "QuestionnaireResponse.implicitRules" + "QuestionnaireResponse.item.definition" + "RelatedPerson.implicitRules" + "RequestGroup.implicitRules" + "RequestGroup.instantiatesUri" + "ResearchDefinition.implicitRules" + "ResearchDefinition.url" + "ResearchElementDefinition.implicitRules" + "ResearchElementDefinition.url" + "ResearchStudy.implicitRules" + "ResearchSubject.implicitRules" + "RiskAssessment.implicitRules" + "RiskEvidenceSynthesis.implicitRules" + "RiskEvidenceSynthesis.url" + "Schedule.implicitRules" + "SearchParameter.implicitRules" + "SearchParameter.url" + "ServiceRequest.implicitRules" + "ServiceRequest.instantiatesUri" + "Slot.implicitRules" + "Specimen.implicitRules" + "SpecimenDefinition.implicitRules" + "StructureDefinition.implicitRules" + "StructureDefinition.url" + "StructureDefinition.mapping.uri" + "StructureDefinition.type" + "StructureMap.implicitRules" + "StructureMap.url" + "Subscription.implicitRules" + "Substance.implicitRules" + "SubstanceNucleicAcid.implicitRules" + "SubstancePolymer.implicitRules" + "SubstanceProtein.implicitRules" + "SubstanceReferenceInformation.implicitRules" + "SubstanceSourceMaterial.implicitRules" + "SubstanceSpecification.implicitRules" + "SupplyDelivery.implicitRules" + "SupplyRequest.implicitRules" + "Task.implicitRules" + "Task.instantiatesUri" + "TerminologyCapabilities.implicitRules" + "TerminologyCapabilities.url" + "TestReport.implicitRules" + "TestReport.participant.uri" + "TestReport.setup.action.operation.detail" + "TestScript.implicitRules" + "TestScript.url" + "TestScript.metadata.link.url" + "TestScript.metadata.capability.link" + "ValueSet.implicitRules" + "ValueSet.url" + "ValueSet.compose.include.system" + "ValueSet.expansion.identifier" + "ValueSet.expansion.contains.system" + "VerificationResult.implicitRules" + "VisionPrescription.implicitRules" + "MetadataResource.implicitRules" + "MetadataResource.url" + "Coding.system" + "Identifier.system" + "Quantity.system" + "Reference.type") + (xml/primitive-xml-form #"(?U)[\p{Print}&&[^\p{Blank}]]*" `type/uri-interned `identity) (keyword "fhir.xml" (:code type)))}]) (defn elem-def->spec-def @@ -249,7 +485,7 @@ child-spec-defs))) (defn- type-check-form [key] - `(fn [~'m] (identical? ~key (type/type ~'m)))) + `(fn [~'m] (identical? ~key (:fhir/type ~'m)))) (defn- internal-schema-spec-def [parent-path-parts path-part elem-def child-spec-defs] (let [key (spec-key "fhir" parent-path-parts path-part)] @@ -258,19 +494,49 @@ :max (:max elem-def) :spec-form (case key - (:fhir/Attachment - :fhir/Extension - :fhir/Coding + (:fhir/Address + :fhir/Age + :fhir/Annotation + :fhir/Attachment :fhir/CodeableConcept + :fhir/Coding + :fhir/ContactDetail + :fhir/ContactPoint + :fhir/Contributor + :fhir/Count + :fhir/DataRequirement + :fhir/Distance + :fhir/Dosage + :fhir/Duration + :fhir/Expression + :fhir/Extension + :fhir/HumanName + :fhir/Identifier + :fhir/Meta + :fhir/Money + :fhir/ParameterDefinition + :fhir/Period :fhir/Quantity + :fhir/Range :fhir/Ratio - :fhir/Period - :fhir/Identifier - :fhir/HumanName - :fhir/Address :fhir/Reference - :fhir/Meta) + :fhir/RelatedArtifact + :fhir/SampledData + :fhir/Signature + :fhir/Timing + :fhir/TriggerDefinition + :fhir/UsageContext) (record-spec-form path-part child-spec-defs) + :fhir.DataRequirement/codeFilter + (record-spec-form "DataRequirement$CodeFilter" child-spec-defs) + :fhir.DataRequirement/dateFilter + (record-spec-form "DataRequirement$DateFilter" child-spec-defs) + :fhir.DataRequirement/sort + (record-spec-form "DataRequirement$Sort" child-spec-defs) + :fhir.Dosage/doseAndRate + (record-spec-form "Dosage$DoseAndRate" child-spec-defs) + :fhir.Timing/repeat + (record-spec-form "Timing$Repeat" child-spec-defs) :fhir.Bundle.entry/search (record-spec-form "BundleEntrySearch" child-spec-defs) `(s/and ~(type-check-form key) ~(schema-spec-form nil child-spec-defs)))})) @@ -294,7 +560,7 @@ "Add the type suffix to the key of a choice typed data element." [m key] (if-some [v (get m key)] - (-> (dissoc m key) (assoc (choice-type-key key (type/type v)) v)) + (-> (dissoc m key) (assoc (choice-type-key key (:fhir/type v)) v)) m)) (defn- remap-choice-conformer-form @@ -342,13 +608,13 @@ content)) (defn select-non-nil-keys [m ks] - (into {} (remove (comp nil? val)) (select-keys m ks))) + (into {} (keep (fn [entry] (when (ks (key entry)) entry))) m)) (defn- xml-attrs-form [child-spec-defs] `(select-non-nil-keys ~'m ~(into - [] + #{} (comp (filter :key) (filter :representation) @@ -403,14 +669,15 @@ `s/and)) (defn- special-xml-schema-spec-form [kind type-name child-spec-defs] - (let [constructor-sym (symbol "blaze.fhir.spec.type" (str "map->" type-name)) - constructor (resolve constructor-sym)] - (conj (seq (conj (remap-choice-conformer-forms child-spec-defs) - `(s/conformer ~constructor identity))) - (schema-spec-form :xml child-spec-defs) - `(s/conformer conform-xml - ~(xml-unformer kind (keyword type-name) child-spec-defs)) - `s/and))) + (let [constructor-sym (symbol "blaze.fhir.spec.type" (su/pascal->kebab type-name))] + (if-let [constructor (resolve constructor-sym)] + (conj (seq (conj (remap-choice-conformer-forms child-spec-defs) + `(s/conformer ~constructor #(into {} %)))) + (schema-spec-form :xml child-spec-defs) + `(s/conformer conform-xml + ~(xml-unformer kind (keyword type-name) child-spec-defs)) + `s/and) + (throw (Exception. (format "Can't resolve constructor `%s`." constructor-sym)))))) (defn- xml-schema-spec-def [kind parent-path-parts path-part elem-def child-spec-defs] @@ -421,19 +688,49 @@ :modifier :xml :spec-form (case key - (:fhir.xml/Attachment - :fhir.xml/Extension - :fhir.xml/Coding + (:fhir.xml/Address + :fhir.xml/Age + :fhir.xml/Annotation + :fhir.xml/Attachment :fhir.xml/CodeableConcept + :fhir.xml/Coding + :fhir.xml/ContactDetail + :fhir.xml/ContactPoint + :fhir.xml/Contributor + :fhir.xml/Count + :fhir.xml/DataRequirement + :fhir.xml/Distance + :fhir.xml/Dosage + :fhir.xml/Duration + :fhir.xml/Expression + :fhir.xml/Extension + :fhir.xml/HumanName + :fhir.xml/Identifier + :fhir.xml/Meta + :fhir.xml/Money + :fhir.xml/ParameterDefinition + :fhir.xml/Period :fhir.xml/Quantity + :fhir.xml/Range :fhir.xml/Ratio - :fhir.xml/Period - :fhir.xml/Identifier - :fhir.xml/HumanName - :fhir.xml/Address :fhir.xml/Reference - :fhir.xml/Meta) + :fhir.xml/RelatedArtifact + :fhir.xml/SampledData + :fhir.xml/Signature + :fhir.xml/Timing + :fhir.xml/TriggerDefinition + :fhir.xml/UsageContext) (special-xml-schema-spec-form kind (name key) child-spec-defs) + :fhir.xml.DataRequirement/codeFilter + (special-xml-schema-spec-form kind "DataRequirementCodeFilter" child-spec-defs) + :fhir.xml.DataRequirement/dateFilter + (special-xml-schema-spec-form kind "DataRequirementDateFilter" child-spec-defs) + :fhir.xml.DataRequirement/sort + (special-xml-schema-spec-form kind "DataRequirementSort" child-spec-defs) + :fhir.xml.Dosage/doseAndRate + (special-xml-schema-spec-form kind "DosageDoseAndRate" child-spec-defs) + :fhir.xml.Timing/repeat + (special-xml-schema-spec-form kind "TimingRepeat" child-spec-defs) :fhir.xml.Bundle.entry/search (special-xml-schema-spec-form kind "BundleEntrySearch" child-spec-defs) (xml-schema-spec-form kind (spec-key "fhir" parent-path-parts path-part) @@ -504,18 +801,32 @@ (defn- value-type [element] (some #(when (str/ends-with? (:path %) "value") (first (:type %))) element)) +(defn- pattern [name element] + (case name + "string" nil + "uri" #"(?U)[\p{Print}&&[^\p{Blank}]]*" + "url" #"(?U)[\p{Print}&&[^\p{Blank}]]*" + "canonical" #"(?U)[\p{Print}&&[^\p{Blank}]]*" + "code" nil + "markdown" nil + (type-regex (value-type element)))) + (defn- xml-spec-form [name {:keys [element]}] - (let [pattern (type-regex (value-type element)) - constructor (str "xml->" (su/capital name))] + (let [pattern (pattern name element)] (case name - "string" (xml/primitive-xml-form `type/xml->String) - "uri" (xml/primitive-xml-form #"(?U)[\p{Print}&&[^\p{Blank}]]*" `type/xml->Uri) - "url" (xml/primitive-xml-form #"(?U)[\p{Print}&&[^\p{Blank}]]*" `type/xml->Url) - "canonical" (xml/primitive-xml-form #"(?U)[\p{Print}&&[^\p{Blank}]]*" `type/xml->Canonical) - "code" (xml/primitive-xml-form `type/xml->Code) - "markdown" (xml/primitive-xml-form `type/xml->Markdown) - "xhtml" `(s/and xml/element? (s/conformer type/xml->Xhtml type/to-xml)) - (xml/primitive-xml-form pattern (symbol "blaze.fhir.spec.type" constructor))))) + "boolean" (xml/primitive-xml-form pattern `type/boolean `system/parse-boolean) + "integer" (xml/primitive-xml-form pattern `type/integer `system/parse-integer) + "decimal" (xml/primitive-xml-form pattern `type/decimal `system/parse-decimal) + "instant" (xml/primitive-xml-form pattern `type/instant `system/parse-date-time) + "date" (xml/primitive-xml-form pattern `type/date `system/parse-date) + "dateTime" (xml/primitive-xml-form pattern `type/dateTime `system/parse-date-time) + "time" (xml/primitive-xml-form pattern `type/time `system/parse-time) + "unsignedInt" (xml/primitive-xml-form pattern `type/unsignedInt `system/parse-integer) + "positiveInt" (xml/primitive-xml-form pattern `type/positiveInt `system/parse-integer) + "xhtml" `(s/and xml/element? (s/conformer type/xml->Xhtml type/xhtml-to-xml)) + (if pattern + (xml/primitive-xml-form pattern (symbol "blaze.fhir.spec.type" name) `identity) + (xml/primitive-xml-form (symbol "blaze.fhir.spec.type" name) `identity))))) (defn primitive-type->spec-defs "Converts a primitive type structure definition into spec defs for XML and @@ -553,8 +864,7 @@ (defmulti resource (constantly :default)) (defmethod resource :default [{:fhir/keys [type]}] - (when type - (keyword "fhir" (name type)))) + type) (s/def :fhir/Resource (s/multi-spec resource :fhir/type)) diff --git a/modules/fhir-structure/src/blaze/fhir/spec/impl/intern.clj b/modules/fhir-structure/src/blaze/fhir/spec/impl/intern.clj deleted file mode 100644 index 0ec22e1c1..000000000 --- a/modules/fhir-structure/src/blaze/fhir/spec/impl/intern.clj +++ /dev/null @@ -1,38 +0,0 @@ -(ns blaze.fhir.spec.impl.intern - (:import - [clojure.lang Util] - [java.lang.ref ReferenceQueue WeakReference] - [java.util.concurrent ConcurrentHashMap])) - -(set! *warn-on-reflection* true) - -(defn intern-value - "Returns a function of arity 1 that creates a value using `create-fn` only if - no value exists already in an internal cache using the argument as key. - - Should be used the following way: - - (def intern-foo - \"Creates interned instances of foo.\" - (intern-value foo)) - - Holds weak references to values so that they can be collected under high - pressure. The algorithm was taken from Clojure 1.10.3 keyword interning." - [create-fn] - (let [cache (ConcurrentHashMap.) - rq (ReferenceQueue.)] - (fn [key] - (loop [ref (.get cache key)] - (if ref - (if-let [existing-value (.get ^WeakReference ref)] - existing-value - (do (.remove cache key ref) - (recur (.get cache key)))) - (let [new-value (create-fn key)] - (Util/clearCache rq cache) - (if-let [ref (.putIfAbsent cache key (WeakReference. new-value rq))] - (if-let [existing-value (.get ^WeakReference ref)] - existing-value - (do (.remove cache key ref) - (recur (.get cache key)))) - new-value))))))) diff --git a/modules/fhir-structure/src/blaze/fhir/spec/impl/specs.clj b/modules/fhir-structure/src/blaze/fhir/spec/impl/specs.clj index aaa3af098..a1ffddcb9 100644 --- a/modules/fhir-structure/src/blaze/fhir/spec/impl/specs.clj +++ b/modules/fhir-structure/src/blaze/fhir/spec/impl/specs.clj @@ -49,7 +49,7 @@ sp/Spec (conform* [_ x _ settings] (if (instance? class x) - (loop [ret x [[k v] & ks] x] + (loop [ret (into {} x) [[k v] & ks] x] (if k (if (some? v) (let [conformed (if-let [sp (@specs k)] (sp/conform* sp v k settings) v)] diff --git a/modules/fhir-structure/src/blaze/fhir/spec/impl/xml.clj b/modules/fhir-structure/src/blaze/fhir/spec/impl/xml.clj index 1d08afa18..316b7d43a 100644 --- a/modules/fhir-structure/src/blaze/fhir/spec/impl/xml.clj +++ b/modules/fhir-structure/src/blaze/fhir/spec/impl/xml.clj @@ -1,5 +1,6 @@ (ns blaze.fhir.spec.impl.xml (:require + [blaze.anomaly :refer [if-ok]] [blaze.fhir.spec.type :as type] [clojure.alpha.spec :as s] [clojure.data.xml.name :as xml-name]) @@ -26,17 +27,26 @@ (defn remove-character-content [element] (update element :content (partial filter element?))) +(defn xml-constructor [constructor system-constructor] + (fn [{{:keys [id value]} :attrs content :content}] + (let [extension (some-> (seq content) vec)] + (if-ok [value (some-> value system-constructor)] + (if (or id extension) + (constructor {:id id :extension extension :value value}) + (constructor value)) + (fn [_] ::s/invalid))))) + (defn primitive-xml-form - ([constructor] + ([constructor system-constructor] `(s/and element? (s/conformer remove-character-content set-extension-tag) (s/schema {:content (s/coll-of :fhir.xml/Extension)}) - (s/conformer ~constructor type/to-xml))) - ([regex constructor] + (s/conformer (xml-constructor ~constructor ~system-constructor) type/to-xml))) + ([regex constructor system-constructor] `(s/and element? (fn [~'e] (value-matches? ~regex ~'e)) (s/conformer remove-character-content set-extension-tag) (s/schema {:content (s/coll-of :fhir.xml/Extension)}) - (s/conformer ~constructor type/to-xml)))) + (s/conformer (xml-constructor ~constructor ~system-constructor) type/to-xml)))) diff --git a/modules/fhir-structure/src/blaze/fhir/spec/resource.clj b/modules/fhir-structure/src/blaze/fhir/spec/resource.clj index 9ee5f2a3b..cf5f582eb 100644 --- a/modules/fhir-structure/src/blaze/fhir/spec/resource.clj +++ b/modules/fhir-structure/src/blaze/fhir/spec/resource.clj @@ -23,7 +23,6 @@ (:refer-clojure :exclude [str]) (:require [blaze.anomaly :as ba :refer [if-ok when-ok]] - [blaze.fhir.spec.impl :as impl] [blaze.fhir.spec.type :as type] [blaze.fhir.spec.type.string-util :as su] [blaze.fhir.spec.type.system :as system] @@ -32,7 +31,8 @@ [clojure.string :as str] [cognitect.anomalies :as anom]) (:import - [clojure.lang PersistentArrayMap PersistentVector RT] + [blaze.fhir.spec.type Lists] + [clojure.lang PersistentArrayMap RT] [com.fasterxml.jackson.core JsonFactory JsonParseException JsonParser JsonToken StreamReadConstraints] [com.fasterxml.jackson.core.exc InputCoercionException] [com.fasterxml.jackson.core.io JsonEOFException] @@ -75,18 +75,9 @@ [parent-type element-definitions] (:types (separate-element-definitions* parent-type element-definitions))) -(defn- find-fhir-type [{:keys [extension]}] - (some - #(when (= "http://hl7.org/fhir/StructureDefinition/structuredefinition-fhir-type" (:url %)) - (:valueUrl %)) - extension)) - -(defn- prepare-element-type [{:keys [code] :as type} path] +(defn- prepare-element-type [{:keys [code]} path] (condp = code - "http://hl7.org/fhirpath/System.String" - (if (= "uri" (find-fhir-type type)) - :system.string/uri - :system/string) + "http://hl7.org/fhirpath/System.String" :system/string "http://hl7.org/fhirpath/System.Time" :system/time "http://hl7.org/fhirpath/System.Date" :system/date "http://hl7.org/fhirpath/System.DateTime" :system/date-time @@ -95,11 +86,263 @@ "http://hl7.org/fhirpath/System.Boolean" :system/boolean "Element" (keyword "element" path) "BackboneElement" (keyword "backboneElement" path) - (keyword - (if (Character/isLowerCase ^char (first code)) - "primitive" - "complex") - code))) + (case path + ("Address.city" + "Address.district" + "Address.state" + "Address.postalCode" + "Address.country" + "Age.unit" + "Bundle.link.relation" + "Bundle.response.status" + "CodeableConcept.text" + "Coding.version" + "Coding.display" + "Count.unit" + "Distance.unit" + "Duration.unit" + "HumanName.family" + "HumanName.prefix" + "HumanName.suffix" + "Quantity.unit") + :primitive/string-interned + ("Resource.implicitRules" + "Account.implicitRules" + "ActivityDefinition.implicitRules" + "ActivityDefinition.url" + "AdverseEvent.implicitRules" + "Age.system" + "AllergyIntolerance.implicitRules" + "Appointment.implicitRules" + "AppointmentResponse.implicitRules" + "AuditEvent.implicitRules" + "AuditEvent.agent.policy" + "Basic.implicitRules" + "Binary.implicitRules" + "BiologicallyDerivedProduct.implicitRules" + "BodyStructure.implicitRules" + "Bundle.implicitRules" + "CapabilityStatement.implicitRules" + "CapabilityStatement.url" + "CarePlan.implicitRules" + "CarePlan.instantiatesUri" + "CarePlan.activity.detail.instantiatesUri" + "CareTeam.implicitRules" + "CatalogEntry.implicitRules" + "ChargeItem.implicitRules" + "ChargeItem.definitionUri" + "ChargeItemDefinition.implicitRules" + "ChargeItemDefinition.url" + "ChargeItemDefinition.derivedFromUri" + "Claim.implicitRules" + "ClaimResponse.implicitRules" + "ClinicalImpression.implicitRules" + "ClinicalImpression.protocol" + "CodeSystem.implicitRules" + "CodeSystem.url" + "CodeSystem.property.uri" + "Communication.implicitRules" + "Communication.instantiatesUri" + "CommunicationRequest.implicitRules" + "CompartmentDefinition.implicitRules" + "CompartmentDefinition.url" + "Composition.implicitRules" + "ConceptMap.implicitRules" + "ConceptMap.url" + "Condition.implicitRules" + "Consent.implicitRules" + "Consent.policy.authority" + "Consent.policy.uri" + "Contract.implicitRules" + "Contract.url" + "Contract.instantiatesUri" + "Count.system" + "Coverage.implicitRules" + "CoverageEligibilityRequest.implicitRules" + "CoverageEligibilityResponse.implicitRules" + "CoverageEligibilityResponse.insurance.item.authorizationUrl" + "DetectedIssue.implicitRules" + "Device.implicitRules" + "Device.udiCarrier.issuer" + "Device.udiCarrier.jurisdiction" + "Device.url" + "DeviceDefinition.implicitRules" + "DeviceDefinition.udiDeviceIdentifier.issuer" + "DeviceDefinition.udiDeviceIdentifier.jurisdiction" + "DeviceDefinition.url" + "DeviceDefinition.onlineInformation" + "DeviceMetric.implicitRules" + "DeviceRequest.implicitRules" + "DeviceRequest.instantiatesUri" + "DeviceUseStatement.implicitRules" + "DiagnosticReport.implicitRules" + "Distance.system" + "DocumentManifest.implicitRules" + "DocumentManifest.source" + "DocumentReference.implicitRules" + "DomainResource.implicitRules" + "Duration.system" + "EffectEvidenceSynthesis.implicitRules" + "EffectEvidenceSynthesis.url" + "Encounter.implicitRules" + "Endpoint.implicitRules" + "EnrollmentRequest.implicitRules" + "EnrollmentResponse.implicitRules" + "EpisodeOfCare.implicitRules" + "EventDefinition.implicitRules" + "EventDefinition.url" + "Evidence.implicitRules" + "Evidence.url" + "EvidenceVariable.implicitRules" + "EvidenceVariable.url" + "ExampleScenario.implicitRules" + "ExampleScenario.url" + "ExplanationOfBenefit.implicitRules" + "FamilyMemberHistory.implicitRules" + "FamilyMemberHistory.instantiatesUri" + "Flag.implicitRules" + "Goal.implicitRules" + "GraphDefinition.implicitRules" + "GraphDefinition.url" + "Group.implicitRules" + "GuidanceResponse.implicitRules" + "HealthcareService.implicitRules" + "ImagingStudy.implicitRules" + "Immunization.implicitRules" + "Immunization.education.reference" + "ImmunizationEvaluation.implicitRules" + "ImmunizationRecommendation.implicitRules" + "ImplementationGuide.implicitRules" + "ImplementationGuide.url" + "InsurancePlan.implicitRules" + "Invoice.implicitRules" + "Library.implicitRules" + "Library.url" + "Linkage.implicitRules" + "List.implicitRules" + "Location.implicitRules" + "Measure.implicitRules" + "Measure.url" + "MeasureReport.implicitRules" + "Media.implicitRules" + "Medication.implicitRules" + "MedicationAdministration.implicitRules" + "MedicationAdministration.instantiates" + "MedicationDispense.implicitRules" + "MedicationKnowledge.implicitRules" + "MedicationRequest.implicitRules" + "MedicationRequest.instantiatesUri" + "MedicationStatement.implicitRules" + "MedicinalProduct.implicitRules" + "MedicinalProductAuthorization.implicitRules" + "MedicinalProductContraindication.implicitRules" + "MedicinalProductIndication.implicitRules" + "MedicinalProductIngredient.implicitRules" + "MedicinalProductInteraction.implicitRules" + "MedicinalProductManufactured.implicitRules" + "MedicinalProductPackaged.implicitRules" + "MedicinalProductPharmaceutical.implicitRules" + "MedicinalProductUndesirableEffect.implicitRules" + "MessageDefinition.implicitRules" + "MessageDefinition.url" + "MessageHeader.implicitRules" + "MolecularSequence.implicitRules" + "MolecularSequence.repository.url" + "NamingSystem.implicitRules" + "NutritionOrder.implicitRules" + "NutritionOrder.instantiatesUri" + "NutritionOrder.instantiates" + "Observation.implicitRules" + "ObservationDefinition.implicitRules" + "OperationDefinition.implicitRules" + "OperationDefinition.url" + "OperationOutcome.implicitRules" + "Organization.implicitRules" + "OrganizationAffiliation.implicitRules" + "Parameters.implicitRules" + "Patient.implicitRules" + "PaymentNotice.implicitRules" + "PaymentReconciliation.implicitRules" + "Person.implicitRules" + "PlanDefinition.implicitRules" + "PlanDefinition.url" + "Practitioner.implicitRules" + "PractitionerRole.implicitRules" + "Procedure.implicitRules" + "Procedure.instantiatesUri" + "Provenance.implicitRules" + "Provenance.policy" + "Questionnaire.implicitRules" + "Questionnaire.url" + "Questionnaire.item.definition" + "QuestionnaireResponse.implicitRules" + "QuestionnaireResponse.item.definition" + "RelatedPerson.implicitRules" + "RequestGroup.implicitRules" + "RequestGroup.instantiatesUri" + "ResearchDefinition.implicitRules" + "ResearchDefinition.url" + "ResearchElementDefinition.implicitRules" + "ResearchElementDefinition.url" + "ResearchStudy.implicitRules" + "ResearchSubject.implicitRules" + "RiskAssessment.implicitRules" + "RiskEvidenceSynthesis.implicitRules" + "RiskEvidenceSynthesis.url" + "Schedule.implicitRules" + "SearchParameter.implicitRules" + "SearchParameter.url" + "ServiceRequest.implicitRules" + "ServiceRequest.instantiatesUri" + "Slot.implicitRules" + "Specimen.implicitRules" + "SpecimenDefinition.implicitRules" + "StructureDefinition.implicitRules" + "StructureDefinition.url" + "StructureDefinition.mapping.uri" + "StructureDefinition.type" + "StructureMap.implicitRules" + "StructureMap.url" + "Subscription.implicitRules" + "Substance.implicitRules" + "SubstanceNucleicAcid.implicitRules" + "SubstancePolymer.implicitRules" + "SubstanceProtein.implicitRules" + "SubstanceReferenceInformation.implicitRules" + "SubstanceSourceMaterial.implicitRules" + "SubstanceSpecification.implicitRules" + "SupplyDelivery.implicitRules" + "SupplyRequest.implicitRules" + "Task.implicitRules" + "Task.instantiatesUri" + "TerminologyCapabilities.implicitRules" + "TerminologyCapabilities.url" + "TestReport.implicitRules" + "TestReport.participant.uri" + "TestReport.setup.action.operation.detail" + "TestScript.implicitRules" + "TestScript.url" + "TestScript.metadata.link.url" + "TestScript.metadata.capability.link" + "ValueSet.implicitRules" + "ValueSet.url" + "ValueSet.compose.include.system" + "ValueSet.expansion.identifier" + "ValueSet.expansion.contains.system" + "VerificationResult.implicitRules" + "VisionPrescription.implicitRules" + "MetadataResource.implicitRules" + "MetadataResource.url" + "Coding.system" + "Identifier.system" + "Quantity.system" + "Reference.type") + :primitive/uri-interned + (keyword + (if (Character/isLowerCase ^char (first code)) + "primitive" + "complex") + code)))) (defn base-field-name "The field name without possible polymorphic type." @@ -213,10 +456,17 @@ JsonToken/START_ARRAY "array start" (format "token %s" (current-token parser)))) +(defn- incorrect-value-anom* + ([value locator expected-type] + (let [msg (format "Error on %s. Expected type is `%s`." value expected-type)] + (ba/incorrect msg :fhir/issues [(fhir-issue msg locator)]))) + ([value locator expected-type reason-msg] + (let [msg (format "Error on %s. Expected type is `%s`. %s" value expected-type reason-msg)] + (ba/incorrect msg :fhir/issues [(fhir-issue msg locator)])))) + (defn- incorrect-value-anom [parser locator expected-type] (when-ok [value (get-current-value parser locator)] - (let [msg (format "Error on %s. Expected type is `%s`." value expected-type)] - (ba/incorrect msg :fhir/issues [(fhir-issue msg locator)])))) + (incorrect-value-anom* value locator expected-type))) (defn- unknown-property-anom [locator name] (let [msg (format "Unknown property `%s`." name)] @@ -291,18 +541,21 @@ value." [field-name key m constructor value locator] (if-some [primitive-value (get-value m key)] - (if (some? (type/value primitive-value)) + (if (some? (:value primitive-value)) (duplicate-property-anom field-name locator) - (put-value! m key (type/assoc-value primitive-value value))) - (put-value! m key (constructor value)))) + (put-value! m key (assoc primitive-value :value value))) + (if-ok [value (constructor value)] + (put-value! m key value) + #(let [msg (::anom/message %)] + (ba/incorrect msg :fhir/issues [(fhir-issue msg (cons (name key) locator))]))))) (defn- assoc-primitive-many-value "Like `assoc-primitive-value` but with a single value for cardinality many." [{:keys [field-name key]} m constructor value locator] (if-some [primitive-value (first (get-value m key))] - (if (some? (type/value primitive-value)) + (if (some? (:value primitive-value)) (duplicate-property-anom field-name locator) - (put-value! m key [(type/assoc-value primitive-value value)])) + (put-value! m key [(assoc primitive-value :value value)])) (put-value! m key [(constructor value)]))) (defn- primitive-boolean-value-handler @@ -335,11 +588,11 @@ token (when-ok [value (extract-value parser (cons path locator))] (if-some [primitive-value (when (< i (.size l)) (.get l i))] - (if (some? (type/value primitive-value)) + (if (some? (:value primitive-value)) (duplicate-property-anom field-name locator) - (recur (doto l (.set i (type/assoc-value primitive-value value))) (inc i))) + (recur (doto l (.set i (assoc primitive-value :value value))) (inc i))) (recur (set-value! l i (constructor value)) (inc i)))) - JsonToken/END_ARRAY (put-value! m key (PersistentVector/create l)) + JsonToken/END_ARRAY (put-value! m key (Lists/intern l)) JsonToken/VALUE_NULL (recur (cond-> l (= i (.size l)) (doto (.add nil))) (inc i)) (incorrect-value-anom parser (cons path locator) (str expected-type "[]"))))) @@ -369,18 +622,18 @@ token-1 (when-ok [value (extract-value-1 parser (cons path locator))] (if-some [primitive-value (when (< i (.size l)) (.get l i))] - (if (some? (type/value primitive-value)) + (if (some? (:value primitive-value)) (duplicate-property-anom field-name locator) - (recur (doto l (.set i (type/assoc-value primitive-value value))) (inc i))) + (recur (doto l (.set i (assoc primitive-value :value value))) (inc i))) (recur (set-value! l i (constructor value)) (inc i)))) token-2 (when-ok [value (extract-value-2 parser (cons path locator))] (if-some [primitive-value (when (< i (.size l)) (.get l i))] - (if (some? (type/value primitive-value)) + (if (some? (:value primitive-value)) (duplicate-property-anom field-name locator) - (recur (doto l (.set i (type/assoc-value primitive-value value))) (inc i))) + (recur (doto l (.set i (assoc primitive-value :value value))) (inc i))) (recur (set-value! l i (constructor value)) (inc i)))) - JsonToken/END_ARRAY (put-value! m key (PersistentVector/create l)) + JsonToken/END_ARRAY (put-value! m key (Lists/intern l)) JsonToken/VALUE_NULL (recur (cond-> l (= i (.size l)) (doto (.add nil))) (inc i)) (incorrect-value-anom parser (cons path locator) expected-type)))) @@ -398,7 +651,7 @@ (def ^:private primitive-id-handler "A property-handler for id properties." - (create-system-string-handler type/assoc-id "id" "string")) + (create-system-string-handler #(assoc %1 :id %2) "id" "string")) (defn- parse-complex-list [handler type-handlers parser locator] (loop [list (ArrayList.)] @@ -406,7 +659,7 @@ JsonToken/START_OBJECT (when-ok [value (handler type-handlers parser (cons (.size list) locator))] (recur (doto list (.add value)))) - JsonToken/END_ARRAY (PersistentVector/create list) + JsonToken/END_ARRAY (Lists/intern list) (incorrect-value-anom parser (cons (.size list) locator) (handler))))) (defn- unsupported-type-anom [type] @@ -424,15 +677,15 @@ (cond-next-token parser locator JsonToken/START_ARRAY (when-ok [list (parse-complex-list extension-handler type-handlers parser (cons "extension" locator))] - (recur (type/assoc-extension data list))) + (recur (assoc data :extension list))) JsonToken/START_OBJECT (when-ok [extension (extension-handler type-handlers parser (cons 0 (cons "extension" locator)))] - (recur (type/assoc-extension data [extension]))) + (recur (assoc data :extension (Lists/intern [extension])))) (incorrect-value-anom parser (cons "extension" locator) "Extension[]"))) (unknown-property-anom locator (current-name parser))) JsonToken/END_OBJECT data))) -(defn- trim-trailing-nils ^List [^List vector] +(defn- trim-trailing-nils [^List vector] (loop [i (.size vector)] (if (zero? i) (ArrayList.) @@ -466,7 +719,7 @@ (recur (doto l (.set i primitive-value)) (inc i))) (when-ok [data (parse-extended-primitive-properties type-handlers parser (cons path locator) {})] (recur (set-value! l i (constructor data)) (inc i)))) - JsonToken/END_ARRAY (put-value! m key (PersistentVector/create (trim-trailing-nils l))) + JsonToken/END_ARRAY (put-value! m key (Lists/intern (trim-trailing-nils l))) JsonToken/VALUE_NULL (recur (cond-> l (= i (.size l)) (doto (.add nil))) (inc i)) (incorrect-value-anom parser (cons path locator) "primitive extension map")))) @@ -503,30 +756,32 @@ representation using `constructor`." [def] (->> (primitive-value-handler def type/decimal JsonToken/VALUE_NUMBER_INT - get-long JsonToken/VALUE_NUMBER_FLOAT get-decimal + get-decimal JsonToken/VALUE_NUMBER_FLOAT get-decimal "decimal") (primitive-handler def type/decimal))) (defn- get-text-pattern [pattern] (fn [parser locator expected-type] - (when-ok [s (get-text parser locator)] - (if (.matches (re-matcher pattern s)) - s - (incorrect-value-anom parser locator (format "%s, regex %s" expected-type pattern)))))) + (when-ok [text (get-text parser locator)] + (if (.matches (re-matcher pattern text)) + text + (incorrect-value-anom* (format "value `%s`" text) locator (format "%s, regex %s" expected-type pattern)))))) + +(defn- parse-text [system-parser locator expected-type text] + (if-ok [value (system-parser text)] + value + #(incorrect-value-anom* (format "value `%s`" text) locator expected-type (::anom/message %)))) (defn- system-value-parser ([system-parser expected-type] (fn system-value-parser [parser locator] (when-ok [text (get-text parser locator)] - (let [value (system-parser text)] - (if (ba/anomaly? value) - (incorrect-value-anom parser locator expected-type) - value))))) + (parse-text system-parser locator expected-type text)))) ([system-parser expected-type pattern] (let [get-text (get-text-pattern pattern)] (fn pattern-system-value-parser [parser locator] (when-ok [text (get-text parser locator expected-type)] - (system-parser text)))))) + (parse-text system-parser locator expected-type text)))))) (defn- primitive-string-handler "A handler that reads a string value and creates the internal representation @@ -589,9 +844,6 @@ :system/string {field-name (create-system-string-handler #(put-value! %1 key %2) (name key) "string")} - :system.string/uri - {field-name (create-system-string-handler #(put-value! %1 key (impl/intern-string %2)) (name key) "uri")} - :primitive/boolean (primitive-handler def type/boolean (primitive-boolean-value-handler def)) @@ -601,6 +853,9 @@ :primitive/string (primitive-string-handler def type/string identity "string") + :primitive/string-interned + (primitive-string-handler def type/string-interned identity "string") + :primitive/decimal (primitive-decimal-handler def) @@ -608,6 +863,10 @@ (primitive-string-handler def type/uri identity "uri" #"(?U)[\p{Print}&&[^\p{Blank}]]*" use-regex) + :primitive/uri-interned + (primitive-string-handler def type/uri-interned identity "uri" + #"(?U)[\p{Print}&&[^\p{Blank}]]*" use-regex) + :primitive/url (primitive-string-handler def type/url identity "url" #"(?U)[\p{Print}&&[^\p{Blank}]]*" use-regex) @@ -658,11 +917,11 @@ #"urn:uuid:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" use-regex) :primitive/xhtml - (primitive-string-handler def type/->Xhtml identity "xhtml") + (primitive-string-handler def type/xhtml identity "xhtml") (if (#{"complex" "element" "backboneElement"} (namespace type)) (create-complex-property-handler opts def) - (ba/unsupported (format "unsupported type: %s" (name type)))))) + (unsupported-type-anom (name type))))) (defn- create-property-handlers "Returns a map of JSON property names to property handlers." @@ -692,18 +951,43 @@ (defn- complex-type-finalizer [type] (condp = type "Address" #(type/address (persist-array-map %)) + "Age" #(type/age (persist-array-map %)) + "Annotation" #(type/annotation (persist-array-map %)) "Attachment" #(type/attachment (persist-array-map %)) + "Bundle.entry.search" #(type/bundle-entry-search (persist-array-map %)) "CodeableConcept" #(type/codeable-concept (persist-array-map %)) "Coding" #(type/coding (persist-array-map %)) + "ContactDetail" #(type/contact-detail (persist-array-map %)) + "ContactPoint" #(type/contact-point (persist-array-map %)) + "Contributor" #(type/contributor (persist-array-map %)) + "Count" #(type/count (persist-array-map %)) + "DataRequirement" #(type/data-requirement (persist-array-map %)) + "DataRequirement.codeFilter" #(type/data-requirement-code-filter (persist-array-map %)) + "DataRequirement.dateFilter" #(type/data-requirement-date-filter (persist-array-map %)) + "DataRequirement.sort" #(type/data-requirement-sort (persist-array-map %)) + "Distance" #(type/distance (persist-array-map %)) + "Dosage" #(type/dosage (persist-array-map %)) + "Dosage.doseAndRate" #(type/dosage-dose-and-rate (persist-array-map %)) + "Duration" #(type/duration (persist-array-map %)) + "Expression" #(type/expression (persist-array-map %)) "Extension" #(type/extension (persist-array-map %)) "HumanName" #(type/human-name (persist-array-map %)) "Identifier" #(type/identifier (persist-array-map %)) + "Meta" #(type/meta (persist-array-map %)) + "Money" #(type/money (persist-array-map %)) + "ParameterDefinition" #(type/parameter-definition (persist-array-map %)) "Period" #(type/period (persist-array-map %)) "Quantity" #(type/quantity (persist-array-map %)) + "Range" #(type/range (persist-array-map %)) "Ratio" #(type/ratio (persist-array-map %)) "Reference" #(type/reference (persist-array-map %)) - "Meta" #(type/meta (persist-array-map %)) - "Bundle.entry.search" #(type/bundle-entry-search (persist-array-map %)) + "RelatedArtifact" #(type/related-artifact (persist-array-map %)) + "SampledData" #(type/sampled-data (persist-array-map %)) + "Signature" #(type/signature (persist-array-map %)) + "Timing" #(type/timing (persist-array-map %)) + "Timing.repeat" #(type/timing-repeat (persist-array-map %)) + "TriggerDefinition" #(type/trigger-definition (persist-array-map %)) + "UsageContext" #(type/usage-context (persist-array-map %)) (let [fhir-type-kw (fhir-type-keyword type)] #(persist-map (put-value! % :fhir/type fhir-type-kw))))) @@ -825,7 +1109,7 @@ ;; skip the first token (next-token! parser locator) (type-handler type-handlers parser (if (empty? locator) [type] locator))) - (ba/unsupported (format "Unsupported resource type: %s" type))) + (unsupported-type-anom type)) (let [msg "Missing property `resourceType`."] (ba/incorrect msg :fhir/issues [(fhir-issue msg locator)]))))))) @@ -870,13 +1154,10 @@ (.createParser ^JsonFactory factory source)) String (-create-parser [source factory] - (.createParser ^JsonFactory factory source))) - -(extend byte/1 - ParserFactory - {:-create-parser - (fn [source factory] - (.createParser ^JsonFactory factory ^bytes source))}) + (.createParser ^JsonFactory factory source)) + byte/1 + (-create-parser [source factory] + (.createParser ^JsonFactory factory ^bytes source))) (def ^:private ^JsonFactory json-factory (doto (-> (JsonFactory/builder) @@ -900,15 +1181,15 @@ (if-some [handler (get type-handlers (keyword type))] (with-open [parser (-create-parser source json-factory)] (read-value type-handlers parser (RT/list type) handler)) - (ba/unsupported (format "Unsupported resource type: %s" type))))) + (unsupported-type-anom type)))) (defn write-json [type-handlers out value] - (if-some [type (type/type value)] + (if-some [type (:fhir/type value)] (if-some [handler (get type-handlers type)] (with-open [gen (.createGenerator json-factory ^OutputStream out)] (handler type-handlers gen value)) - (ba/unsupported (format "Unsupported resource type: %s" (name type)))) - (ba/incorrect (format "Missing resource type.")))) + (unsupported-type-anom (name type))) + (ba/incorrect (format "Missing type.")))) (def ^:private ^JsonFactory cbor-factory (doto (-> (CBORFactory/builder) @@ -924,10 +1205,12 @@ (if-some [handler (get type-handlers (if (= :summary variant) (keyword "summary" type) (keyword type)))] (with-open [parser (.createParser ^JsonFactory cbor-factory ^bytes source)] (read-value type-handlers parser (RT/list type) handler)) - (ba/unsupported (format "Unsupported resource type: %s" type)))) + (unsupported-type-anom type))) (defn write-cbor [type-handlers out value] - (if-some [handler (get type-handlers (type/type value))] - (with-open [gen (.createGenerator cbor-factory ^OutputStream out)] - (handler type-handlers gen value)) - (ba/unsupported (format "Unsupported resource type: %s" (name (type/type value)))))) + (if-some [type (:fhir/type value)] + (if-some [handler (get type-handlers type)] + (with-open [gen (.createGenerator cbor-factory ^OutputStream out)] + (handler type-handlers gen value)) + (unsupported-type-anom (name type))) + (ba/incorrect (format "Missing type.")))) diff --git a/modules/fhir-structure/src/blaze/fhir/spec/spec.clj b/modules/fhir-structure/src/blaze/fhir/spec/spec.clj index f270df8a3..c506e8974 100644 --- a/modules/fhir-structure/src/blaze/fhir/spec/spec.clj +++ b/modules/fhir-structure/src/blaze/fhir/spec/spec.clj @@ -31,6 +31,9 @@ (s/def :blaze.resource/elements (s/coll-of simple-keyword?)) +(s/def :fhir/value + (s/keys :req [:fhir/type])) + (s/def :fhir/Resource #(s2/valid? :fhir/Resource %)) diff --git a/modules/fhir-structure/src/blaze/fhir/spec/type.clj b/modules/fhir-structure/src/blaze/fhir/spec/type.clj index 786123500..0ba0ca800 100644 --- a/modules/fhir-structure/src/blaze/fhir/spec/type.clj +++ b/modules/fhir-structure/src/blaze/fhir/spec/type.clj @@ -2,1195 +2,352 @@ "Functions for primitive and complex types." (:refer-clojure :exclude - [boolean boolean? decimal? integer? long meta str string? time type uri? uuid?]) + [boolean boolean? count decimal? integer? long meta range str string? time + type uri? uuid?]) (:require - [blaze.anomaly :as ba :refer [if-ok]] + [blaze.anomaly :as ba] [blaze.byte-string] - [blaze.fhir.spec.impl.intern :as intern] - [blaze.fhir.spec.type.json :as json] - [blaze.fhir.spec.type.macros :as macros - :refer [def-complex-type def-primitive-type defextended]] - [blaze.fhir.spec.type.protocols :as p] - [blaze.fhir.spec.type.system :as system] + [blaze.fhir.spec.type.string-util :as su] [blaze.fhir.spec.xml :as spec-xml] [blaze.util :refer [str]] - [clojure.alpha.spec :as s2] [clojure.data.xml :as xml] [clojure.data.xml.name :as xml-name] [clojure.data.xml.node :as xml-node] - [clojure.string :as str]) + [clojure.string :as str] + [cognitect.anomalies :as anom]) (:import - [blaze.fhir.spec.type.system Date DateTime] - [clojure.lang ILookup IPersistentMap Keyword] - [com.fasterxml.jackson.core JsonGenerator] - [com.google.common.hash PrimitiveSink] + [blaze.fhir.spec.type + Address Age Annotation Attachment Base Base64Binary BundleEntrySearch + Canonical Code CodeableConcept Coding ContactDetail ContactPoint Contributor Count + DataRequirement DataRequirement$CodeFilter DataRequirement$DateFilter DataRequirement$Sort + Date DateTime Decimal Distance Dosage Dosage$DoseAndRate Duration Expression Extension HumanName Id + Identifier Instant Markdown Meta Money Oid ParameterDefinition Period PositiveInt Primitive Quantity + Range Ratio Reference RelatedArtifact SampledData Signature String$Interned + String$Normal Time Timing Timing$Repeat TriggerDefinition UnsignedInt Uri Uri$Interned + Uri$Normal Url UsageContext Uuid Xhtml] + [clojure.lang IPersistentMap PersistentVector] [java.io Writer] - [java.time - DateTimeException Instant LocalDate LocalDateTime LocalTime OffsetDateTime ZoneOffset] - [java.time.format DateTimeFormatter] - [java.util Comparator List Map$Entry UUID])) + [java.time LocalTime OffsetDateTime])) (xml-name/alias-uri 'f "http://hl7.org/fhir") (set! *warn-on-reflection* true) (set! *unchecked-math* :warn-on-boxed) -(defn type - "Returns the FHIR type if `x` if it has some." - [x] - (p/-type x)) - -(defn value - "Returns the possible value of the primitive value `x` as FHIRPath system - type." - [x] - (p/-value x)) - -(defn assoc-id - "Associates `id` to `x`." - [x id] - (p/-assoc-id x id)) - -(defn assoc-extension - "Associates `extension` to `x`." - [x extension] - (p/-assoc-extension x extension)) - -(defn assoc-value - "Associates `value` to `x`." - [x value] - (p/-assoc-value x value)) - -(defn to-xml [x] - (p/-to-xml x)) - -(defn hash-into [x sink] - (p/-hash-into x sink)) +(defn to-xml [^Primitive x] + (xml-node/element* + nil + (let [value (.valueAsString x)] + (cond-> {} + (some? (.id x)) + (assoc :id (.id x)) + (some? value) + (assoc :value (spec-xml/replace-invalid-chars value)))) + (.extension x))) (defn references "Returns a collection of local references which are tuples of FHIR resource type name and FHIR resource id." [x] - (p/-references x)) - -(defn- create-fn [intern create parse-fn] - (fn [x] - (if (map? x) - (let [{:keys [id extension value]} x] - (cond - (and (nil? value) (p/-interned extension) (nil? id)) - (intern {:extension extension}) - - (and (nil? extension) (nil? id)) - (parse-fn value) - - :else - (create id extension (some-> value parse-fn)))) - (parse-fn x)))) - -(defn- system-to-xml [x] - (xml-node/element nil {:value (system/-to-string x)})) - -;; ---- nil ------------------------------------------------------------------- - -;; Be sure all methods can be called on nil. -(extend-protocol p/FhirType - nil - (-type [_]) - (-interned [_] true) - (-assoc-id [_ _]) - (-assoc-extension [_ _]) - (-value [_]) - (-assoc-value [_ _]) - (-has-primary-content [_] false) - (-serialize-json [_ _ _]) - (-has-secondary-content [_] false) - (-serialize-json-secondary [_ _ _]) - (-to-xml [_]) - (-hash-into [_ _]) - (-references [_])) - -;; ---- Object ------------------------------------------------------------------- - -;; Other instances have no type. -(extend-protocol p/FhirType - Object - (-type [_]) - (-interned [_] false) - (-references [_])) + (PersistentVector/create (.toList (Base/references x)))) + +;; ---- Macros ---------------------------------------------------------------- + +(defn print-data-element [separate? ^Writer writer entry] + (when separate? + (.write writer " ")) + (.write writer (str ":" (name (key entry)) " ")) + (print-method (val entry) writer) + true) + +(defmacro print-type [name & [tag-name]] + `(do (.write ~'w ~(str "#" (or tag-name (format "fhir/%s" name)) "{")) + (reduce #(print-data-element %1 ~'w %2) false ~'v) + (.write ~'w "}"))) + +(defmacro def-print-method-primitive [name & [tag-name]] + (let [class-sym (symbol (str "blaze.fhir.spec.type." (su/capital (str name))))] + `(defmethod print-method ~class-sym + [~(with-meta 'v {:tag class-sym}) ~(with-meta 'w {:tag 'Writer})] + (if (or (.id ~'v) (seq (.extension ~'v))) + (print-type ~name ~tag-name) + (do (.write ~'w ~(str "#" (or tag-name (format "fhir/%s" name)) " ")) + (print-method (.value ~'v) ~'w)))))) ;; ---- boolean --------------------------------------------------------------- -(declare boolean) - -(extend-protocol p/FhirType - Boolean - (-type [_] :fhir/boolean) - (-interned [_] true) - (-assoc-id [b id] (boolean {:id id :value b})) - (-assoc-extension [b extension] (boolean {:extension extension :value b})) - (-value [b] b) - (-assoc-value [_ value] (boolean value)) - (-has-primary-content [_] true) - (-serialize-json [b generator] - (.writeBoolean ^JsonGenerator generator b)) - (-has-secondary-content [_] false) - (-serialize-json-secondary [_ generator] - (.writeNull ^JsonGenerator generator)) - (-to-xml [b] - (xml-node/element nil {:value (str b)})) - (-hash-into [b sink] - (doto ^PrimitiveSink sink - (.putByte (byte 0)) ; :fhir/boolean - (.putByte (byte 2))) ; :value - (system/-hash-into b sink)) - (-references [_])) - -(defextended ExtendedBoolean [id extension ^Boolean value] - :fhir-type :fhir/boolean :hash-num 0 :interned true) - -(def ^{:arglists '([x])} boolean - (let [intern (intern/intern-value map->ExtendedBoolean)] - (fn [x] - (cond - (map? x) - (let [{:keys [id extension value]} x] - (cond - (and (nil? extension) (nil? id)) - value - - (and (p/-interned extension) (nil? id)) - (intern {:extension extension :value value}) - - :else - (->ExtendedBoolean id extension value))) - (clojure.core/boolean? x) x - :else ::s2/invalid)))) - -(defn xml->Boolean - {:arglists '([element])} - [{{:keys [id value]} :attrs content :content}] - (let [extension (seq content) - value (some-> ^String value (Boolean/valueOf))] - (if (or id extension) - (boolean {:id id :extension extension :value value}) - (boolean value)))) - (defn boolean? [x] - (identical? :fhir/boolean (type x))) + (instance? blaze.fhir.spec.type.Boolean x)) -;; ---- integer --------------------------------------------------------------- +(defn boolean [x] + (cond + (map? x) + (blaze.fhir.spec.type.Boolean/create x) + + (true? x) blaze.fhir.spec.type.Boolean/TRUE + (false? x) blaze.fhir.spec.type.Boolean/FALSE + + :else (ba/incorrect (format "Invalid boolean value `%s`." x)))) -(declare integer) - -(extend-protocol p/FhirType - Integer - (-type [_] :fhir/integer) - (-interned [_] false) - (-assoc-id [i id] (integer {:id id :value i})) - (-assoc-extension [i extension] (integer {:extension extension :value i})) - (-value [i] i) - (-assoc-value [_ value] (integer value)) - (-has-primary-content [_] true) - (-serialize-json [i generator] - (.writeNumber ^JsonGenerator generator i)) - (-has-secondary-content [_] false) - (-serialize-json-secondary [_ generator] - (.writeNull ^JsonGenerator generator)) - (-to-xml [i] - (xml-node/element nil {:value (str i)})) - (-hash-into [i sink] - (doto ^PrimitiveSink sink - (.putByte (byte 1)) ; :fhir/integer - (.putByte (byte 2))) ; :value - (system/-hash-into i sink)) - (-references [_])) - -(defextended ExtendedInteger [id extension ^Integer value] - :fhir-type :fhir/integer :hash-num 1) - -(def ^{:arglists '([x])} integer - (create-fn (intern/intern-value map->ExtendedInteger) ->ExtendedInteger - #(if (clojure.core/int? %) (clojure.core/int %) ::s2/invalid))) - -(defn xml->Integer - {:arglists '([element])} - [{{:keys [id value]} :attrs content :content}] - (let [extension (seq content) - value (some-> ^String value (Integer/valueOf))] - (if (or id extension) - (integer {:id id :extension extension :value value}) - (integer value)))) +(def-print-method-primitive boolean) + +;; ---- integer --------------------------------------------------------------- (defn integer? [x] - (identical? :fhir/integer (type x))) - -;; ---- long --------------------------------------------------------------- - -(declare long) - -(extend-protocol p/FhirType - Long - (-type [_] :fhir/long) - (-interned [_] false) - (-assoc-id [l id] (long {:id id :value l})) - (-assoc-extension [l extension] (long {:extension extension :value l})) - (-value [l] l) - (-assoc-value [_ value] (long value)) - (-has-primary-content [_] true) - (-serialize-json [l generator] - (.writeNumber ^JsonGenerator generator (unchecked-long l))) - (-has-secondary-content [_] false) - (-serialize-json-secondary [_ generator] - (.writeNull ^JsonGenerator generator)) - (-to-xml [l] - (xml-node/element nil {:value (str l)})) - (-hash-into [l sink] - (doto ^PrimitiveSink sink - (.putByte (byte 2)) ; :fhir/long - (.putByte (byte 2))) ; :value - (system/-hash-into l sink)) - (-references [_])) - -(defextended ExtendedLong [id extension ^Long value] - :fhir-type :fhir/long :hash-num 2) - -(def ^{:arglists '([x])} long - (create-fn (intern/intern-value map->ExtendedLong) ->ExtendedLong - #(if (clojure.core/int? %) (clojure.core/long %) ::s2/invalid))) - -(defn xml->Long - {:arglists '([element])} - [{{:keys [id value]} :attrs content :content}] - (let [extension (seq content) - value (some-> ^String value (Long/valueOf))] - (if (or id extension) - (long {:id id :extension extension :value value}) - (long value)))) - -(defn long? [x] - (identical? :fhir/long (type x))) + (instance? blaze.fhir.spec.type.Integer x)) -;; ---- string ---------------------------------------------------------------- +(defn integer [x] + (cond + (map? x) (ba/try-one IllegalArgumentException ::anom/incorrect + (blaze.fhir.spec.type.Integer/create ^IPersistentMap x)) + (int? x) (ba/try-one IllegalArgumentException ::anom/incorrect + (blaze.fhir.spec.type.Integer/create ^Number x)) + :else (ba/incorrect (format "Invalid integer value `%s`." x)))) -(declare string) - -(extend-protocol p/FhirType - String - (-type [_] :fhir/string) - (-interned [_] false) - (-assoc-id [s id] (string {:id id :value s})) - (-assoc-extension [s extension] (string {:extension extension :value s})) - (-value [s] s) - (-assoc-value [_ value] value) - (-has-primary-content [_] true) - (-serialize-json [s generator] - (.writeString ^JsonGenerator generator s)) - (-has-secondary-content [_] false) - (-serialize-json-secondary [_ generator] - (.writeNull ^JsonGenerator generator)) - (-to-xml [s] - (xml-node/element nil {:value (spec-xml/replace-invalid-chars s)})) - (-hash-into [s sink] - (doto ^PrimitiveSink sink - (.putByte (byte 3)) ; :fhir/string - (.putByte (byte 2))) ; :value - (system/-hash-into s sink)) - (-references [_])) - -(defextended ExtendedString [id extension value] - :fhir-type :fhir/string :hash-num 3) - -(def ^{:arglists '([x])} string - (create-fn (intern/intern-value map->ExtendedString) ->ExtendedString - identity)) - -(defn xml->String - {:arglists '([element])} - [{{:keys [id value]} :attrs content :content}] - (let [extension (seq content)] - (if (or id extension) - (string {:id id :extension extension :value value}) - (string value)))) - -(def ^{:arglists '([x])} intern-string - (let [intern (intern/intern-value identity) - intern-extended (intern/intern-value map->ExtendedString)] - (fn [x] - (if (map? x) - (let [{:keys [id extension value]} x] - (if (and (p/-interned extension) (nil? id)) - (intern-extended {:extension extension :value value}) - (->ExtendedString id extension value))) - (intern x))))) - -(defn xml->InternedString - {:arglists '([element])} - [{{:keys [id value]} :attrs content :content}] - (let [extension (seq content)] - (if (or id extension) - (intern-string {:id id :extension extension :value value}) - (intern-string value)))) +(def-print-method-primitive integer) + +;; ---- string ---------------------------------------------------------------- (defn string? [x] - (identical? :fhir/string (type x))) + (instance? blaze.fhir.spec.type.String x)) -;; ---- decimal --------------------------------------------------------------- +(defn string [x] + (if (clojure.core/string? x) + (blaze.fhir.spec.type.String/create ^String x) + (blaze.fhir.spec.type.String/create ^IPersistentMap x))) -(declare decimal) - -(extend-protocol p/FhirType - BigDecimal - (-type [_] :fhir/decimal) - (-interned [_] false) - (-assoc-id [d id] (decimal {:id id :value d})) - (-assoc-extension [d extension] (decimal {:extension extension :value d})) - (-value [d] d) - (-assoc-value [_ value] (decimal value)) - (-has-primary-content [_] true) - (-serialize-json [d generator] - (.writeNumber ^JsonGenerator generator d)) - (-has-secondary-content [_] false) - (-serialize-json-secondary [_ generator] - (.writeNull ^JsonGenerator generator)) - (-to-xml [d] - (xml-node/element nil {:value (str d)})) - (-hash-into [d sink] - (doto ^PrimitiveSink sink - (.putByte (byte 4)) ; :fhir/decimal - (.putByte (byte 2))) ; :value - (system/-hash-into d sink)) - (-references [_])) - -(defextended ExtendedDecimal [id extension ^BigDecimal value] - :fhir-type :fhir/decimal :hash-num 4 :value-constructor bigdec) - -(def ^{:arglists '([x])} decimal - (create-fn (intern/intern-value map->ExtendedDecimal) ->ExtendedDecimal - #(cond - (int? %) (BigDecimal/valueOf (clojure.core/long %)) - (clojure.core/decimal? %) % - :else ::s2/invalid))) - -(defn xml->Decimal - {:arglists '([element])} - [{{:keys [id value]} :attrs content :content}] - (let [extension (seq content) - value (some-> ^String value (BigDecimal.))] - (if (or id extension) - (decimal {:id id :extension extension :value value}) - (decimal value)))) +(defn string-interned [x] + (if (clojure.core/string? x) + (blaze.fhir.spec.type.String/createForceIntern ^String x) + (blaze.fhir.spec.type.String/createForceIntern ^IPersistentMap x))) + +(def-print-method-primitive String$Normal "fhir/string") +(def-print-method-primitive String$Interned "fhir/string-interned") + +;; ---- decimal --------------------------------------------------------------- (defn decimal? [x] - (identical? :fhir/decimal (type x))) + (instance? Decimal x)) + +(defn decimal [x] + (if (clojure.core/decimal? x) + (Decimal/create ^BigDecimal x) + (Decimal/create ^IPersistentMap x))) + +(def-print-method-primitive decimal) ;; ---- uri ------------------------------------------------------------------- -(declare uri?) -(declare uri) -(declare create-uri) -(declare map->ExtendedUri) -(declare xml->Uri) +(defn uri? [x] + (instance? Uri x)) -(def-primitive-type Uri [^String value] :hash-num 5 :interned true) +(defn uri [x] + (if (clojure.core/string? x) + (Uri/create ^String x) + (Uri/create ^IPersistentMap x))) + +(defn uri-interned [x] + (if (clojure.core/string? x) + (Uri/createForceIntern ^String x) + (Uri/createForceIntern ^IPersistentMap x))) + +(def-print-method-primitive Uri$Normal "fhir/uri") +(def-print-method-primitive Uri$Interned "fhir/uri-interned") ;; ---- url ------------------------------------------------------------------- -(declare url?) -(declare url) -(declare map->ExtendedUrl) -(declare xml->Url) +(defn url? [x] + (instance? Url x)) + +(defn url [x] + (if (clojure.core/string? x) + (Url/create ^String x) + (Url/create ^IPersistentMap x))) -(def-primitive-type Url [value] :hash-num 6) +(def-print-method-primitive url) ;; ---- canonical ------------------------------------------------------------- -(declare canonical?) -(declare canonical) -(declare create-canonical) -(declare map->ExtendedCanonical) -(declare xml->Canonical) +(defn canonical? [x] + (instance? Canonical x)) -(def-primitive-type Canonical [^String value] :hash-num 7 :interned true) +(defn canonical [x] + (if (clojure.core/string? x) + (Canonical/create ^String x) + (Canonical/create ^IPersistentMap x))) + +(def-print-method-primitive canonical) ;; ---- base64Binary ---------------------------------------------------------- -(declare base64Binary?) -(declare base64Binary) -(declare map->ExtendedBase64Binary) -(declare xml->Base64Binary) +(defn base64Binary? [x] + (instance? Base64Binary x)) -(def-primitive-type Base64Binary [value] :hash-num 8) +(defn base64Binary [x] + (if (clojure.core/string? x) + (Base64Binary/create ^String x) + (Base64Binary/create ^IPersistentMap x))) -;; ---- instant --------------------------------------------------------------- +(def-print-method-primitive base64Binary) -(declare instant) - -(defmethod print-method Instant [^Instant instant ^Writer w] - (doto w - (.write "#java/instant\"") - (.write (.toString instant)) - (.write "\""))) - -(defmethod print-dup Instant [^Instant instant ^Writer w] - (.write w "#=(java.time.Instant/ofEpochSecond ") - (.write w (str (.getEpochSecond instant))) - (.write w " ") - (.write w (str (.getNano instant))) - (.write w ")")) - -;; Implementation of a FHIR instant with a variable ZoneOffset. -(deftype OffsetInstant [value] - p/FhirType - (-type [_] :fhir/instant) - (-interned [_] false) - (-assoc-id [_ id] (instant {:id id :value value})) - (-assoc-extension [_ extension] (instant {:extension extension :value value})) - (-value [_] value) - (-assoc-value [_ val] (instant val)) - (-has-primary-content [_] true) - (-serialize-json [_ generator] - (.writeString ^JsonGenerator generator ^String (system/-to-string value))) - (-has-secondary-content [_] false) - (-serialize-json-secondary [_ generator] - (.writeNull ^JsonGenerator generator)) - (-to-xml [_] - (system-to-xml value)) - (-hash-into [_ sink] - (doto ^PrimitiveSink sink - (.putByte (byte 9)) ; :fhir/instant - (.putByte (byte 2))) ; :value - (system/-hash-into value sink)) - (-references [_]) - Object - (equals [this x] - (or (identical? this x) - (and (instance? OffsetInstant x) - (.equals value (.value ^OffsetInstant x))))) - (hashCode [_] - (.hashCode value)) - (toString [_] - (str value))) - -(defmethod print-method OffsetInstant [^OffsetInstant instant ^Writer w] - (doto w - (.write "#fhir/instant\"") - (.write ^String (system/-to-string (.-value instant))) - (.write "\""))) - -(defextended ExtendedOffsetInstant [id extension value] - :fhir-type :fhir/instant :hash-num 9) - -(extend-protocol p/FhirType - Instant - (-type [_] :fhir/instant) - (-interned [_] false) - (-assoc-id [i id] (instant {:id id :value i})) - (-assoc-extension [i extension] (instant {:extension extension :value i})) - (-value [instant] (.atOffset instant ZoneOffset/UTC)) - (-assoc-value [_ value] (instant value)) - (-has-primary-content [_] true) - (-serialize-json [instant generator] - (.writeString ^JsonGenerator generator (.format DateTimeFormatter/ISO_INSTANT instant))) - (-has-secondary-content [_] false) - (-serialize-json-secondary [_ generator] - (.writeNull ^JsonGenerator generator)) - (-to-xml [instant] - (xml-node/element nil {:value (str instant)})) - (-hash-into [instant sink] - (doto ^PrimitiveSink sink - (.putByte (byte 9)) ; :fhir/instant - (.putByte (byte 2))) ; :value - (system/-hash-into (value instant) sink)) - (-references [_])) - -(defn- at-utc [instant] - (.atOffset ^Instant instant ZoneOffset/UTC)) - -(defextended ExtendedInstant [id extension ^Instant value] - :fhir-type :fhir/instant :hash-num 9 :value-form (some-> value at-utc)) - -(defn- parse-instant-value [value] - (try - (cond - (str/ends-with? value "Z") (Instant/parse value) - (str/ends-with? value "+00:00") (Instant/parse (str (subs value 0 (- (count value) 6)) "Z")) - :else (OffsetDateTime/parse value)) - (catch DateTimeException _ - ::s2/invalid))) - -(def ^{:arglists '([x])} instant - (let [intern (intern/intern-value map->ExtendedInstant)] - (fn [x] - (cond - (map? x) - (let [{:keys [id extension value]} x - value (cond-> value (string? value) parse-instant-value)] - (cond - (and (nil? value) (p/-interned extension) (nil? id)) - (intern {:extension extension}) - - (and (nil? extension) (nil? id)) - (if (instance? OffsetDateTime value) - (OffsetInstant. value) - value) - - :else - (if (instance? OffsetDateTime value) - (ExtendedOffsetInstant. id extension value) - (ExtendedInstant. id extension value)))) - - (instance? OffsetDateTime x) - (if (= ZoneOffset/UTC (.getOffset ^OffsetDateTime x)) - (.toInstant ^OffsetDateTime x) - (OffsetInstant. x)) - - :else - (let [value (parse-instant-value x)] - (if (instance? OffsetDateTime value) - (OffsetInstant. value) - value)))))) - -(defn xml->Instant - {:arglists '([element])} - [{{:keys [id value]} :attrs content :content}] - (let [extension (seq content)] - (if (or id extension) - (instant {:id id :extension extension :value value}) - (instant value)))) +;; ---- instant --------------------------------------------------------------- (defn instant? [x] - (identical? :fhir/instant (type x))) + (instance? Instant x)) -;; -- date -------------------------------------------------------------------- +(defn instant [x] + (if (map? x) + (Instant/create ^IPersistentMap x) + (Instant/create ^OffsetDateTime x))) -(declare date) -(declare create-date) -(declare map->ExtendedDate) - -(deftype DateYear [^int year] - p/FhirType - (-type [_] :fhir/date) - (-interned [_] false) - (-assoc-id [d id] (date {:id id :value (p/-value d)})) - (-assoc-extension [d extension] - (date {:extension extension :value (p/-value d)})) - (-value [_] (system/date year)) - (-assoc-value [_ value] (create-date value)) - (-has-primary-content [_] true) - (-serialize-json [date generator] - (.writeString ^JsonGenerator generator (str (p/-value date)))) - (-has-secondary-content [_] false) - (-serialize-json-secondary [_ generator] - (.writeNull ^JsonGenerator generator)) - (-to-xml [date] - (xml-node/element nil {:value (str (p/-value date))})) - (-hash-into [date sink] - (doto ^PrimitiveSink sink - (.putByte (byte 10)) ; :fhir/date - (.putByte (byte 2))) ; :value - (system/-hash-into (p/-value date) sink)) - (-references [_]) - ILookup - (valAt [date key] - (.valAt date key nil)) - (valAt [date key not-found] - (if (identical? :value key) - (p/-value date) - not-found)) - Object - (equals [date x] - (or (identical? date x) - (and (instance? DateYear x) - (= year (.year ^DateYear x))))) - (hashCode [_] - year) - (toString [date] - (str (p/-value date)))) - -(defmethod print-method DateYear [^DateYear date ^Writer w] - (.write w "#fhir/date\"") - (.write w (str date)) - (.write w "\"")) - -(deftype DateYearMonth [^int year ^int month] - p/FhirType - (-type [_] :fhir/date) - (-interned [_] false) - (-assoc-id [d id] (date {:id id :value (p/-value d)})) - (-assoc-extension [d extension] - (date {:extension extension :value (p/-value d)})) - (-value [_] (system/date year month)) - (-assoc-value [_ value] (create-date value)) - (-has-primary-content [_] true) - (-serialize-json [date generator] - (.writeString ^JsonGenerator generator (str (p/-value date)))) - (-has-secondary-content [_] false) - (-serialize-json-secondary [_ generator] - (.writeNull ^JsonGenerator generator)) - (-to-xml [date] - (xml-node/element nil {:value (str (p/-value date))})) - (-hash-into [date sink] - (doto ^PrimitiveSink sink - (.putByte (byte 10)) ; :fhir/date - (.putByte (byte 2))) ; :value - (system/-hash-into (p/-value date) sink)) - (-references [_]) - ILookup - (valAt [date key] - (.valAt date key nil)) - (valAt [date key not-found] - (if (identical? :value key) - (p/-value date) - not-found)) - Object - (equals [date x] - (or (identical? date x) - (and (instance? DateYearMonth x) - (.equals ^Object (p/-value date) (p/-value x))))) - (hashCode [date] - (.hashCode ^Object (p/-value date))) - (toString [date] - (str (p/-value date)))) - -(defmethod print-method DateYearMonth [^DateYearMonth date ^Writer w] - (.write w "#fhir/date\"") - (.write w (str date)) - (.write w "\"")) - -(deftype DateDate [^int year ^int month ^int day] - p/FhirType - (-type [_] :fhir/date) - (-interned [_] false) - (-assoc-id [d id] (date {:id id :value (p/-value d)})) - (-assoc-extension [d extension] - (date {:extension extension :value (p/-value d)})) - (-value [_] (system/date year month day)) - (-assoc-value [_ value] (create-date value)) - (-has-primary-content [_] true) - (-serialize-json [date generator] - (.writeString ^JsonGenerator generator (str (p/-value date)))) - (-has-secondary-content [_] false) - (-serialize-json-secondary [_ generator] - (.writeNull ^JsonGenerator generator)) - (-to-xml [date] - (xml-node/element nil {:value (str (p/-value date))})) - (-hash-into [date sink] - (doto ^PrimitiveSink sink - (.putByte (byte 10)) ; :fhir/date - (.putByte (byte 2))) ; :value - (system/-hash-into (p/-value date) sink)) - (-references [_]) - ILookup - (valAt [date key] - (.valAt date key nil)) - (valAt [date key not-found] - (if (identical? :value key) - (p/-value date) - not-found)) - Object - (equals [date x] - (or (identical? date x) - (and (instance? DateDate x) - (.equals ^Object (p/-value date) (p/-value x))))) - (hashCode [date] - (.hashCode ^Object (p/-value date))) - (toString [date] - (str (p/-value date)))) - -(defmethod print-method DateDate [^DateDate date ^Writer w] - (.write w "#fhir/date\"") - (.write w (str date)) - (.write w "\"")) - -(defextended ExtendedDate [id extension value] - :fhir-type :fhir/date :hash-num 10) - -(defn create-date [system-date] - (condp = (class system-date) - blaze.fhir.spec.type.system.DateYear - (DateYear. - (.year ^blaze.fhir.spec.type.system.DateYear system-date)) - blaze.fhir.spec.type.system.DateYearMonth - (DateYearMonth. - (.year ^blaze.fhir.spec.type.system.DateYearMonth system-date) - (.month ^blaze.fhir.spec.type.system.DateYearMonth system-date)) - blaze.fhir.spec.type.system.DateDate - (DateDate. - (.year ^blaze.fhir.spec.type.system.DateDate system-date) - (.month ^blaze.fhir.spec.type.system.DateDate system-date) - (.day ^blaze.fhir.spec.type.system.DateDate system-date)))) - -(defn- parse-date [s] - (try - (create-date (Date/parse s)) - (catch DateTimeException _ - ::s2/invalid))) - -(def ^{:arglists '([x])} date - (let [intern (intern/intern-value map->ExtendedDate)] - (fn [x] - (cond - (map? x) - (let [{:keys [id extension value]} x - value (cond-> value (string? value) system/parse-date)] - (cond - (ba/anomaly? value) - ::s2/invalid - - (and (nil? value) (p/-interned extension) (nil? id)) - (intern {:extension extension}) - - (and (nil? extension) (nil? id)) - (create-date value) - - :else - (ExtendedDate. id extension value))) - (system/date? x) (create-date x) - (string? x) (parse-date x) - :else ::s2/invalid)))) - -(defn xml->Date - "Creates a primitive date value from XML `element`." - {:arglists '([element])} - [{{:keys [id value]} :attrs content :content}] - (let [extension (seq content)] - (if (or id extension) - (if value - (if-ok [value (system/parse-date value)] - (date {:id id :extension extension :value value}) - (fn [_] ::s2/invalid)) - (date {:id id :extension extension})) - (date value)))) +(def-print-method-primitive instant) + +;; -- date -------------------------------------------------------------------- (defn date? [x] - (identical? :fhir/date (type x))) + (instance? Date x)) -;; -- dateTime ---------------------------------------------------------------- +(defn date [x] + (if (map? x) + (Date/create ^IPersistentMap x) + (Date/create ^blaze.fhir.spec.type.system.Date x))) -(declare dateTime) -(declare create-date-time) -(declare map->ExtendedDateTime) - -(deftype DateTimeYear [^int year] - p/FhirType - (-type [_] :fhir/dateTime) - (-interned [_] false) - (-assoc-id [d id] (dateTime {:id id :value (p/-value d)})) - (-assoc-extension [d extension] - (dateTime {:extension extension :value (p/-value d)})) - (-value [_] (system/date-time year)) - (-assoc-value [_ value] (create-date-time value)) - (-has-primary-content [_] true) - (-serialize-json [date-time generator] - (.writeString ^JsonGenerator generator (str (p/-value date-time)))) - (-has-secondary-content [_] false) - (-serialize-json-secondary [_ generator] - (.writeNull ^JsonGenerator generator)) - (-to-xml [date-time] - (xml-node/element nil {:value (str (p/-value date-time))})) - (-hash-into [date-time sink] - (doto ^PrimitiveSink sink - (.putByte (byte 11)) ; :fhir/dateTime - (.putByte (byte 2))) ; :value - (system/-hash-into (p/-value date-time) sink)) - (-references [_]) - ILookup - (valAt [date-time key] - (.valAt date-time key nil)) - (valAt [date-time key not-found] - (if (identical? :value key) - (p/-value date-time) - not-found)) - Object - (equals [date-time x] - (or (identical? date-time x) - (and (instance? DateTimeYear x) - (= year (.year ^DateTimeYear x))))) - (hashCode [_] - year) - (toString [date-time] - (str (p/-value date-time)))) - -(defmethod print-method DateTimeYear [^DateTimeYear date-time ^Writer w] - (.write w "#fhir/dateTime\"") - (.write w (str date-time)) - (.write w "\"")) - -(deftype DateTimeYearMonth [^int year ^int month] - p/FhirType - (-type [_] :fhir/dateTime) - (-interned [_] false) - (-assoc-id [d id] (dateTime {:id id :value (p/-value d)})) - (-assoc-extension [d extension] - (dateTime {:extension extension :value (p/-value d)})) - (-value [_] (system/date-time year month)) - (-assoc-value [_ value] (create-date-time value)) - (-has-primary-content [_] true) - (-serialize-json [date-time generator] - (.writeString ^JsonGenerator generator (str (p/-value date-time)))) - (-has-secondary-content [_] false) - (-serialize-json-secondary [_ generator] - (.writeNull ^JsonGenerator generator)) - (-to-xml [date-time] - (xml-node/element nil {:value (str (p/-value date-time))})) - (-hash-into [date-time sink] - (doto ^PrimitiveSink sink - (.putByte (byte 11)) ; :fhir/dateTime - (.putByte (byte 2))) ; :value - (system/-hash-into (p/-value date-time) sink)) - (-references [_]) - ILookup - (valAt [date-time key] - (.valAt date-time key nil)) - (valAt [date-time key not-found] - (if (identical? :value key) - (p/-value date-time) - not-found)) - Object - (equals [date-time x] - (or (identical? date-time x) - (and (instance? DateTimeYearMonth x) - (.equals ^Object (p/-value date-time) (p/-value x))))) - (hashCode [date-time] - (.hashCode ^Object (p/-value date-time))) - (toString [date] - (str (p/-value date)))) - -(defmethod print-method DateTimeYearMonth [^DateTimeYearMonth date-time ^Writer w] - (.write w "#fhir/dateTime\"") - (.write w (str date-time)) - (.write w "\"")) - -(deftype DateTimeDate [^int year ^int month ^int day] - p/FhirType - (-type [_] :fhir/dateTime) - (-interned [_] false) - (-assoc-id [d id] (dateTime {:id id :value (p/-value d)})) - (-assoc-extension [d extension] - (dateTime {:extension extension :value (p/-value d)})) - (-value [_] (system/date-time year month day)) - (-assoc-value [_ value] (create-date-time value)) - (-has-primary-content [_] true) - (-serialize-json [date-time generator] - (.writeString ^JsonGenerator generator (str (p/-value date-time)))) - (-has-secondary-content [_] false) - (-serialize-json-secondary [_ generator] - (.writeNull ^JsonGenerator generator)) - (-to-xml [date-time] - (xml-node/element nil {:value (str (p/-value date-time))})) - (-hash-into [date-time sink] - (doto ^PrimitiveSink sink - (.putByte (byte 11)) ; :fhir/dateTime - (.putByte (byte 2))) ; :value - (system/-hash-into (p/-value date-time) sink)) - (-references [_]) - ILookup - (valAt [date-time key] - (.valAt date-time key nil)) - (valAt [date-time key not-found] - (if (identical? :value key) - (p/-value date-time) - not-found)) - Object - (equals [date-time x] - (or (identical? date-time x) - (and (instance? DateTimeDate x) - (.equals ^Object (p/-value date-time) (p/-value x))))) - (hashCode [date-time] - (.hashCode ^Object (p/-value date-time))) - (toString [date-time] - (str (p/-value date-time)))) - -(defmethod print-method DateTimeDate [^DateTimeDate date-time ^Writer w] - (.write w "#fhir/dateTime\"") - (.write w (str date-time)) - (.write w "\"")) - -(extend-protocol p/FhirType - OffsetDateTime - (-type [_] :fhir/dateTime) - (-interned [_] false) - (-assoc-id [d id] (dateTime {:id id :value d})) - (-assoc-extension [d extension] - (dateTime {:extension extension :value d})) - (-value [date-time] date-time) - (-assoc-value [_ value] (create-date-time value)) - (-has-primary-content [_] true) - (-serialize-json [date-time generator] - (.writeString ^JsonGenerator generator (.format DateTimeFormatter/ISO_DATE_TIME date-time))) - (-has-secondary-content [_] false) - (-serialize-json-secondary [_ generator] - (.writeNull ^JsonGenerator generator)) - (-to-xml [date-time] - (system-to-xml date-time)) - (-hash-into [date-time sink] - (doto ^PrimitiveSink sink - (.putByte (byte 11)) ; :fhir/dateTime - (.putByte (byte 2))) ; :value - (system/-hash-into date-time sink)) - (-references [_]) - - LocalDateTime - (-type [_] :fhir/dateTime) - (-interned [_] false) - (-assoc-id [d id] (dateTime {:id id :value d})) - (-assoc-extension [d extension] - (dateTime {:extension extension :value d})) - (-value [date-time] date-time) - (-assoc-value [_ value] (create-date-time value)) - (-has-primary-content [_] true) - (-serialize-json [date-time generator] - (.writeString ^JsonGenerator generator (.format DateTimeFormatter/ISO_LOCAL_DATE_TIME date-time))) - (-has-secondary-content [_] false) - (-serialize-json-secondary [_ generator] - (.writeNull ^JsonGenerator generator)) - (-to-xml [date-time] - (system-to-xml date-time)) - (-hash-into [date-time sink] - (doto ^PrimitiveSink sink - (.putByte (byte 11)) ; :fhir/dateTime - (.putByte (byte 2))) ; :value - (system/-hash-into date-time sink)) - (-references [_])) - -(defextended ExtendedDateTime [id extension value] - :fhir-type :fhir/dateTime :hash-num 11) - -(defn create-date-time [system-date-time] - (condp = (class system-date-time) - blaze.fhir.spec.type.system.DateTimeYear - (DateTimeYear. - (.year ^blaze.fhir.spec.type.system.DateTimeYear system-date-time)) - blaze.fhir.spec.type.system.DateTimeYearMonth - (DateTimeYearMonth. - (.year ^blaze.fhir.spec.type.system.DateTimeYearMonth system-date-time) - (.month ^blaze.fhir.spec.type.system.DateTimeYearMonth system-date-time)) - blaze.fhir.spec.type.system.DateTimeDate - (DateTimeDate. - (.year ^blaze.fhir.spec.type.system.DateTimeDate system-date-time) - (.month ^blaze.fhir.spec.type.system.DateTimeDate system-date-time) - (.day ^blaze.fhir.spec.type.system.DateTimeDate system-date-time)) - system-date-time)) - -(defn- parse-date-time [value] - (try - (create-date-time (DateTime/parse value)) - (catch DateTimeException _ - ::s2/invalid))) - -(def ^{:arglists '([x])} dateTime - (let [intern (intern/intern-value map->ExtendedDateTime)] - (fn [x] - (cond - (map? x) - (let [{:keys [id extension value]} x - value (cond-> value (string? value) system/parse-date-time)] - (cond - (ba/anomaly? value) - ::s2/invalid - - (and (nil? value) (p/-interned extension) (nil? id)) - (intern {:extension extension}) - - (and (nil? extension) (nil? id)) - (create-date-time value) - - :else - (ExtendedDateTime. id extension value))) - (system/date-time? x) (create-date-time x) - (string? x) (parse-date-time x) - :else ::s2/invalid)))) - -(defn xml->DateTime - "Creates a primitive dateTime value from XML `element`." - {:arglists '([element])} - [{{:keys [id value]} :attrs content :content}] - (let [extension (seq content)] - (if (or id extension) - (if value - (if-ok [value (system/parse-date-time value)] - (dateTime {:id id :extension extension :value value}) - (fn [_] ::s2/invalid)) - (dateTime {:id id :extension extension})) - (dateTime value)))) +(def-print-method-primitive date) + +;; -- dateTime ---------------------------------------------------------------- (defn dateTime? [x] - (identical? :fhir/dateTime (type x))) + (instance? DateTime x)) -;; ---- time ------------------------------------------------------------------ +(defn dateTime [x] + (if (map? x) + (DateTime/create ^IPersistentMap x) + (DateTime/create ^blaze.fhir.spec.type.system.DateTime x))) -(declare time) - -(extend-protocol p/FhirType - LocalTime - (-type [_] :fhir/time) - (-interned [_] false) - (-assoc-id [t id] (time {:id id :value t})) - (-assoc-extension [t extension] - (time {:extension extension :value t})) - (-value [time] time) - (-assoc-value [_ value] value) - (-has-primary-content [_] true) - (-serialize-json [time generator] - (.writeString ^JsonGenerator generator ^String (system/-to-string time))) - (-has-secondary-content [_] false) - (-serialize-json-secondary [_ generator] - (.writeNull ^JsonGenerator generator)) - (-to-xml [time] - (system-to-xml time)) - (-hash-into [time sink] - (doto ^PrimitiveSink sink - (.putByte (byte 12)) ; :fhir/time - (.putByte (byte 2))) ; :value - (system/-hash-into time sink)) - (-references [_])) - -(defextended ExtendedTime [id extension value] - :fhir-type :fhir/time :hash-num 12) - -(defn- parse-time [s] - (try - (LocalTime/parse s) - (catch DateTimeException _ - ::s2/invalid))) - -(def ^{:arglists '([x])} time - (let [intern (intern/intern-value map->ExtendedTime)] - (fn [x] - (cond - (map? x) - (let [{:keys [id extension value]} x - value (cond-> value (string? value) system/parse-time)] - (cond - (ba/anomaly? value) - ::s2/invalid - - (and (nil? value) (p/-interned extension) (nil? id)) - (intern {:extension extension}) - - (and (nil? extension) (nil? id)) - value - - :else - (ExtendedTime. id extension value))) - (system/time? x) x - (string? x) (parse-time x) - :else ::s2/invalid)))) - -(defn xml->Time - "Creates a primitive time value from XML `element`." - {:arglists '([element])} - [{{:keys [id value]} :attrs content :content}] - (let [extension (seq content)] - (if (or id extension) - (time {:id id :extension extension :value value}) - (time value)))) +(def-print-method-primitive dateTime) + +;; ---- time ------------------------------------------------------------------ (defn time? [x] - (identical? :fhir/time (type x))) + (instance? Time x)) + +(defn time [x] + (if (map? x) + (Time/create ^IPersistentMap x) + (Time/create ^LocalTime x))) + +(def-print-method-primitive time) ;; ---- code ------------------------------------------------------------------ -(declare code?) -(declare code) -(declare create-code) -(declare map->ExtendedCode) -(declare xml->Code) +(defn code? [x] + (instance? Code x)) + +(defn code [x] + (if (clojure.core/string? x) + (Code/create ^String x) + (Code/create ^IPersistentMap x))) -(def-primitive-type Code [^String value] :hash-num 13 :interned true) +(def-print-method-primitive code) ;; ---- oid ------------------------------------------------------------------- -(declare oid?) -(declare oid) -(declare map->ExtendedOid) +(defn oid? [x] + (instance? Oid x)) -(def-primitive-type Oid [value] :hash-num 14) +(defn oid [x] + (if (map? x) + (Oid/create ^IPersistentMap x) + (Oid/create ^String x))) + +(def-print-method-primitive oid) ;; ---- id -------------------------------------------------------------------- -(declare id?) -(declare id) -(declare map->ExtendedId) +(defn id? [x] + (instance? Id x)) + +(defn id [x] + (if (map? x) + (Id/create ^IPersistentMap x) + (Id/create ^String x))) -(def-primitive-type Id [value] :hash-num 15) +(def-print-method-primitive id) ;; ---- markdown -------------------------------------------------------------- -(declare markdown?) -(declare markdown) -(declare map->ExtendedMarkdown) -(declare xml->Markdown) +(defn markdown? [x] + (instance? Markdown x)) + +(defn markdown [x] + (if (map? x) + (Markdown/create ^IPersistentMap x) + (Markdown/create ^String x))) -(def-primitive-type Markdown [value] :hash-num 16) +(def-print-method-primitive markdown) ;; ---- unsignedInt ----------------------------------------------------------- -(declare unsignedInt?) -(declare unsignedInt) -(declare map->ExtendedUnsignedInt) -(declare xml->UnsignedInt) +(defn unsignedInt? [x] + (instance? UnsignedInt x)) -(def-primitive-type UnsignedInt [^Integer value] :hash-num 17) +(defn unsignedInt [x] + (cond + (map? x) (ba/try-one IllegalArgumentException ::anom/incorrect + (UnsignedInt/create ^IPersistentMap x)) + (nat-int? x) (ba/try-one IllegalArgumentException ::anom/incorrect + (UnsignedInt/create ^Number x)) + :else (ba/incorrect (format "Invalid unsignedInt value `%s`." x)))) + +(def-print-method-primitive unsignedInt) ;; ---- positiveInt ----------------------------------------------------------- -(declare positiveInt?) -(declare positiveInt) -(declare map->ExtendedPositiveInt) -(declare xml->PositiveInt) +(defn positiveInt? [x] + (instance? PositiveInt x)) -(def-primitive-type PositiveInt [^Integer value] :hash-num 18) +(defn positiveInt [x] + (cond + (map? x) (ba/try-one IllegalArgumentException ::anom/incorrect + (PositiveInt/create ^IPersistentMap x)) + (pos-int? x) (ba/try-one IllegalArgumentException ::anom/incorrect + (PositiveInt/create ^Number x)) + :else (ba/incorrect (format "Invalid positiveInt value `%s`." x)))) -;; ---- uuid ------------------------------------------------------------------ +(def-print-method-primitive positiveInt) -(declare uuid) - -(extend-protocol p/FhirType - UUID - (-type [_] :fhir/uuid) - (-interned [_] false) - (-assoc-id [value id] (uuid {:id id :value value})) - (-assoc-extension [value extension] (uuid {:extension extension :value value})) - (-value [uuid] (str "urn:uuid:" uuid)) - (-assoc-value [_ value] (uuid value)) - (-has-primary-content [_] true) - (-serialize-json [uuid generator] - (.writeString ^JsonGenerator generator (str "urn:uuid:" uuid))) - (-has-secondary-content [_] false) - (-serialize-json-secondary [_ generator] - (.writeNull ^JsonGenerator generator)) - (-to-xml [uuid] - (xml-node/element nil {:value (str "urn:uuid:" uuid)})) - (-hash-into [uuid sink] - (doto ^PrimitiveSink sink - (.putByte (byte 19)) ; :fhir/uuid - (.putByte (byte 2)) ; :value - (.putLong (.getMostSignificantBits uuid)) - (.putLong (.getLeastSignificantBits uuid)))) - (-references [_])) - -(defextended ExtendedUuid [id extension ^UUID value] - :fhir-type :fhir/uuid :hash-num 19 :value-constructor uuid :value-form (str "urn:uuid:" value)) - -(def ^{:arglists '([x])} uuid - (create-fn (intern/intern-value map->ExtendedUuid) ->ExtendedUuid - #(if (clojure.core/uuid? %) % (parse-uuid (subs % 9))))) - -(defn xml->Uuid - {:arglists '([element])} - [{{:keys [id value]} :attrs content :content}] - (let [extension (seq content)] - (if (or id extension) - (uuid {:id id :extension extension :value value}) - (uuid value)))) +;; ---- uuid ------------------------------------------------------------------ (defn uuid? [x] - (identical? :fhir/uuid (type x))) + (instance? Uuid x)) + +(defn uuid [x] + (if (map? x) + (Uuid/create ^IPersistentMap x) + (Uuid/create ^String x))) + +(def-print-method-primitive uuid) ;; ---- xhtml ----------------------------------------------------------------- +(defn xhtml? [x] + (instance? Xhtml x)) + +(defn xhtml [x] + (if (map? x) + (Xhtml/create ^IPersistentMap x) + (Xhtml/create ^String x))) + +(def-print-method-primitive xhtml) + +(def ^:const xml-preamble-length + (clojure.core/count "")) + +(defn xml->Xhtml + "Creates a xhtml from XML `element`." + [element] + (Xhtml/create (subs (xml/emit-str element) xml-preamble-length))) + (defn- wrap-div [s] (str "

" s "
")) @@ -1210,234 +367,268 @@ (wrap-div) (parse-xhtml*))))) -(declare ->Xhtml) - -(deftype Xhtml [value] - p/FhirType - (-type [_] :fhir/xhtml) - (-interned [_] false) - (-value [_] value) - (-assoc-value [_ val] (->Xhtml val)) - (-has-primary-content [_] true) - (-serialize-json [_ generator] - (.writeString ^JsonGenerator generator ^String value)) - (-has-secondary-content [_] false) - (-serialize-json-secondary [_ generator] - (.writeNull ^JsonGenerator generator)) - (-to-xml [_] (update (parse-xhtml value) :attrs assoc :xmlns "http://www.w3.org/1999/xhtml")) - (-hash-into [_ sink] - (doto ^PrimitiveSink sink - (.putByte (byte 20)) ; :fhir/xhtml - (.putByte (byte 2))) ; :value - (system/-hash-into value sink)) - (-references [_]) - Object - (equals [_ x] - (and (instance? Xhtml x) (= value (.value ^Xhtml x)))) - (hashCode [_] - (.hashCode value)) - (toString [_] - (str value))) - -(defmethod print-method Xhtml [xhtml ^Writer w] - (.write w "#fhir/xhtml\"") - (.write w ^String (value xhtml)) - (.write w "\"")) +(defn xhtml-to-xml [{:keys [value]}] + (update (parse-xhtml value) :attrs assoc :xmlns "http://www.w3.org/1999/xhtml")) -(def ^:const xml-preamble-length - (count "")) +;; ---- Complex Types -------------------------------------------------------- -(defn xml->Xhtml - "Creates a xhtml from XML `element`." - [element] - (->Xhtml (subs (xml/emit-str element) xml-preamble-length))) +(defmacro def-print-method-complex [name & [tag-name]] + `(defmethod print-method ~(symbol name) + [~(with-meta 'v {:tag (symbol name)}) ~(with-meta 'w {:tag 'Writer})] + (print-type ~name ~tag-name))) -(defn xhtml? [x] - (instance? Xhtml x)) +;; ---- Address --------------------------------------------------------------- -;; ---- Complex Types -------------------------------------------------------- +(defn address [x] + (Address/create x)) + +(def-print-method-complex "Address") + +;; ---- Age --------------------------------------------------------------- + +(defn age [x] + (Age/create x)) + +(def-print-method-complex "Age") + +;; ---- Annotation ------------------------------------------------------------ + +(defn annotation [x] + (Annotation/create x)) + +(def-print-method-complex "Annotation") + +;; ---- Attachment ------------------------------------------------------------ + +(defn attachment [x] + (Attachment/create x)) + +(def-print-method-complex "Attachment") + +;; ---- CodeableConcept ------------------------------------------------------- + +(defn codeable-concept [x] + (CodeableConcept/create x)) + +(def-print-method-complex "CodeableConcept") + +;; ---- Coding ---------------------------------------------------------------- + +(defn coding [x] + (Coding/create x)) + +(def-print-method-complex "Coding") + +;; ---- ContactDetail ------------------------------------------------------------ + +(defn contact-detail [x] + (ContactDetail/create x)) + +(def-print-method-complex "ContactDetail") + +;; ---- ContactPoint ------------------------------------------------------------ + +(defn contact-point [x] + (ContactPoint/create x)) + +(def-print-method-complex "ContactPoint") + +;; ---- Contributor -------------------------------------------------------- + +(defn contributor [x] + (Contributor/create x)) + +(def-print-method-complex "Contributor") + +;; ---- Count -------------------------------------------------------- + +(defn count [x] + (Count/create x)) + +(def-print-method-complex "Count") + +;; ---- DataRequirement ------------------------------------------------------- + +(defn data-requirement [x] + (DataRequirement/create x)) + +(def-print-method-complex "DataRequirement") + +(defn data-requirement-code-filter [x] + (DataRequirement$CodeFilter/create x)) + +(def-print-method-complex "DataRequirement$CodeFilter" "fhir.DataRequirement/codeFilter") + +(defn data-requirement-date-filter [x] + (DataRequirement$DateFilter/create x)) + +(def-print-method-complex "DataRequirement$DateFilter" "fhir.DataRequirement/dateFilter") + +(defn data-requirement-sort [x] + (DataRequirement$Sort/create x)) + +(def-print-method-complex "DataRequirement$Sort" "fhir.DataRequirement/sort") + +;; ---- Distance -------------------------------------------------------- + +(defn distance [x] + (Distance/create x)) + +(def-print-method-complex "Distance") + +;; ---- Dosage ---------------------------------------------------------------- + +(defn dosage [x] + (Dosage/create x)) + +(def-print-method-complex "Dosage") + +(defn dosage-dose-and-rate [x] + (Dosage$DoseAndRate/create x)) + +(def-print-method-complex "Dosage$DoseAndRate" "fhir.Dosage/doseAndRate") + +;; ---- Duration -------------------------------------------------------- + +(defn duration [x] + (Duration/create x)) + +(def-print-method-complex "Duration") + +;; ---- Expression -------------------------------------------------------- + +(defn expression [x] + (Expression/create x)) + +(def-print-method-complex "Expression") + +;; ---- Extension -------------------------------------------------------- + +(defn extension [x] + (Extension/create x)) + +(def-print-method-complex "Extension") + +;; ---- HumanName ------------------------------------------------------------- + +(defn human-name [x] + (HumanName/create x)) + +(def-print-method-complex "HumanName") + +;; ---- Identifier ------------------------------------------------------------ + +(defn identifier [x] + (Identifier/create x)) + +(def-print-method-complex "Identifier") + +;; ---- Meta ------------------------------------------------------------------ + +(defn meta [x] + (Meta/create x)) + +(def-print-method-complex "Meta") + +;; ---- Money ------------------------------------------------------------------ + +(defn money [x] + (Money/create x)) + +(def-print-method-complex "Money") + +;; ---- ParameterDefinition -------------------------------------------------------- + +(defn parameter-definition [x] + (ParameterDefinition/create x)) + +(def-print-method-complex "ParameterDefinition") + +;; ---- Period ---------------------------------------------------------------- + +(defn period [x] + (Period/create x)) + +(def-print-method-complex "Period") + +;; ---- Quantity -------------------------------------------------------------- + +(defn quantity [x] + (Quantity/create x)) + +(def-print-method-complex "Quantity") + +;; ---- Range ----------------------------------------------------------------- + +(defn range [x] + (Range/create x)) + +(def-print-method-complex "Range") + +;; ---- Ratio ----------------------------------------------------------------- + +(defn ratio [x] + (Ratio/create x)) + +(def-print-method-complex "Ratio") + +;; ---- Reference -------------------------------------------------------- + +(defn reference [x] + (Reference/create x)) + +(def-print-method-complex "Reference") + +;; ---- RelatedArtifact ------------------------------------------------------------ + +(defn related-artifact [x] + (RelatedArtifact/create x)) + +(def-print-method-complex "RelatedArtifact") + +;; ---- SampledData ------------------------------------------------------------ + +(defn sampled-data [x] + (SampledData/create x)) + +(def-print-method-complex "SampledData") + +;; ---- Signature ------------------------------------------------------------ + +(defn signature [x] + (Signature/create x)) + +(def-print-method-complex "Signature") + +;; ---- Timing ------------------------------------------------------------ + +(defn timing [x] + (Timing/create x)) + +(def-print-method-complex "Timing") + +(defn timing-repeat [x] + (Timing$Repeat/create x)) + +(def-print-method-complex "Timing$Repeat" "fhir.Timing/repeat") + +;; ---- TriggerDefinition ------------------------------------------------------------ + +(defn trigger-definition [x] + (TriggerDefinition/create x)) + +(def-print-method-complex "TriggerDefinition") + +;; ---- UsageContext ------------------------------------------------------------ + +(defn usage-context [x] + (UsageContext/create x)) + +(def-print-method-complex "UsageContext") + +;; ---- BundleEntrySearch ----------------------------------------------------- + +(defn bundle-entry-search [x] + (BundleEntrySearch/create x)) -(extend-protocol p/FhirType - List - (-type [_]) - (-interned [xs] - (reduce #(if (p/-interned %2) %1 (reduced false)) true xs)) - (-value [_]) - (-has-primary-content [xs] - (reduce #(when (p/-has-primary-content %2) (reduced true)) nil xs)) - (-serialize-json [xs generator] - (.writeStartArray ^JsonGenerator generator) - (run! #(p/-serialize-json % generator) xs) - (.writeEndArray ^JsonGenerator generator)) - (-has-secondary-content [xs] - (reduce #(when (p/-has-secondary-content %2) (reduced true)) nil xs)) - (-serialize-json-secondary [xs generator] - (.writeStartArray ^JsonGenerator generator) - (reduce #(p/-serialize-json-secondary %2 generator) nil xs) - (.writeEndArray ^JsonGenerator generator)) - (-hash-into [xs sink] - (.putByte ^PrimitiveSink sink (byte 36)) - (reduce #(p/-hash-into %2 sink) nil xs)) - (-references [xs] - (reduce #(into %1 (p/-references %2)) [] xs)) - Keyword - (-type [_]) - (-value [_]) - (-hash-into [k sink] - (.putInt ^PrimitiveSink sink (.hasheq k))) - (-references [_]) - IPersistentMap - (-type [m] - (.valAt m :fhir/type)) - (-interned [_] false) - (-assoc-id [m id] (assoc m :id id)) - (-assoc-extension [m extension] (assoc m :extension extension)) - (-value [_]) - (-assoc-value [m value] (assoc m :value value)) - (-has-primary-content [_] true) - (-serialize-json [m generator] - (.writeStartObject ^JsonGenerator generator) - (run! - (fn [^Map$Entry e] - (let [^Keyword key (.getKey e)] - (when-not (identical? :fhir/type key) - (when-some [v (.getValue e)] - (json/write-field generator (json/field-name (.getName key)) v))))) - m) - (.writeEndObject ^JsonGenerator generator)) - (-has-secondary-content [_] false) - (-serialize-json-secondary [m _] - (throw (ex-info "A complex type/resource has no secondary content." m))) - (-hash-into [m sink] - (.putByte ^PrimitiveSink sink (byte 37)) - (run! - (fn [^Map$Entry e] - (p/-hash-into (.getKey e) sink) - (p/-hash-into (.getValue e) sink)) - (sort - (reify Comparator - (compare [_ e1 e2] - (.compareTo ^Keyword (.getKey ^Map$Entry e1) (.getKey ^Map$Entry e2)))) - m))) - (-references [m] - ;; Bundle entries have no references, because Bundles itself are stored "as-is" - (when-not (identical? :fhir.Bundle/entry (p/-type m)) - (transduce (mapcat p/-references) conj [] (vals m))))) - -(declare attachment) - -(def-complex-type Attachment - [^String id extension ^:primitive contentType ^:primitive language - ^:primitive ^:primitive data ^:primitive url ^:primitive size - ^:primitive hash ^:primitive title ^:primitive creation] - :hash-num 46) - -(declare extension) - -(def-complex-type Extension - [^String id extension ^String url ^:polymorph ^:primitive - ^{:types [base64Binary boolean canonical code date dateTime decimal id - instant integer markdown oid positiveInt string time unsignedInt - uri url uuid Address Age Annotation Attachment CodeableConcept - Coding ContactPoint Count Distance Duration HumanName Identifier - Money Period Quantity Range Ratio Reference SampledData Signature - Timing ContactDetail Contributor DataRequirement Expression - ParameterDefinition RelatedArtifact TriggerDefinition UsageContext - Dosage Meta]} value] - :hash-num 39 - :interned (and (nil? id) (p/-interned extension) (p/-interned value))) - -(declare coding) - -(def-complex-type Coding - [^String id extension ^:primitive system ^:primitive-string version - ^:primitive code ^:primitive-string display ^:primitive userSelected] - :hash-num 38 - :interned (and (nil? id) (p/-interned extension))) - -(declare codeable-concept) - -(def-complex-type CodeableConcept - [^String id extension coding ^:primitive-string text] - :hash-num 39 - :interned (and (nil? id) (p/-interned extension))) - -(declare quantity) - -(def-complex-type Quantity - [^String id extension ^:primitive value ^:primitive comparator - ^:primitive-string unit ^:primitive system ^:primitive code] - :hash-num 40 - :interned (and (nil? id) (p/-interned extension) (nil? value))) - -(declare ratio) - -(def-complex-type Ratio [^String id extension numerator denominator] - :hash-num 48) - -(declare period) - -(def-complex-type Period [^String id extension ^:primitive start ^:primitive end] - :hash-num 41) - -(declare identifier) - -(def-complex-type Identifier - [^String id extension ^:primitive use type ^:primitive system - ^:primitive-string value period assigner] - :hash-num 42) - -(declare human-name) - -(def-complex-type HumanName - [^String id extension ^:primitive use ^:primitive-string text - ^:primitive-string family ^:primitive-list given ^:primitive-list prefix - ^:primitive-list suffix period] - :hash-num 46) - -(declare address) - -(def-complex-type Address - [^String id extension ^:primitive use ^:primitive type ^:primitive-string text - ^:primitive-list line ^:primitive-string city ^:primitive-string district - ^:primitive-string state ^:primitive-string postalCode - ^:primitive-string country period] - :hash-num 47) - -(defn- valid-ref? [[type id]] - (and (.matches (re-matcher #"[A-Z]([A-Za-z0-9_]){0,254}" type)) - (some->> id (re-matcher #"[A-Za-z0-9\-\.]{1,64}") .matches))) - -(defn- reference-reference [ref] - (let [ref (str/split ref #"/" 2)] - (when (valid-ref? ref) - [ref]))) - -(declare reference) - -(def-complex-type Reference - [^String id extension ^:primitive-string reference ^:primitive type identifier - ^:primitive-string display] - :hash-num 43 - :references - (-> (transient (or (some-> reference value reference-reference) [])) - (macros/into! (p/-references extension)) - (macros/into! (p/-references type)) - (macros/into! (p/-references identifier)) - (macros/into! (p/-references display)) - (persistent!))) - -(declare meta) - -(def-complex-type Meta - [^String id extension ^:primitive versionId ^:primitive lastUpdated - ^:primitive source ^:primitive-list profile security tag] - :hash-num 44) - -(declare bundle-entry-search) - -(def-complex-type BundleEntrySearch - [^String id extension ^:primitive mode ^:primitive score] - :fhir-type :fhir.Bundle.entry/search - :hash-num 45 - :interned (and (nil? id) (p/-interned extension) (nil? score))) +(def-print-method-complex "BundleEntrySearch" "fhir.Bundle.entry/search") diff --git a/modules/fhir-structure/src/blaze/fhir/spec/type/json.clj b/modules/fhir-structure/src/blaze/fhir/spec/type/json.clj deleted file mode 100644 index 2de0b0ae1..000000000 --- a/modules/fhir-structure/src/blaze/fhir/spec/type/json.clj +++ /dev/null @@ -1,76 +0,0 @@ -(ns blaze.fhir.spec.type.json - (:refer-clojure :exclude [str]) - (:require - [blaze.fhir.spec.type.protocols :as p] - [blaze.util :refer [str]]) - (:import - [com.fasterxml.jackson.core JsonGenerator SerializableString] - [java.io Writer] - [java.nio.charset StandardCharsets] - [java.util Arrays])) - -(set! *warn-on-reflection* true) -(set! *unchecked-math* :warn-on-boxed) - -(deftype FieldName [^String s ^bytes utf-8-bytes] - SerializableString - (getValue [_] - s) - (charLength [_] - (.length s)) - (appendQuotedUTF8 [_ buffer offset] - (let [num-bytes (alength utf-8-bytes)] - (if (> (unchecked-add-int offset num-bytes) (alength buffer)) - (int -1) - (do (System/arraycopy utf-8-bytes 0 buffer offset num-bytes) - num-bytes)))) - (appendQuoted [_ buffer offset] - (let [length (.length s)] - (if (> (unchecked-add-int offset length) (alength buffer)) - (int -1) - (do (.getChars s 0 length buffer offset) - length)))) - (asUnquotedUTF8 [_] - (Arrays/copyOf ^bytes utf-8-bytes (alength utf-8-bytes))) - (asQuotedUTF8 [this] - (.asUnquotedUTF8 this)) - Object - (toString [_] - s)) - -(defmethod print-method FieldName [^FieldName fieldName ^Writer w] - (.write w "#blaze/field-name") - (print-dup (.-s fieldName) w)) - -(defn field-name ^SerializableString [s] - (->FieldName s (.getBytes ^String s StandardCharsets/UTF_8))) - -(defn write-field [^JsonGenerator generator ^SerializableString field-name value] - (when (p/-has-primary-content value) - (.writeFieldName generator field-name) - (p/-serialize-json value generator)) - (when (p/-has-secondary-content value) - (.writeFieldName generator (str "_" (.getValue field-name))) - (p/-serialize-json-secondary value generator))) - -(defn write-primitive-list-field [^JsonGenerator generator ^SerializableString field-name list] - (when (some p/-has-primary-content list) - (.writeFieldName generator field-name) - (.writeStartArray generator) - (run! #(p/-serialize-json % generator) list) - (.writeEndArray generator)) - (when (some p/-has-secondary-content list) - (.writeFieldName generator (str "_" (.getValue field-name))) - (.writeStartArray generator) - (run! #(p/-serialize-json-secondary % generator) list) - (.writeEndArray generator))) - -(defn write-primitive-string-field [^JsonGenerator generator ^SerializableString field-name value] - (if (string? value) - (do (.writeFieldName generator field-name) - (.writeString generator ^String value)) - (write-field generator field-name value))) - -(defn write-non-primitive-field [^JsonGenerator generator ^SerializableString field-name value] - (.writeFieldName generator field-name) - (p/-serialize-json value generator)) diff --git a/modules/fhir-structure/src/blaze/fhir/spec/type/macros.clj b/modules/fhir-structure/src/blaze/fhir/spec/type/macros.clj deleted file mode 100644 index d1aab6f66..000000000 --- a/modules/fhir-structure/src/blaze/fhir/spec/type/macros.clj +++ /dev/null @@ -1,404 +0,0 @@ -(ns blaze.fhir.spec.type.macros - (:refer-clojure :exclude [str]) - (:require - [blaze.fhir.spec.impl.intern :as intern] - [blaze.fhir.spec.type.json :as json] - [blaze.fhir.spec.type.protocols :as p] - [blaze.fhir.spec.type.string-util :as su] - [blaze.fhir.spec.type.system :as system] - [blaze.fhir.spec.xml :as spec-xml] - [blaze.util :refer [str]] - [clojure.data.xml.node :as xml-node] - [clojure.string :as str]) - (:import - [clojure.lang ILookup] - [com.fasterxml.jackson.core JsonGenerator SerializableString] - [com.fasterxml.jackson.core.io JsonStringEncoder] - [com.google.common.hash PrimitiveSink] - [java.io Writer] - [java.nio.charset StandardCharsets] - [java.util Arrays])) - -(set! *warn-on-reflection* true) - -(defn into! [to from] - (reduce conj! to from)) - -(defn- write-start-object [gen] - `(.writeStartObject ~(with-meta gen {:tag `JsonGenerator}))) - -(defn- write-end-object [gen] - `(.writeEndObject ~(with-meta gen {:tag `JsonGenerator}))) - -(defn- write-string [gen s] - `(.writeString ~(with-meta gen {:tag `JsonGenerator}) ~(with-meta s {:tag `String}))) - -(defn- write-value [gen value] - (cond - (#{'Boolean 'boolean} (:tag (meta value))) - `(.writeBoolean ~(with-meta gen {:tag `JsonGenerator}) ~value) - - (#{'Integer 'int 'BigDecimal} (:tag (meta value))) - `(.writeNumber ~(with-meta gen {:tag `JsonGenerator}) ~value) - - (#{'String} (:tag (meta value))) - `(.writeString ~(with-meta gen {:tag `JsonGenerator}) ~value) - - :else - (write-string gen `(system/-to-string ~value)))) - -(defn- write-null [gen] - `(.writeNull ~(with-meta gen {:tag `JsonGenerator}))) - -(defn- xml-value [value] - (if (= 'UUID (:tag (meta value))) - `(str "urn:uuid:" ~value) - `(spec-xml/replace-invalid-chars (system/-to-string ~value)))) - -(defn- lower-case-first [name] - (str (str/lower-case (subs (str name) 0 1)) (subs (str name) 1))) - -(defn- fhir-type-kw [name] - (keyword "fhir" (lower-case-first name))) - -(def ^:private tagged-sink - (with-meta 'sink {:tag `PrimitiveSink})) - -(def ^:private tagged-writer - (with-meta 'w {:tag `Writer})) - -(defn- tagged-x [name] - (with-meta 'x {:tag name})) - -(def ^:private id-tag 0) -(def ^:private extension-tag 1) -(def ^:private value-tag 2) - -(defn- parse-value [value form] - (if (#{'Integer 'int} (:tag (meta value))) - `(Integer/parseInt ~form) - form)) - -(defn- primitive-tag [value] - (if (= 'Integer (:tag (meta value))) - (with-meta value {:tag 'int}) - value)) - -(defn- gen-equals-sym [value-sym] - (if (= 'int (:tag (meta value-sym))) - '= - '.equals)) - -(defn- gen-hash-code [value-sym] - (if (= 'int (:tag (meta value-sym))) - value-sym - `(.hashCode ~value-sym))) - -(defn- gen-serializable-string [value] - (let [unquoted-utf-8-bytes (with-meta 'unquoted-utf-8-bytes {:tag 'bytes}) - quoted-utf-8-bytes (with-meta 'quoted-utf-8-bytes {:tag 'bytes})] - `[SerializableString - (~'getValue [~'_] - ~value) - (~'appendQuotedUTF8 [~'_ ~'buffer ~'offset] - (let [num-bytes# (alength ~quoted-utf-8-bytes)] - (if (> (unchecked-add-int ~'offset num-bytes#) (alength ~'buffer)) - (int -1) - (do (System/arraycopy ~quoted-utf-8-bytes 0 ~'buffer ~'offset num-bytes#) - num-bytes#)))) - (~'appendQuoted [~'_ ~'buffer ~'offset] - (let [length# (.length ~value)] - (if (> (unchecked-add-int ~'offset length#) (alength ~'buffer)) - (int -1) - (do (.getChars ~value 0 length# ~'buffer ~'offset) - length#)))) - (~'asUnquotedUTF8 [~'_] - (Arrays/copyOf ~unquoted-utf-8-bytes (alength ~unquoted-utf-8-bytes))) - (~'asQuotedUTF8 [~'_] - (Arrays/copyOf ~quoted-utf-8-bytes (alength ~quoted-utf-8-bytes)))])) - -(defn- gen-type - [name value {:keys [fhir-type hash-num interned] - :or {fhir-type (fhir-type-kw name) interned false}}] - `(do - (deftype - ~name - ~(cond-> [value] - (and interned (#{'String} (:tag (meta value)))) - (conj 'unquoted-utf-8-bytes 'quoted-utf-8-bytes)) - p/FhirType - (~'-type [~'_] ~fhir-type) - (~'-interned [~'_] ~interned) - (~'-value [~'_] ~value) - (~'-assoc-id [~'_ id#] - (~(symbol (lower-case-first name)) {:id id# :value ~'value})) - (~'-assoc-extension [~'_ ~'extension] - (~(symbol (lower-case-first name)) {:extension ~'extension :value ~'value})) - (~'-assoc-value [~'_ ~'val] - (~(if (and interned (#{'String} (:tag (meta value)))) - (symbol (str "create-" (lower-case-first name))) - (symbol (str name "."))) - ~'val)) - (~'-has-primary-content [~'_] true) - ~(if (and interned (#{'String} (:tag (meta value)))) - `(~'-serialize-json [~'this ~'generator] - (.writeString ~(with-meta 'generator {:tag `JsonGenerator}) ~'this)) - `(~'-serialize-json [~'_ ~'generator] - ~(write-value 'generator value))) - (~'-has-secondary-content [~'_] false) - (~'-serialize-json-secondary [~'_ ~'generator] - ~(write-null 'generator)) - (~'-to-xml [~'_] (xml-node/element nil {:value (spec-xml/replace-invalid-chars (system/-to-string ~value))})) - (~'-hash-into [~'_ ~'sink] - (.putByte ~tagged-sink (byte ~hash-num)) - (.putByte ~tagged-sink (byte ~value-tag)) - (system/-hash-into ~value ~'sink)) - (~'-references [~'_]) - ~@(when (and interned (#{'String} (:tag (meta value)))) - (gen-serializable-string value)) - ILookup - (~'valAt [~'_ ~'key] - (when (identical? :value ~'key) - ~value)) - (~'valAt [~'_ ~'key ~'not-found] - (if (identical? :value ~'key) - ~value - ~'not-found)) - Object - (~'equals [~'this ~'x] - (or (identical? ~'this ~'x) - (and (instance? ~name ~'x) (~(gen-equals-sym value) ~value (.-value ~(tagged-x name)))))) - (~'hashCode [~'_] - ~(gen-hash-code value)) - (~'toString [~'_] - (system/-to-string ~value))) - - (defmethod print-method ~name [~(tagged-x name) ~tagged-writer] - (.write ~'w ~(str "#fhir/" (lower-case-first name))) - ~(when (= 'int (:tag (meta value))) `(.write ~'w " ")) - (print-method (.-value ~'x) ~'w)))) - -(defn write-extended-attributes [^JsonGenerator generator id extension] - (.writeStartObject generator) - (when id - (.writeFieldName generator "id") - (p/-serialize-json id generator)) - (when extension - (.writeFieldName generator "extension") - (p/-serialize-json extension generator)) - (.writeEndObject generator)) - -(defn- gen-extended-record - [name value-sym {:keys [fhir-type hash-num interned value-constructor - value-form] - :or {interned false}}] - `(do - (defrecord ~name [~'id ~'extension ~value-sym] - p/FhirType - (~'-type [~'_] ~fhir-type) - (~'-interned [~'_] - ~(if interned - `(and (p/-interned ~'extension) (nil? ~'id)) - `(and (nil? ~value-sym) (p/-interned ~'extension) (nil? ~'id)))) - (~'-value [~'_] ~(or value-form value-sym)) - (~'-assoc-id [~'this ~'id] - (assoc ~'this :id ~'id)) - (~'-assoc-extension [~'this ~'extension] - (assoc ~'this :extension ~'extension)) - (~'-assoc-value [~'this ~'val] - (assoc ~'this :value ~(if value-constructor `(~value-constructor ~'val) 'val))) - (~'-has-primary-content [~'_] (some? ~value-sym)) - (~'-serialize-json [~'_ ~'generator] - (if (some? ~value-sym) - ~(write-value 'generator (or value-form value-sym)) - ~(write-null 'generator))) - (~'-has-secondary-content [~'_] - (or ~'id (seq ~'extension))) - (~'-serialize-json-secondary [~'_ ~'generator] - (write-extended-attributes ~'generator ~'id ~'extension)) - (~'-to-xml [~'_] - (xml-node/element* - nil - (cond-> {} - ~'id (assoc :id ~'id) - (some? ~value-sym) (assoc :value ~(xml-value value-sym))) - ~'extension)) - (~'-hash-into [~'_ ~'sink] - (.putByte ~tagged-sink (byte ~hash-num)) - (when ~'id - (.putByte ~tagged-sink (byte ~id-tag)) - (system/-hash-into ~'id ~'sink)) - (when ~'extension - (.putByte ~tagged-sink (byte ~extension-tag)) - (p/-hash-into ~'extension ~'sink)) - (when-not (nil? ~value-sym) - (.putByte ~tagged-sink (byte ~value-tag)) - (system/-hash-into ~value-sym ~'sink))) - (~'-references [~'_] - (p/-references ~'extension))) - - (defmethod print-method ~name [x# ~tagged-writer] - (.write ~'w ~(str "#" (namespace fhir-type) "/" (clojure.core/name fhir-type))) - (print-method (into {} (remove (comp nil? val)) x#) ~'w)))) - -(defmacro def-primitive-type - [name [value] & {:keys [interned] :or {interned false} :as opts}] - `(do - ~(gen-type name (primitive-tag value) opts) - - ~(when (and interned (#{'String} (:tag (meta value)))) - `(defn ~(symbol (str "create-" (lower-case-first name))) [~value] - (~(symbol (str name ".")) - ~value - (.getBytes ~value StandardCharsets/UTF_8) - (.quoteAsUTF8 (JsonStringEncoder/getInstance) ~value)))) - - ~(gen-extended-record (symbol (str "Extended" name)) value - (cond-> opts - (nil? (:fhir-type opts)) - (assoc :fhir-type (fhir-type-kw name)))) - - (defn ~(symbol (str (lower-case-first name) "?")) [~'x] - (identical? ~(fhir-type-kw name) (blaze.fhir.spec.type/type ~'x))) - - ~(let [fn-sym (symbol (lower-case-first name)) - map-create-extended (symbol (str "map->Extended" name))] - (if interned - (let [create (symbol (str "create-" (lower-case-first name)))] - `(def ~fn-sym - (let [intern# (intern/intern-value ~create) - intern-extended# (intern/intern-value ~map-create-extended)] - (fn [x#] - (if (string? x#) - (intern# x#) - (let [{id# :id extension# :extension} x#] - (cond - (and (nil? extension#) (nil? id#)) - (intern# (:value x#)) - - (and (p/-interned extension#) (nil? id#)) - (intern-extended# x#) - - :else - (~(symbol (str "Extended" name ".")) id# extension# (:value x#))))))))) - - `(def ~fn-sym - (let [intern-extended# (intern/intern-value ~map-create-extended)] - (fn [x#] - (if (map? x#) - (let [{id# :id extension# :extension value# :value} x#] - (cond - (and (nil? value#) (p/-interned extension#) (nil? id#)) - (intern-extended# x#) - - (and (nil? extension#) (nil? id#)) - (~(symbol (str name ".")) value#) - - :else - (~(symbol (str "Extended" name ".")) id# extension# value#))) - (~(symbol (str name ".")) x#))))))) - - (def ~(symbol (str "xml->" name)) - ~(let [value-sym (gensym "value")] - `(fn [{{id# :id ~value-sym :value} :attrs content# :content}] - (let [extension# (seq content#)] - (if (or id# extension#) - (~(symbol (lower-case-first name)) - (cond-> {:id id# :extension extension#} - ~value-sym (assoc :value ~(parse-value value value-sym)))) - (~(symbol (lower-case-first name)) ~(parse-value value value-sym))))))))) - -(defmacro defextended [name [_id _extension value] & {:as opts}] - (gen-extended-record name value opts)) - -(defn- polymorphic-field-names [base-field-name types] - (into - {} - (map - (fn [type] - [(keyword "fhir" (str type)) - (json/field-name (str base-field-name (su/capital (str type))))])) - types)) - -(defn- field-name - "Pre-calculates the field name for non-polymorphic fields or emits code that - gets the field name from a pre-calculated map of possible field names by type." - [field-sym] - (if (:polymorph (meta field-sym)) - (let [field-names (polymorphic-field-names (str field-sym) (:types (meta field-sym)))] - `(~field-names (blaze.fhir.spec.type/type ~(with-meta field-sym nil)))) - (json/field-name (str field-sym)))) - -(defn write-field [generator field-sym] - `(when-not (nil? ~(with-meta field-sym nil)) - ~@(cond - (= 'String (:tag (meta field-sym))) - `[(.writeFieldName ~(with-meta generator {:tag `JsonGenerator}) ~(field-name field-sym)) - (.writeString ~(with-meta generator {:tag `JsonGenerator}) ~(with-meta field-sym nil))] - - (:primitive-string (meta field-sym)) - `[(json/write-primitive-string-field ~generator ~(field-name field-sym) ~(with-meta field-sym nil))] - - (:primitive (meta field-sym)) - `[(json/write-field ~generator ~(field-name field-sym) ~(with-meta field-sym nil))] - - (:primitive-list (meta field-sym)) - `[(json/write-primitive-list-field ~generator ~(field-name field-sym) ~(with-meta field-sym nil))] - - :else - `[(json/write-non-primitive-field ~generator ~(field-name field-sym) ~(with-meta field-sym nil))]))) - -(defmacro def-complex-type - [name [& fields] & {:keys [fhir-type hash-num interned references]}] - (let [m-sym (gensym "m") - interned (or interned `(every? #(p/-interned %) (vals ~m-sym))) - sink-sym (gensym "sink") - sink-sym-tag (with-meta sink-sym {:tag `PrimitiveSink})] - `(do - (defrecord ~name [~@fields] - p/FhirType - (~'-type [~'_] ~(or fhir-type (keyword "fhir" (str name)))) - (~'-interned [~m-sym] ~interned) - (~'-value [~'_]) - (~'-has-primary-content [~'_] true) - (~'-serialize-json [~'_ ~'gen] - ~(write-start-object 'gen) - ~@(map - (fn [field] - (write-field 'gen field)) - fields) - ~(write-end-object 'gen)) - (~'-has-secondary-content [~'_] false) - (~'-serialize-json-secondary [~'this ~'gen] - (throw (ex-info "A complex type has no secondary content." ~'this))) - (~'-hash-into [~'_ ~sink-sym] - (.putByte ~sink-sym-tag (byte ~hash-num)) - ~@(map-indexed - (fn [idx field] - `(when-not (nil? ~field) - (.putByte ~sink-sym-tag (byte ~idx)) - (~(if (= 'id field) `system/-hash-into `p/-hash-into) - ~field ~sink-sym))) - fields)) - ~(if references - `(~'-references [~'_] - ~references) - `(~'-references [~'_] - (-> (transient []) - ~@(keep - (fn [field] - (when-not (= 'id field) - `(into! (p/-references ~field)))) - fields) - (persistent!))))) - - (def ~(symbol (su/pascal->kebab (str name))) - (let [intern# (intern/intern-value ~(symbol (str "map->" name)))] - (fn ~(symbol (su/pascal->kebab (str name))) [{:keys [~@fields] :as ~m-sym}] - (if ~interned - (intern# ~m-sym) - (~(symbol (str name ".")) ~@fields))))) - - (defmethod print-method ~name [x# ~(with-meta 'w {:tag `Writer})] - (.write ~'w ~(str "#fhir/" name)) - (print-method (into {} (remove (comp nil? val)) x#) ~'w))))) diff --git a/modules/fhir-structure/src/blaze/fhir/spec/type/protocols.clj b/modules/fhir-structure/src/blaze/fhir/spec/type/protocols.clj deleted file mode 100644 index e916b0c91..000000000 --- a/modules/fhir-structure/src/blaze/fhir/spec/type/protocols.clj +++ /dev/null @@ -1,34 +0,0 @@ -(ns blaze.fhir.spec.type.protocols) - -(defprotocol FhirType - (-type [_]) - (-interned [_]) - (-value [_]) - (-assoc-id [_ id]) - (-assoc-extension [_ extension]) - (-assoc-value [_ value]) - (-has-primary-content [value] - "Returns true if there is primary JSON content available.") - (-serialize-json [value generator] - "Serializes the primary content of `value`. - - For primitive types, the primary content is the (inner) :value which can be - represented as a primary JSON value or the JSON `null` value because that is - needed to fill up JSON arrays in case `value` has secondary content. For all - other types, it's the whole content. - - See also: https://www.hl7.org/fhir/datatypes.html#representations") - (-has-secondary-content [value] - "Returns true if there is secondary JSON content available.") - (-serialize-json-secondary [value generator] - "Serializes the secondary content of `value`. - - For primitive types, the secondary content is it's :id and :extension that - has to be serialized under a field starting with an underscore. For all - other types, it's a JSON `null` value because that is needed to fill up - JSON arrays in case `value` has primary content. - - See also: https://www.hl7.org/fhir/datatypes.html#representations") - (-to-xml [_]) - (-hash-into [_ sink]) - (-references [_])) diff --git a/modules/fhir-structure/src/blaze/fhir/spec/type/system.clj b/modules/fhir-structure/src/blaze/fhir/spec/type/system.clj index b5efaf08e..862a09535 100644 --- a/modules/fhir-structure/src/blaze/fhir/spec/type/system.clj +++ b/modules/fhir-structure/src/blaze/fhir/spec/type/system.clj @@ -9,20 +9,18 @@ * DateTime * Time * Quantity" - (:refer-clojure :exclude [boolean? decimal? integer? str string? time type]) + (:refer-clojure :exclude [boolean? decimal? integer? str string? time type parse-boolean]) (:require [blaze.anomaly :as ba] [blaze.util :refer [str]] [cognitect.anomalies :as anom]) (:import [blaze.fhir.spec.type.system - Date DateDate DateTime DateTimeDate DateTimeYear DateTimeYearMonth DateYear - DateYearMonth] - [com.google.common.hash PrimitiveSink] + Date DateDate DateTime DateTimeDate DateTimeYear DateTimeYearMonth DateTimes DateYear + DateYearMonth Times] [java.io Writer] - [java.nio.charset StandardCharsets] [java.time DateTimeException LocalDateTime LocalTime OffsetDateTime ZoneOffset] - [java.time.format DateTimeFormatter DateTimeParseException] + [java.time.format DateTimeParseException] [java.time.temporal ChronoField])) (set! *warn-on-reflection* true) @@ -30,8 +28,6 @@ (defprotocol SystemType (-type [_]) - (-to-string [_]) - (-hash-into [_ sink]) (-equals [_ x])) (defn value? @@ -57,48 +53,41 @@ Boolean (-type [_] :system/boolean) - (-to-string [b] - (.toString b)) - (-hash-into [b sink] - (doto ^PrimitiveSink sink - (.putByte (byte 0)) - (.putBoolean b))) (-equals [b x] (some->> x (.equals b)))) (defn boolean? [x] (identical? :system/boolean (-type x))) +(defn parse-boolean [s] + (if (#{"true" "false"} s) + (clojure.core/parse-boolean s) + (ba/incorrect (format "Invalid boolean value `%s`." s)))) + ;; ---- System.Integer -------------------------------------------------------- (extend-protocol SystemType Integer (-type [_] :system/integer) - (-to-string [i] - (.toString i)) - (-hash-into [i sink] - (doto ^PrimitiveSink sink - (.putByte (byte 2)) - (.putInt i))) (-equals [i x] (some->> x (.equals i)))) (defn integer? [x] (identical? :system/integer (-type x))) +(defn parse-integer [s] + (try + (Integer/valueOf ^String s) + (catch NumberFormatException _ + (ba/incorrect (format "Invalid integer value `%s`." s))))) + ;; ---- System.Long ----------------------------------------------------------- (extend-protocol SystemType Long (-type [_] :system/long) - (-to-string [i] - (.toString i)) - (-hash-into [l sink] - (doto ^PrimitiveSink sink - (.putByte (byte 3)) - (.putInt l))) (-equals [l x] (some->> x (.equals l)))) @@ -111,12 +100,6 @@ String (-type [_] :system/string) - (-to-string [s] - s) - (-hash-into [s sink] - (doto ^PrimitiveSink sink - (.putByte (byte 1)) - (.putString s StandardCharsets/UTF_8))) (-equals [s x] (some->> x (.equals s)))) @@ -129,12 +112,6 @@ BigDecimal (-type [_] :system/decimal) - (-to-string [d] - (.toString d)) - (-hash-into [d sink] - (doto ^PrimitiveSink sink - (.putByte (byte 4)) - (.putString (str d) StandardCharsets/UTF_8))) (-equals [d x] (some->> x (.equals d)))) @@ -180,10 +157,6 @@ DateYear (-type [date] (.type date)) - (-to-string [date] - (.toString date)) - (-hash-into [date sink] - (.hashInto date sink)) (-equals [date x] (cond (instance? DateYear x) (.equals date x) @@ -192,10 +165,6 @@ DateYearMonth (-type [date] (.type date)) - (-to-string [date] - (.toString date)) - (-hash-into [date sink] - (.hashInto date sink)) (-equals [date x] (cond (instance? DateYearMonth x) (.equals date x) @@ -204,17 +173,13 @@ DateDate (-type [date] (.type date)) - (-to-string [date] - (.toString date)) - (-hash-into [date sink] - (.hashInto date sink)) (-equals [date x] (cond (instance? DateDate x) (.equals date x) (instance? DateTimeDate x) (.equals date (.toDate ^DateTimeDate x))))) (defmethod print-method DateYear [^DateYear date ^Writer w] - (.write w "#system/date\"") + (.write w "#system/date \"") (.write w (str date)) (.write w "\"")) @@ -224,7 +189,7 @@ (.write w ")")) (defmethod print-method DateYearMonth [^DateYearMonth date ^Writer w] - (.write w "#system/date\"") + (.write w "#system/date \"") (.write w (str date)) (.write w "\"")) @@ -236,7 +201,7 @@ (.write w ")")) (defmethod print-method DateDate [^DateDate date ^Writer w] - (.write w "#system/date\"") + (.write w "#system/date \"") (.write w (str date)) (.write w "\"")) @@ -290,7 +255,7 @@ (ba/try-one DateTimeException ::anom/incorrect (DateTime/parse s)))) (defmethod print-method DateTimeYear [^DateTimeYear date-time ^Writer w] - (.write w "#system/date-time\"") + (.write w "#system/date-time \"") (.write w (str date-time)) (.write w "\"")) @@ -300,7 +265,7 @@ (.write w ")")) (defmethod print-method DateTimeYearMonth [^DateTimeYearMonth date-time ^Writer w] - (.write w "#system/date-time\"") + (.write w "#system/date-time \"") (.write w (str date-time)) (.write w "\"")) @@ -312,7 +277,7 @@ (.write w ")")) (defmethod print-method DateTimeDate [^DateTimeDate date-time ^Writer w] - (.write w "#system/date-time\"") + (.write w "#system/date-time \"") (.write w (str date-time)) (.write w "\"")) @@ -326,8 +291,8 @@ (.write w ")")) (defmethod print-method LocalDateTime [^LocalDateTime dateTime ^Writer w] - (.write w "#system/date-time\"") - (.write w (str dateTime)) + (.write w "#system/date-time \"") + (.write w (DateTimes/toString dateTime)) (.write w "\"")) (defmethod print-dup LocalDateTime [^LocalDateTime dateTime ^Writer w] @@ -348,8 +313,8 @@ (.write w ")")) (defmethod print-method OffsetDateTime [^OffsetDateTime dateTime ^Writer w] - (.write w "#system/date-time\"") - (.write w (str dateTime)) + (.write w "#system/date-time \"") + (.write w (DateTimes/toString dateTime)) (.write w "\"")) (defmethod print-dup OffsetDateTime [^OffsetDateTime dateTime ^Writer w] @@ -371,6 +336,11 @@ (.write w (.getId (.getOffset dateTime))) (.write w "\"))")) +(defmethod print-method LocalTime [^LocalTime time ^Writer w] + (.write w "#system/time \"") + (.write w (Times/toString time)) + (.write w "\"")) + (defmethod print-dup LocalTime [^LocalTime time ^Writer w] (.write w "#=(java.time.LocalTime/of ") (.write w (str (.getHour time))) @@ -386,10 +356,6 @@ DateTimeYear (-type [date-time] (.type date-time)) - (-to-string [date-time] - (.toString date-time)) - (-hash-into [date-time sink] - (.hashInto date-time sink)) (-equals [date-time x] (cond (instance? DateTimeYear x) (.equals date-time x) @@ -398,10 +364,6 @@ DateTimeYearMonth (-type [date-time] (.type date-time)) - (-to-string [date-time] - (.toString date-time)) - (-hash-into [date-time sink] - (.hashInto date-time sink)) (-equals [date-time x] (cond (instance? DateTimeYearMonth x) (.equals date-time x) @@ -410,10 +372,6 @@ DateTimeDate (-type [date-time] (.type date-time)) - (-to-string [date-time] - (.toString date-time)) - (-hash-into [date-time sink] - (.hashInto date-time sink)) (-equals [date-time x] (cond (instance? DateTimeDate x) (.equals date-time x) @@ -422,18 +380,6 @@ LocalDateTime (-type [_] :system/date-time) - (-to-string [date-time] - (.format DateTimeFormatter/ISO_LOCAL_DATE_TIME date-time)) - (-hash-into [date-time sink] - (doto ^PrimitiveSink sink - (.putByte (byte 6)) - (.putInt (.getYear date-time)) - (.putInt (.getMonthValue date-time)) - (.putInt (.getDayOfMonth date-time)) - (.putInt (.getHour date-time)) - (.putInt (.getMinute date-time)) - (.putInt (.getSecond date-time)) - (.putInt (.getNano date-time)))) (-equals [date-time x] (cond (instance? LocalDateTime x) (.equals date-time x))) @@ -441,19 +387,6 @@ OffsetDateTime (-type [_] :system/date-time) - (-to-string [date-time] - (.format DateTimeFormatter/ISO_DATE_TIME date-time)) - (-hash-into [date-time sink] - (doto ^PrimitiveSink sink - (.putByte (byte 6)) - (.putInt (.getYear date-time)) - (.putInt (.getMonthValue date-time)) - (.putInt (.getDayOfMonth date-time)) - (.putInt (.getHour date-time)) - (.putInt (.getMinute date-time)) - (.putInt (.getSecond date-time)) - (.putInt (.getNano date-time)) - (.putInt (.getTotalSeconds (.getOffset date-time))))) (-equals [date-time x] (cond (instance? OffsetDateTime x) (.equals date-time x)))) @@ -557,15 +490,6 @@ LocalTime (-type [_] :system/time) - (-to-string [time] - (.format DateTimeFormatter/ISO_LOCAL_TIME time)) - (-hash-into [time sink] - (doto ^PrimitiveSink sink - (.putByte (byte 7)) - (.putInt (.getHour time)) - (.putInt (.getMinute time)) - (.putInt (.getSecond time)) - (.putInt (.getNano time)))) (-equals [time x] (some->> x (.equals time)))) @@ -593,14 +517,8 @@ (extend-protocol SystemType Object (-type [_]) - (-to-string [o] - (.toString o)) - (-hash-into [_ _]) (-equals [_ _] false) nil (-type [_]) - (-to-string [_] - "nil") - (-hash-into [_ _]) (-equals [_ _])) diff --git a/modules/fhir-structure/src/blaze/fhir/spec/type/system/spec.clj b/modules/fhir-structure/src/blaze/fhir/spec/type/system/spec.clj index c11e06d71..338ea50b7 100644 --- a/modules/fhir-structure/src/blaze/fhir/spec/type/system/spec.clj +++ b/modules/fhir-structure/src/blaze/fhir/spec/type/system/spec.clj @@ -1,31 +1,13 @@ (ns blaze.fhir.spec.type.system.spec (:require [blaze.fhir.spec.type.system :as system] - [clojure.spec.alpha :as s] - [clojure.spec.gen.alpha :as sg])) + [clojure.spec.alpha :as s])) (s/def :system/date - (s/with-gen - system/date? - #(sg/fmap - (partial apply system/date) - (sg/tuple - (s/gen (s/int-in 1 10000)) - (s/gen (s/int-in 1 13)) - (s/gen (s/int-in 1 29)))))) + system/date?) (s/def :system/date-time - (s/with-gen - system/date-time? - #(sg/fmap - (partial apply system/date-time) - (sg/tuple - (s/gen (s/int-in 1 10000)) - (s/gen (s/int-in 1 13)) - (s/gen (s/int-in 1 29)) - (s/gen (s/int-in 0 24)) - (s/gen (s/int-in 0 60)) - (s/gen (s/int-in 0 60)))))) + system/date-time?) (s/def :system/date-or-date-time (s/or :date :system/date :date-time :system/date-time)) diff --git a/modules/fhir-structure/src/blaze/fhir/spec/type_spec.clj b/modules/fhir-structure/src/blaze/fhir/spec/type_spec.clj index e31cc6e25..1b090828a 100644 --- a/modules/fhir-structure/src/blaze/fhir/spec/type_spec.clj +++ b/modules/fhir-structure/src/blaze/fhir/spec/type_spec.clj @@ -1,18 +1,18 @@ (ns blaze.fhir.spec.type-spec (:require + [blaze.fhir.spec.impl.xml :as xml] [blaze.fhir.spec.type :as type] [blaze.fhir.spec.type.system :as system] [blaze.fhir.spec.type.system-spec] - [clojure.alpha.spec :as s2] - [clojure.spec.alpha :as s])) + [clojure.spec.alpha :as s] + [cognitect.anomalies :as anom]) + (:import + [blaze.fhir.spec.type Primitive] + [java.time OffsetDateTime])) -(s/fdef type/type - :args (s/cat :x any?) - :ret (s/nilable :fhir/type)) - -(s/fdef type/value - :args (s/cat :x any?) - :ret (s/nilable system/value?)) +(s/fdef type/to-xml + :args (s/cat :x #(instance? Primitive %)) + :ret xml/element?) (s/fdef type/references :args (s/cat :x any?) @@ -20,76 +20,187 @@ (s/fdef type/boolean :args (s/cat :value (s/alt :value boolean? :extended map?)) - :ret (s/or :value type/boolean? :invalid s2/invalid?)) + :ret (s/or :value type/boolean? :anomaly ::anom/anomaly)) (s/fdef type/integer :args (s/cat :value (s/alt :value int? :extended map?)) - :ret (s/or :value type/integer? :invalid s2/invalid?)) - -(s/fdef type/long - :args (s/cat :value (s/alt :value int? :extended map?)) - :ret (s/or :value type/integer? :invalid s2/invalid?)) + :ret (s/or :value type/integer? :anomaly ::anom/anomaly)) (s/fdef type/string :args (s/cat :value (s/alt :value string? :extended map?)) - :ret (s/or :value type/string? :invalid s2/invalid?)) + :ret type/string?) + +(s/fdef type/string-interned + :args (s/cat :value (s/alt :value string? :extended map?)) + :ret type/string?) (s/fdef type/decimal - :args (s/cat :value (s/alt :integer-value int? :decimal-value decimal? :extended map?)) - :ret (s/or :value type/string? :invalid s2/invalid?)) + :args (s/cat :value (s/alt :value decimal? :extended map?)) + :ret type/decimal?) (s/fdef type/uri :args (s/cat :value (s/alt :value string? :extended map?)) - :ret (s/or :value type/uri? :invalid s2/invalid?)) + :ret type/uri?) + +(s/fdef type/uri-interned + :args (s/cat :value (s/alt :value string? :extended map?)) + :ret type/uri?) (s/fdef type/url :args (s/cat :value (s/alt :value string? :extended map?)) - :ret (s/or :value type/url? :invalid s2/invalid?)) + :ret type/url?) (s/fdef type/canonical :args (s/cat :value (s/alt :value string? :extended map?)) - :ret (s/or :value type/canonical? :invalid s2/invalid?)) + :ret type/canonical?) + +(s/fdef type/base64Binary + :args (s/cat :value (s/alt :value string? :extended map?)) + :ret type/base64Binary?) (s/fdef type/instant - :args (s/cat :value (s/alt :string-value string? :system-value system/date-time? :extended map?)) - :ret (s/or :value type/instant? :invalid s2/invalid?)) + :args (s/cat :value (s/alt :value #(instance? OffsetDateTime %) :extended map?)) + :ret type/instant?) (s/fdef type/date - :args (s/cat :value (s/alt :string-value string? :system-value system/date? :extended map?)) - :ret (s/or :value type/date? :invalid s2/invalid?)) + :args (s/cat :value (s/alt :value system/date? :extended map?)) + :ret type/date?) (s/fdef type/dateTime - :args (s/cat :value (s/alt :string-value string? :system-value system/date-time? :extended map?)) - :ret (s/or :value type/dateTime? :invalid s2/invalid?)) + :args (s/cat :value (s/alt :value system/date-time? :extended map?)) + :ret type/dateTime?) (s/fdef type/time - :args (s/cat :value (s/alt :string-value string? :system-value system/time? :extended map?)) - :ret (s/or :value type/time? :invalid s2/invalid?)) + :args (s/cat :value (s/alt :value system/time? :extended map?)) + :ret type/time?) (s/fdef type/code :args (s/cat :value (s/alt :value string? :extended map?)) - :ret (s/or :value type/code? :invalid s2/invalid?)) + :ret type/code?) (s/fdef type/oid :args (s/cat :value (s/alt :value string? :extended map?)) - :ret (s/or :value type/oid? :invalid s2/invalid?)) + :ret type/oid?) (s/fdef type/id :args (s/cat :value (s/alt :value string? :extended map?)) - :ret (s/or :value type/id? :invalid s2/invalid?)) + :ret type/id?) (s/fdef type/markdown :args (s/cat :value (s/alt :value string? :extended map?)) - :ret (s/or :value type/markdown? :invalid s2/invalid?)) + :ret type/markdown?) (s/fdef type/unsignedInt :args (s/cat :value (s/alt :value int? :extended map?)) - :ret (s/or :value type/unsignedInt? :invalid s2/invalid?)) + :ret (s/or :value type/unsignedInt? :anomaly ::anom/anomaly)) (s/fdef type/positiveInt :args (s/cat :value (s/alt :value int? :extended map?)) - :ret (s/or :value type/positiveInt? :invalid s2/invalid?)) + :ret (s/or :value type/positiveInt? :anomaly ::anom/anomaly)) (s/fdef type/uuid :args (s/cat :value (s/alt :value string? :extended map?)) - :ret (s/or :value type/uuid? :invalid s2/invalid?)) + :ret type/uuid?) + +(s/fdef type/xhtml + :args (s/cat :value (s/alt :value string? :extended map?)) + :ret type/xhtml?) + +(s/fdef type/address + :args (s/cat :x map?)) + +(s/fdef type/age + :args (s/cat :x map?)) + +(s/fdef type/annotation + :args (s/cat :x map?)) + +(s/fdef type/attachment + :args (s/cat :x map?)) + +(s/fdef type/codeable-concept + :args (s/cat :x map?)) + +(s/fdef type/coding + :args (s/cat :x map?)) + +(s/fdef type/contact-detail + :args (s/cat :x map?)) + +(s/fdef type/contact-point + :args (s/cat :x map?)) + +(s/fdef type/contributor + :args (s/cat :x map?)) + +(s/fdef type/count + :args (s/cat :x map?)) + +(s/fdef type/data-requirement + :args (s/cat :x map?)) + +(s/fdef type/distance + :args (s/cat :x map?)) + +(s/fdef type/dosage + :args (s/cat :x map?)) + +(s/fdef type/dosage-dose-and-rate + :args (s/cat :x map?)) + +(s/fdef type/duration + :args (s/cat :x map?)) + +(s/fdef type/expression + :args (s/cat :x map?)) + +(s/fdef type/human-name + :args (s/cat :x map?)) + +(s/fdef type/identifier + :args (s/cat :x map?)) + +(s/fdef type/meta + :args (s/cat :x map?)) + +(s/fdef type/money + :args (s/cat :x map?)) + +(s/fdef type/parameter-definition + :args (s/cat :x map?)) + +(s/fdef type/period + :args (s/cat :x map?)) + +(s/fdef type/quantity + :args (s/cat :x map?)) + +(s/fdef type/range + :args (s/cat :x map?)) + +(s/fdef type/ratio + :args (s/cat :x map?)) + +(s/fdef type/reference + :args (s/cat :x map?)) + +(s/fdef type/related-artifact + :args (s/cat :x map?)) + +(s/fdef type/sampled-data + :args (s/cat :x map?)) + +(s/fdef type/signature + :args (s/cat :x map?)) + +(s/fdef type/timing-repeat + :args (s/cat :x map?)) + +(s/fdef type/timing + :args (s/cat :x map?)) + +#_(s/fdef type/trigger-definition + :args (s/cat :x map?)) + +#_(s/fdef type/usage-context + :args (s/cat :x map?)) diff --git a/modules/fhir-structure/src/blaze/fhir/spec_spec.clj b/modules/fhir-structure/src/blaze/fhir/spec_spec.clj index c4dcbc139..4e8766237 100644 --- a/modules/fhir-structure/src/blaze/fhir/spec_spec.clj +++ b/modules/fhir-structure/src/blaze/fhir/spec_spec.clj @@ -20,14 +20,6 @@ (s/spec (s/cat :op #(= `s2/or %) :choices (s/* (s/cat :key keyword? :spec some?))))) -(s/fdef fhir-spec/fhir-type - :args (s/cat :x any?) - :ret (s/nilable :fhir/type)) - -(s/fdef fhir-spec/primitive? - :args (s/cat :spec any?) - :ret boolean?) - (s/fdef fhir-spec/primitive-val? :args (s/cat :spec any?) :ret boolean?) @@ -44,19 +36,19 @@ (s/fdef fhir-spec/write-json :args (s/cat :context :blaze.fhir/writing-context - :out #(instance? OutputStream %) :value any?)) + :out #(instance? OutputStream %) :value :fhir/value)) (s/fdef fhir-spec/write-json-as-bytes - :args (s/cat :context :blaze.fhir/writing-context :value any?) - :ret bytes?) + :args (s/cat :context :blaze.fhir/writing-context :value :fhir/value) + :ret (s/or :result bytes? :anomaly ::anom/anomaly)) (s/fdef fhir-spec/write-json-as-string - :args (s/cat :context :blaze.fhir/writing-context :value any?) - :ret string?) + :args (s/cat :context :blaze.fhir/writing-context :value :fhir/value) + :ret (s/or :result string? :anomaly ::anom/anomaly)) (s/fdef fhir-spec/write-cbor - :args (s/cat :context :blaze.fhir/writing-context :resource any?) - :ret bytes?) + :args (s/cat :context :blaze.fhir/writing-context :value :fhir/value) + :ret (s/or :result bytes? :anomaly ::anom/anomaly)) (s/fdef fhir-spec/conform-xml :args (s/cat :x any?) diff --git a/modules/fhir-structure/src/blaze/fhir/structure_definition_repo.clj b/modules/fhir-structure/src/blaze/fhir/structure_definition_repo.clj index e6a454892..304cfd343 100644 --- a/modules/fhir-structure/src/blaze/fhir/structure_definition_repo.clj +++ b/modules/fhir-structure/src/blaze/fhir/structure_definition_repo.clj @@ -70,8 +70,7 @@ (comp (map :resource) (filter (comp #{"complex-type"} :kind)) (remove :abstract) - ;; TODO: look into how to handle this special quantity types - (remove (comp #{"MoneyQuantity" "SimpleQuantity"} :name))) + (remove (comp #{"constraint"} :derivation))) (:entry (read-bundle "blaze/fhir/r4/profiles-types.json")))) (-resources [_] (into diff --git a/modules/fhir-structure/src/blaze/fhir/structure_definition_repo_spec.clj b/modules/fhir-structure/src/blaze/fhir/structure_definition_repo_spec.clj index 763bc5134..0943b104e 100644 --- a/modules/fhir-structure/src/blaze/fhir/structure_definition_repo_spec.clj +++ b/modules/fhir-structure/src/blaze/fhir/structure_definition_repo_spec.clj @@ -6,6 +6,11 @@ [blaze.fhir.structure-definition-repo.spec] [clojure.spec.alpha :as s])) +(s/fdef sdr/primitive-types + :args (s/cat :repo :blaze.fhir/structure-definition-repo)) + +(s/fdef sdr/complex-types + :args (s/cat :repo :blaze.fhir/structure-definition-repo)) + (s/fdef sdr/resources - :args (s/cat :repo :blaze.fhir/structure-definition-repo) - :ret (s/coll-of map?)) + :args (s/cat :repo :blaze.fhir/structure-definition-repo)) diff --git a/modules/fhir-structure/src/blaze/fhir/util.clj b/modules/fhir-structure/src/blaze/fhir/util.clj index aa595647c..05fbff53f 100644 --- a/modules/fhir-structure/src/blaze/fhir/util.clj +++ b/modules/fhir-structure/src/blaze/fhir/util.clj @@ -3,6 +3,7 @@ [blaze.fhir.spec.type :as type] [clojure.string :as str]) (:import + [blaze.fhir.spec.type Base] [java.util Comparator])) (set! *warn-on-reflection* true) @@ -17,20 +18,22 @@ (when (some? value) {:fhir/type :fhir.Parameters/parameter :name (type/string name) - (if (:fhir/type value) :resource :value) value}))) + ;; TODO: improve resource detection + (if (instance? Base value) :value :resource) value}))) (partition 2 nvs))}) (def subsetted + "SUBSETTED Coding" #fhir/Coding - {:system #fhir/uri "http://terminology.hl7.org/CodeSystem/v3-ObservationValue" + {:system #fhir/uri-interned "http://terminology.hl7.org/CodeSystem/v3-ObservationValue" :code #fhir/code "SUBSETTED"}) (defn subsetted? "Checks whether `coding` is a SUBSETTED coding." {:arglists '([coding])} - [{:keys [system code]}] - (and (= #fhir/uri "http://terminology.hl7.org/CodeSystem/v3-ObservationValue" system) - (= #fhir/code "SUBSETTED" code))) + [{{system-value :value} :system {code-value :value} :code}] + (and (= "http://terminology.hl7.org/CodeSystem/v3-ObservationValue" system-value) + (= "SUBSETTED" code-value))) (defn- nat-cmp [^Comparable x y] (.compareTo x y)) @@ -66,8 +69,8 @@ (:blaze.db/t (:blaze.db/tx (meta resource)))) (def ^:private priority-cmp - (-> (Comparator/comparing #(-> % :status type/value) (Comparator/nullsFirst (.reversed (Comparator/naturalOrder)))) - (.thenComparing #(-> % :version type/value) version-cmp) + (-> (Comparator/comparing #(-> % :status :value) (Comparator/nullsFirst (.reversed (Comparator/naturalOrder)))) + (.thenComparing #(-> % :version :value) version-cmp) (.thenComparing t (Comparator/nullsFirst (Comparator/naturalOrder))) (.thenComparing #(% :id) (Comparator/naturalOrder)) (.reversed))) diff --git a/modules/fhir-structure/src/blaze/fhir/writing_context.clj b/modules/fhir-structure/src/blaze/fhir/writing_context.clj index a9f6b2d06..d2ebf6e34 100644 --- a/modules/fhir-structure/src/blaze/fhir/writing_context.clj +++ b/modules/fhir-structure/src/blaze/fhir/writing_context.clj @@ -3,9 +3,6 @@ (:require [blaze.anomaly :as ba] [blaze.fhir.spec.resource :as res] - [blaze.fhir.spec.type :as type] - [blaze.fhir.spec.type.json :as json] - [blaze.fhir.spec.type.protocols :as p] [blaze.fhir.spec.type.string-util :as su] [blaze.fhir.structure-definition-repo :as sdr] [blaze.fhir.structure-definition-repo.spec] @@ -18,6 +15,7 @@ [taoensso.timbre :as log]) (:import [blaze ReducibleArray] + [blaze.fhir.spec.type Base Complex FieldName Primitive] [com.fasterxml.jackson.core JsonGenerator SerializableString])) (set! *warn-on-reflection* true) @@ -34,7 +32,7 @@ (map (fn [{:keys [code]}] [(keyword "fhir" code) - (json/field-name (str base-field-name (su/capital code)))])) + (FieldName/of (str base-field-name (su/capital code)))])) element-types)) (defn- property-handler-definitions @@ -49,7 +47,7 @@ {:keys [path] content-reference :contentReference element-types :type}] (if content-reference (let [base-field-name (res/base-field-name parent-type path false) - field-name (json/field-name base-field-name)] + field-name (FieldName/of base-field-name)] (PropertyHandler. (keyword base-field-name) (fn [_type] field-name) @@ -67,37 +65,46 @@ (keyword base-field-name) (if polymorphic (polymorphic-field-names base-field-name element-types) - (let [field-name (json/field-name base-field-name)] + (let [field-name (FieldName/of base-field-name)] (fn [_type] field-name))) polymorphic (if complex-type (keyword "fhir" first-type-code) - (when element-type - (fhir-type-keyword path))))))) + (if element-type + (fhir-type-keyword path) + (when (= "http://hl7.org/fhirpath/System.String" first-type-code) + :system/string))))))) (defn- create-property-handlers "Returns a map of JSON property names to property handlers." [type element-definitions] (ReducibleArray. (map (partial property-handler-definitions type) element-definitions))) -(defn- field-name ^SerializableString [^PropertyHandler property-handler type] +(defn- field-name ^FieldName [^PropertyHandler property-handler type] ((.-field-name property-handler) type)) -(defn- write-field! [type-handlers ^JsonGenerator gen ^PropertyHandler property-handler value] +(defn- write-system-string-field [^JsonGenerator generator ^SerializableString field-name value] + (.writeFieldName generator field-name) + (.writeString generator ^String value)) + +(defn- write-field! + [type-handlers ^JsonGenerator gen ^PropertyHandler property-handler value] (if (sequential? value) (when-some [first-value (first value)] - (when-some [type (or (.-type property-handler) (type/type first-value))] + (when-some [type (or (.-type property-handler) (:fhir/type first-value))] (if-some [handler (type-handlers type)] - (do (.writeFieldName gen (field-name property-handler type)) + (do (.writeFieldName gen (.normal (field-name property-handler type))) (.writeStartArray gen) (run! #(handler type-handlers gen %) value) (.writeEndArray gen)) - (json/write-field gen (field-name property-handler type) value)))) - (when-some [type (or (.-type property-handler) (type/type value))] + (Primitive/serializeJsonPrimitiveList value gen (field-name property-handler type))))) + (when-some [type (or (.-type property-handler) (:fhir/type value))] (if-some [handler (type-handlers type)] - (do (.writeFieldName gen (field-name property-handler type)) + (do (.writeFieldName gen (.normal (field-name property-handler type))) (handler type-handlers gen value)) - (json/write-field gen (field-name property-handler type) value))))) + (if (identical? :system/string type) + (write-system-string-field gen (.normal (field-name property-handler type)) value) + (.serializeJsonField ^Base value gen (field-name property-handler type))))))) (defn- write-fields! [type-handlers property-handlers gen m] (run! @@ -107,9 +114,14 @@ property-handlers)) (def ^:private record-types - #{"Attachment" "Extension" "Coding" "CodeableConcept" "Quantity" "Ratio" - "Period" "Identifier" "HumanName" "Address" "Reference" "Meta" - "Bundle.entry.search"}) + #{"Address" "Age" "Annotation" "Attachment" "Bundle.entry.search" + "CodeableConcept" "Coding" "ContactDetail" "ContactPoint" "Contributor" + "Count" "DataRequirement" "DataRequirement.codeFilter" + "DataRequirement.dateFilter" "DataRequirement.sort" "Distance" "Dosage" + "Dosage.doseAndRate" "Duration" "Expression" "Extension" "HumanName" + "Identifier" "Meta" "Money" "ParameterDefinition" "Period" "Quantity" + "Range" "Ratio" "Reference" "RelatedArtifact" "SampledData" "Signature" + "Timing" "Timing.repeat" "TriggerDefinition" "UsageContext"}) (defn- create-type-handler "Creates a handler for `type` using `element-definitions`. @@ -120,7 +132,7 @@ [kind type element-definitions] (if (record-types type) (fn record-type-handler [_type-handlers gen value] - (p/-serialize-json value gen)) + (.serializeAsJsonValue ^Complex value gen)) (let [property-handlers (create-property-handlers type element-definitions)] (condp = kind :resource diff --git a/modules/fhir-structure/test-perf/blaze/fhir/hash_test_perf.clj b/modules/fhir-structure/test-perf/blaze/fhir/hash_test_perf.clj index 03203f79a..d2de48f49 100644 --- a/modules/fhir-structure/test-perf/blaze/fhir/hash_test_perf.clj +++ b/modules/fhir-structure/test-perf/blaze/fhir/hash_test_perf.clj @@ -8,7 +8,7 @@ {:fhir/type :fhir/Observation :id "DACG22233TWT7CK4" :meta #fhir/Meta {:versionId #fhir/id "481283" - :lastUpdated #fhir/instant "2022-04-20T11:58:38.070Z" + :lastUpdated #fhir/instant #system/date-time "2022-04-20T11:58:38.070Z" :profile [#fhir/canonical "http://hl7.org/fhir/StructureDefinition/bmi" #fhir/canonical "http://hl7.org/fhir/StructureDefinition/vitalsigns"]} :status #fhir/code "final" @@ -16,21 +16,21 @@ [#fhir/CodeableConcept {:coding [#fhir/Coding - {:system #fhir/uri "http://terminology.hl7.org/CodeSystem/observation-category" + {:system #fhir/uri-interned "http://terminology.hl7.org/CodeSystem/observation-category" :code #fhir/code "vital-signs" - :display #fhir/string "vital-signs"}]}] + :display #fhir/string-interned "vital-signs"}]}] :code #fhir/CodeableConcept - {:coding [#fhir/Coding{:system #fhir/uri "http://loinc.org" + {:coding [#fhir/Coding{:system #fhir/uri-interned "http://loinc.org" :code #fhir/code "39156-5" - :display #fhir/string "Body Mass Index"}] + :display #fhir/string-interned "Body Mass Index"}] :text "Body Mass Index"} :subject #fhir/Reference{:reference #fhir/string "Patient/DACG22233TWT7CKL"} - :effective #fhir/dateTime "2013-01-04T23:45:50Z" - :issued #fhir/instant "2013-01-04T23:45:50.072Z" + :effective #fhir/dateTime #system/date-time "2013-01-04T23:45:50Z" + :issued #fhir/instant #system/date-time "2013-01-04T23:45:50.072Z" :value #fhir/Quantity {:value 14.97M :unit "kg/m2" - :system #fhir/uri "http://unitsofmeasure.org" + :system #fhir/uri-interned "http://unitsofmeasure.org" :code #fhir/code "kg/m2"}}) (comment diff --git a/modules/fhir-structure/test-perf/blaze/fhir/spec/memory.clj b/modules/fhir-structure/test-perf/blaze/fhir/spec/memory.clj deleted file mode 100644 index 8551ea428..000000000 --- a/modules/fhir-structure/test-perf/blaze/fhir/spec/memory.clj +++ /dev/null @@ -1,21 +0,0 @@ -(ns blaze.fhir.spec.memory - (:import - [clojure.lang PersistentVector] - [org.openjdk.jol.info GraphLayout])) - -(defn graph-layout ^GraphLayout [& roots] - (GraphLayout/parseInstance (object-array roots))) - -(defn total-size [& roots] - (-> (apply graph-layout roots) - (.subtract (graph-layout (PersistentVector/EMPTY_NODE))) - (.totalSize))) - -(defn print-layout [& roots] - (-> (apply graph-layout roots) - (.subtract (graph-layout (PersistentVector/EMPTY_NODE))) - (.toPrintable))) - -(defn print-total-layout [& roots] - (-> (apply graph-layout roots) - (.toPrintable))) diff --git a/modules/fhir-structure/test-perf/blaze/fhir/spec/type_test_mem.clj b/modules/fhir-structure/test-perf/blaze/fhir/spec/type_test_mem.clj index ad0a5ac4e..7b4c2aaeb 100644 --- a/modules/fhir-structure/test-perf/blaze/fhir/spec/type_test_mem.clj +++ b/modules/fhir-structure/test-perf/blaze/fhir/spec/type_test_mem.clj @@ -1,57 +1,89 @@ (ns blaze.fhir.spec.type-test-mem (:require + [blaze.fhir.parsing-context] + [blaze.fhir.spec :as fhir-spec] [blaze.fhir.spec.memory :as mem] [blaze.fhir.spec.type :as type] + [blaze.fhir.spec.type.system] + [blaze.fhir.test-util :refer [structure-definition-repo]] [blaze.test-util] [clojure.alpha.spec :as s2] [clojure.string :as str] - [clojure.test :refer [are deftest is testing]]) + [clojure.test :refer [are deftest is testing]] + [integrant.core :as ig] + [jsonista.core :as j]) (:import - [java.time Instant ZoneOffset])) + [blaze.fhir.spec.type Base])) + +(def ^:private parsing-context + (ig/init-key + :blaze.fhir/parsing-context + {:structure-definition-repo structure-definition-repo})) + +(defn- parse-json + ([data] + (fhir-spec/parse-json parsing-context (j/write-value-as-string data))) + ([type data] + (fhir-spec/parse-json parsing-context type (j/write-value-as-string data)))) + +(comment + (mem/print-total-layout #fhir/string "12345678") + (mem/print-layout #fhir/string "12345678") + (mem/print-class-layout #fhir/string{:id "foo"}) + (mem/print-total-layout {:a 1}) + (mem/print-footprint + {:a 1 :b 1 :c 1 :e 1 :f 1 :g 1 :h 1 :i 1 :j 1} + :a :b :c :d :e :f :g :h :i :j 1) + (mem/print-layout (parse-json {:resourceType "Observation" :id "192116"})) + (mem/print-footprint + (parse-json {:resourceType "Observation" :id "192116"}) + :fhir/type :fhir/Observation :id) + (total-size #fhir/decimal 1.1M)) + +(defn- total-size [x] + (if (Base/isInterned x) 0 (mem/total-size x))) (deftest mem-test - (are [x size] (= (mem/total-size x) size) - #fhir/integer 1 16 + (are [x size] (= (total-size x) (Base/memSize x) size) + #fhir/integer 1 24 - #fhir/long 1 24 + #fhir/string "" 0 + #fhir/string "a" 0 + #fhir/string{:value "a"} 0 + (type/string (str/join (repeat 12 "a"))) 64 + (type/string (str/join (repeat 13 "a"))) 72 + #fhir/string{:id "0" :value "foo"} 112 - #fhir/string "" 40 - #fhir/string "a" 48 - #fhir/string{:value "a"} 48 - (type/string (str/join (repeat 8 "a"))) 48 - (type/string (str/join (repeat 9 "a"))) 56 - #fhir/string{:id "0" :value "foo"} 136 + #fhir/decimal 1.1M 48 - #fhir/decimal 1.1M 40 - - #fhir/uri "" 96 - #fhir/uri "a" 120 + #fhir/uri "" 0 + #fhir/uri "a" 0 #fhir/url "" 56 - #fhir/url "a" 64 + #fhir/url "a" 56 - #fhir/canonical "" 96 - #fhir/canonical "a" 120 + #fhir/canonical "" 0 + #fhir/canonical "a" 0 #fhir/base64Binary "" 56 - #fhir/base64Binary "YQo=" 64 - #fhir/base64Binary "MTA1NjE0Cg==" 72 + #fhir/base64Binary "YQo=" 56 + #fhir/base64Binary "MTA1NjE0Cg==" 64 - #fhir/date "2020" 16 - #fhir/date "2020-01" 24 - #fhir/date "2020-01-01" 24 + #fhir/date #system/date "2020" 32 + #fhir/date #system/date "2020-01" 32 + #fhir/date #system/date "2020-01-01" 32 - #fhir/dateTime "2020" 16 - #fhir/dateTime "2020-01" 24 - #fhir/dateTime "2020-01-01" 24 + #fhir/dateTime #system/date-time "2020" 32 + #fhir/dateTime #system/date-time "2020-01" 32 + #fhir/dateTime #system/date-time "2020-01-01" 32 - #fhir/dateTime "2020-01-01T00:00:00" 72 - #fhir/dateTime "2020-01-01T00:00:00.000" 72 + #fhir/dateTime #system/date-time "2020-01-01T00:00:00" 64 + #fhir/dateTime #system/date-time "2020-01-01T00:00:00.000" 64 - #fhir/time "13:53:21" 24 + #fhir/time #system/time "13:53:21" 32 - #fhir/code "" 96 - #fhir/code "175718" 120 + #fhir/code "" 0 + #fhir/code "175718" 0 #fhir/oid "" 56 #fhir/oid "175718" 64 @@ -65,174 +97,71 @@ #fhir/unsignedInt 0 16 #fhir/unsignedInt 175718 16 - #fhir/positiveInt 0 16 + #fhir/positiveInt 1 16 #fhir/positiveInt 175718 16 - #fhir/uuid "urn:uuid:6d270b7d-bf7d-4c95-8e30-4d87360d47a3" 32 + #fhir/uuid "urn:uuid:6d270b7d-bf7d-4c95-8e30-4d87360d47a3" 40 #fhir/xhtml "" 56 #fhir/xhtml "175718" 64 - #fhir/Attachment{} 72 + #fhir/Attachment{} 48 - #fhir/Extension{} 48 + #fhir/Extension{} 0 - #fhir/Coding{} 56 + #fhir/Coding{} 0 - #fhir/CodeableConcept{} 48 + #fhir/CodeableConcept{} 0 - #fhir/Quantity{} 56 + #fhir/Quantity{} 0 - #fhir/Ratio{} 48 + #fhir/Ratio{} 0 - #fhir/Period{} 48 + #fhir/Period{} 0 - #fhir/Identifier{} 64 + #fhir/Identifier{} 40 - #fhir/HumanName{} 64 + #fhir/HumanName{} 40 - #fhir/Address{} 80 - #fhir/Address{:extension [#fhir/Extension{:url "url-120620" :value #fhir/code "code-120656"}]} 392 - #fhir/Address{:text "text-212402"} 136 - #fhir/Address{:line ["line-212441"]} 200 + #fhir/Address{} 56 - #fhir/Reference{} 56 + #fhir/Reference{} 32 - #fhir/Meta{} 64 - #fhir/Meta{:profile [#fhir/canonical "foo"]} 248 + #fhir/Meta{} 0 + #fhir/Meta{:profile [#fhir/canonical "foo"]} 0 - #fhir/BundleEntrySearch{} 48) + #fhir.Bundle.entry/search{} 24) (testing "interning" (are [x y] (= (mem/total-size x) (mem/total-size x y)) - #fhir/Address{:extension [#fhir/Extension{:url "foo" :value #fhir/code "bar"}]} - #fhir/Address{:extension [#fhir/Extension{:url "foo" :value #fhir/code "bar"}]} - #fhir/Meta{:profile [#fhir/canonical "foo"]} #fhir/Meta{:profile [#fhir/canonical "foo"]} - #fhir/Coding{:system #fhir/uri "foo" :code #fhir/code "bar"} - #fhir/Coding{:system #fhir/uri "foo" :code #fhir/code "bar"})) - - (testing "instant" - (testing "backed by OffsetDateTime, taking into account shared offsets" - (is (= 112 (- (mem/total-size #fhir/instant "2020-01-01T00:00:00+02:00") - (mem/total-size ZoneOffset/UTC))))) - (testing "backed by java.time.Instant" - (is (= 24 (mem/total-size Instant/EPOCH))))) - - (testing "dateTime" - (testing "instance size taking into account shared offsets" - (is (= 96 (- (mem/total-size #fhir/dateTime "2020-01-01T00:00:00Z") - (mem/total-size ZoneOffset/UTC)))))) - - (testing "Meta" - (testing "two interned instances take the same memory as one" - (is (= 248 (mem/total-size #fhir/Meta{:profile [#fhir/canonical "foo"]} - #fhir/Meta{:profile [#fhir/canonical "foo"]})))))) - -(deftest extension-url-test - (testing "conformed instance size" - (testing "JSON" - (testing "two interned instances take the same memory as one" - (is (= (mem/total-size "foo") - (mem/total-size - (s2/conform :fhir.json.Extension/url (String. "foo")) - (s2/conform :fhir.json.Extension/url (String. "foo"))))))) - - (testing "CBOR" - (testing "two interned instances take the same memory as one" - (is (= (mem/total-size "foo") - (mem/total-size - (s2/conform :fhir.cbor.Extension/url (String. "foo")) - (s2/conform :fhir.cbor.Extension/url (String. "foo"))))))))) - -(deftest extension-test - (testing "conformed instance size" - (testing "JSON" - (are [json size] (= size (mem/total-size (s2/conform :fhir.json/Extension json))) - {} 48 - {:url "foo" :valueCode "bar"} 216) - - (testing "two instances have only the 48 byte instance overhead" - (is (= (+ (mem/total-size - (s2/conform :fhir.json/Extension - {:url "foo" :valueString "bar"})) - 48) - (mem/total-size - (s2/conform :fhir.json/Extension - {:url (String. "foo") :valueString "bar"}) - (s2/conform :fhir.json/Extension - {:url (String. "foo") :valueString "bar"}))))) - - (testing "two instances with code values take the same amount of memory as one" - (is (= (mem/total-size - (s2/conform :fhir.json/Extension - {:url "foo" :valueCode "bar"})) - (mem/total-size - (s2/conform :fhir.json/Extension - {:url (String. "foo") :valueCode "bar"}) - (s2/conform :fhir.json/Extension - {:url (String. "foo") :valueCode "bar"})))))))) - -(deftest coding-test - (testing "conformed instance size" - (testing "JSON" - (are [json size] (= size (mem/total-size (s2/conform :fhir.json/Coding json))) - {} 56 - {:system "foo" :code "bar"} 296) - - (testing "two interned instances take the same memory as one" - (is (= 296 (mem/total-size (s2/conform :fhir.json/Coding {:system "foo" :code "bar"}) - (s2/conform :fhir.json/Coding {:system "foo" :code "bar"})))))))) - -(deftest quantity-unit-test - (testing "conformed instance size" - (testing "JSON" - (testing "two interned instances take the same memory as one" - (is (= (mem/total-size "foo") - (mem/total-size - (s2/conform :fhir.json.Quantity/unit (String. "foo")) - (s2/conform :fhir.json.Quantity/unit (String. "foo"))))))) - - (testing "CBOR" - (testing "two interned instances take the same memory as one" - (is (= (mem/total-size "foo") - (mem/total-size - (s2/conform :fhir.cbor.Quantity/unit (String. "foo")) - (s2/conform :fhir.cbor.Quantity/unit (String. "foo"))))))))) - -(deftest human-name-test - (testing "conformed instance size" - (testing "JSON" - (are [json size] (= size (mem/total-size (s2/conform :fhir.json/HumanName json))) - {} 64 - {:use "usual"} 184 - {:given ["given-212441"]} 184)) - - (testing "CBOR" - (are [cbor size] (= size (mem/total-size (s2/conform :fhir.cbor/HumanName cbor))) - {} 64 - {:use "usual"} 184 - {:given ["given-212441"]} 184)))) - -(deftest address-test - (testing "conformed instance size" - (are [json size] (= size (mem/total-size (s2/conform :fhir.json/Address json))) - {} 80 - {:extension [{:url "foo1foo1" :valueCode "bar"}]} 360 - {:extension [{:url (String. "foo") :valueCode (String. "bar")} - {:url (String. "foo") :valueCode (String. "bar")}]} 360 - {:text "text-212402"} 136 - {:line ["line-212441"]} 200))) - -(deftest meta-test - (testing "conformed instance size" - (are [json size] (= size (mem/total-size (s2/conform :fhir.json/Meta json))) - {} 64 - {:versionId "1"} 128 - {:profile ["foo"]} 248) - - (testing "two interned instances take the same memory as one" - (is (= 248 (mem/total-size (s2/conform :fhir.json/Meta {:profile ["foo"]}) - (s2/conform :fhir.json/Meta {:profile ["foo"]}))))))) + #fhir/Extension{:url "url-191107"} + #fhir/Extension{:url "url-191107"} + + (parse-json "Extension" {:url "url-191107"}) + (parse-json "Extension" {:url "url-191107"}) + + (parse-json "Quantity" {:unit "unit-191622"}) + (parse-json "Quantity" {:unit "unit-191622"}) + + (parse-json "Coding" {:display "display-191622"}) + (parse-json "Coding" {:display "display-191622"}) + + #fhir/Coding{:system #fhir/uri-interned "foo" :code #fhir/code "bar"} + #fhir/Coding{:system #fhir/uri-interned "foo" :code #fhir/code "bar"})) + + (testing "Observation" + (are [x excludes size] (= (apply mem/total-size-exclude x excludes) (Base/memSize x) size) + (parse-json {:resourceType "Observation" :id "192116"}) + [:fhir/type :fhir/Observation :id] + 104 + + (parse-json {:resourceType "Observation" :status "final"}) + [:fhir/type :fhir/Observation :status #fhir/code "final"] + 56 + + (parse-json {:resourceType "Observation" :category [{:text "vital-signs"}]}) + [:fhir/type :fhir/Observation :category [#fhir/CodeableConcept{:text #fhir/string-interned "vital-signs"}]] + 112))) diff --git a/modules/fhir-structure/test-perf/blaze/fhir/spec_test_perf.clj b/modules/fhir-structure/test-perf/blaze/fhir/spec_test_perf.clj index a2fdb5b63..603027ceb 100644 --- a/modules/fhir-structure/test-perf/blaze/fhir/spec_test_perf.clj +++ b/modules/fhir-structure/test-perf/blaze/fhir/spec_test_perf.clj @@ -31,23 +31,23 @@ (apply format "%.3f µs <> %.3f µs" (map #(* % 1e6) (second (:mean (criterium/benchmark (read-json type x) {})))))) (comment - ;; 0,168 µs <> 0,169 µs - (bench-write-json #fhir/HumanName{:family "Doe" :given ["John"]}) + ;; 0,154 µs <> 0,155 µs + (bench-write-json #fhir/HumanName{:family #fhir/string "Doe" :given [#fhir/string "John"]}) - ;; 0,145 µs <> 0,146 µs + ;; 0,131 µs <> 0,131 µs (bench-write-json #fhir/CodeableConcept {:coding [#fhir/Coding - {:system #fhir/uri "http://loinc.org" + {:system #fhir/uri-interned "http://loinc.org" :code #fhir/code "17861-6"}]}) - ;; 2,380 µs <> 2,393 µs + ;; 1,976 µs <> 1,977 µs (bench-write-json {:fhir/type :fhir/Observation :id "DACG22233TWT7CK4" :meta #fhir/Meta {:versionId #fhir/id "481283" - :lastUpdated #fhir/instant "2022-04-20T11:58:38.070Z" + :lastUpdated #fhir/instant #system/date-time "2022-04-20T11:58:38.070Z" :profile [#fhir/canonical "http://hl7.org/fhir/StructureDefinition/bmi" #fhir/canonical "http://hl7.org/fhir/StructureDefinition/vitalsigns"]} :status #fhir/code "final" @@ -55,24 +55,24 @@ [#fhir/CodeableConcept {:coding [#fhir/Coding - {:system #fhir/uri "http://terminology.hl7.org/CodeSystem/observation-category" + {:system #fhir/uri-interned "http://terminology.hl7.org/CodeSystem/observation-category" :code #fhir/code "vital-signs" - :display #fhir/string "vital-signs"}]}] + :display #fhir/string-interned "vital-signs"}]}] :code #fhir/CodeableConcept - {:coding [#fhir/Coding{:system #fhir/uri "http://loinc.org" + {:coding [#fhir/Coding{:system #fhir/uri-interned "http://loinc.org" :code #fhir/code "39156-5" - :display #fhir/string "Body Mass Index"}] - :text "Body Mass Index"} + :display #fhir/string-interned "Body Mass Index"}] + :text #fhir/string "Body Mass Index"} :subject #fhir/Reference{:reference #fhir/string "Patient/DACG22233TWT7CKL"} - :effective #fhir/dateTime "2013-01-04T23:45:50Z" - :issued #fhir/instant "2013-01-04T23:45:50.072Z" + :effective #fhir/dateTime #system/date-time "2013-01-04T23:45:50Z" + :issued #fhir/instant #system/date-time "2013-01-04T23:45:50.072Z" :value #fhir/Quantity - {:value 14.97M - :unit "kg/m2" - :system #fhir/uri "http://unitsofmeasure.org" + {:value #fhir/decimal 14.97M + :unit #fhir/string "kg/m2" + :system #fhir/uri-interned "http://unitsofmeasure.org" :code #fhir/code "kg/m2"}}) - ;; 2,770 µs <> 2,773 µs + ;; 2,233 µs <> 2,240 µs (bench-write-json {:fhir/type :fhir.Bundle/entry :fullUrl "http://localhost:8080/fhir/Observation/DACG22233TWT7CK4" @@ -80,7 +80,7 @@ {:fhir/type :fhir/Observation :id "DACG22233TWT7CK4" :meta #fhir/Meta {:versionId #fhir/id "481283" - :lastUpdated #fhir/instant "2022-04-20T11:58:38.070Z" + :lastUpdated #fhir/instant #system/date-time "2022-04-20T11:58:38.070Z" :profile [#fhir/canonical "http://hl7.org/fhir/StructureDefinition/bmi" #fhir/canonical "http://hl7.org/fhir/StructureDefinition/vitalsigns"]} :status #fhir/code "final" @@ -88,25 +88,25 @@ [#fhir/CodeableConcept {:coding [#fhir/Coding - {:system #fhir/uri "http://terminology.hl7.org/CodeSystem/observation-category" + {:system #fhir/uri-interned "http://terminology.hl7.org/CodeSystem/observation-category" :code #fhir/code "vital-signs" - :display #fhir/string "vital-signs"}]}] + :display #fhir/string-interned "vital-signs"}]}] :code #fhir/CodeableConcept - {:coding [#fhir/Coding{:system #fhir/uri "http://loinc.org" + {:coding [#fhir/Coding{:system #fhir/uri-interned "http://loinc.org" :code #fhir/code "39156-5" - :display #fhir/string "Body Mass Index"}] - :text "Body Mass Index"} + :display #fhir/string-interned "Body Mass Index"}] + :text #fhir/string "Body Mass Index"} :subject #fhir/Reference{:reference #fhir/string "Patient/DACG22233TWT7CKL"} - :effective #fhir/dateTime "2013-01-04T23:45:50Z" - :issued #fhir/instant "2013-01-04T23:45:50.072Z" + :effective #fhir/dateTime #system/date-time "2013-01-04T23:45:50Z" + :issued #fhir/instant #system/date-time "2013-01-04T23:45:50.072Z" :value #fhir/Quantity - {:value 14.97M - :unit "kg/m2" - :system #fhir/uri "http://unitsofmeasure.org" + {:value #fhir/decimal 14.97M + :unit #fhir/string "kg/m2" + :system #fhir/uri-interned "http://unitsofmeasure.org" :code #fhir/code "kg/m2"}} - :search #fhir/BundleEntrySearch{:mode #fhir/code "match"}}) + :search #fhir.Bundle.entry/search{:mode #fhir/code "match"}}) - ;; 548,383 µs <> 549,193 µs + ;; 538,660 µs <> 539,236 µs (bench-write-json (read-json "Bundle" (slurp kds-bundle-filename))) ;; Read Performance diff --git a/modules/fhir-structure/test/blaze/fhir/spec/generators.clj b/modules/fhir-structure/test/blaze/fhir/spec/generators.clj index e0a3edb1e..76702823b 100644 --- a/modules/fhir-structure/test/blaze/fhir/spec/generators.clj +++ b/modules/fhir-structure/test/blaze/fhir/spec/generators.clj @@ -1,5 +1,5 @@ (ns blaze.fhir.spec.generators - (:refer-clojure :exclude [boolean meta str time]) + (:refer-clojure :exclude [boolean count meta range str time]) (:require [blaze.fhir.spec.type :as type] [blaze.fhir.spec.type.system :as system] @@ -32,15 +32,15 @@ (def ^:private char-printable-whitespace (gen/fmap char (gen/one-of [(gen/choose 9 10) (gen/return 13) (gen/choose 32 126) (gen/choose 160 255)]))) -(def ^:private char-printable - (gen/fmap char (gen/one-of [(gen/choose 32 126) (gen/choose 160 255)]))) - (def ^:private char-printable-non-blank (gen/fmap char (gen/one-of [(gen/choose 33 126) (gen/choose 161 255)]))) (def string-value (gen/fmap str/join (gen/vector char-printable-whitespace))) +(def large-string-value + (gen/fmap str/join (gen/vector char-printable-whitespace 5 100))) + (def decimal-value (gen/fmap #(BigDecimal/valueOf ^double %) (gen/double* {:infinite? false :NaN? false}))) @@ -86,34 +86,41 @@ (gen/one-of [(gen/return "Z") zone-offset])) (def instant-value - (gen/fmap (partial apply format "%04d-%02d-%02dT%02d:%02d:%02d%s") - (gen/tuple year month day hour minute time-second zone))) + (gen/fmap + system/parse-date-time + (gen/fmap (partial apply format "%04d-%02d-%02dT%02d:%02d:%02d%s") + (gen/tuple year month day hour minute time-second zone)))) (def date-value - (gen/one-of - [(gen/fmap (partial format "%04d") year) - (gen/fmap (partial apply format "%04d-%02d") - (gen/tuple year month)) - (gen/fmap (partial apply format "%04d-%02d-%02d") - (gen/tuple year month day))])) + (gen/fmap + system/parse-date + (gen/one-of + [(gen/fmap (partial format "%04d") year) + (gen/fmap (partial apply format "%04d-%02d") + (gen/tuple year month)) + (gen/fmap (partial apply format "%04d-%02d-%02d") + (gen/tuple year month day))]))) (defn dateTime-value [& {:keys [year] :or {year year}}] - (gen/one-of - [(gen/fmap (partial format "%04d") year) - (gen/fmap (partial apply format "%04d-%02d") - (gen/tuple year month)) - (gen/fmap (partial apply format "%04d-%02d-%02d") - (gen/tuple year month day)) - (gen/fmap (partial apply format "%04d-%02d-%02dT%02d:%02d:%02d%s") - (gen/tuple year month day hour minute time-second zone))])) + (gen/fmap + system/parse-date-time + (gen/one-of + [(gen/fmap (partial format "%04d") year) + (gen/fmap (partial apply format "%04d-%02d") + (gen/tuple year month)) + (gen/fmap (partial apply format "%04d-%02d-%02d") + (gen/tuple year month day)) + (gen/fmap (partial apply format "%04d-%02d-%02dT%02d:%02d:%02d%s") + (gen/tuple year month day hour minute time-second zone))]))) (def time-value - (gen/fmap (partial apply format "%02d:%02d:%02d") - (gen/tuple hour minute time-second))) + (gen/fmap + system/parse-time + (gen/fmap (partial apply format "%02d:%02d:%02d") + (gen/tuple hour minute time-second)))) (def code-value - (gen/such-that (partial re-matches #"(?U)[\p{Print}&&[^\p{Blank}]]+(\p{Blank}[\p{Print}&&[^\p{Blank}]]+)*") - (gen/fmap str/join (gen/vector char-printable)) 1000)) + (gen/fmap str/join (gen/vector char-printable-whitespace))) (def char-digit (gen/fmap char (gen/choose 48 57))) @@ -124,7 +131,7 @@ (gen/fmap str/join (gen/vector char-digit))))) (def id-value - (gen/such-that (partial re-matches #"[A-Za-z0-9\-\.]{1,64}") + (gen/such-that (partial re-matches #"[A-Za-z0-9\-\ .]{1,64}") (gen/fmap str/join (gen/vector gen/char-alphanumeric 1 64)) 1000)) @@ -150,7 +157,7 @@ m)) (defn- to-map [keys vals] - (gen/such-that seq (gen/fmap #(keep-vals (zipmap keys %)) vals) 1000)) + (gen/such-that seq (gen/fmap #(keep-vals (zipmap keys %)) vals) 100)) (declare extension) @@ -162,7 +169,7 @@ [& {:keys [id extension value] :or {id (often-nil id-value) extension (often-nil (extensions)) - value (rare-nil value-gen)}}] + value (nilable value-gen)}}] (->> (gen/tuple id extension value) (to-map [:id :extension :value]) (gen/fmap constructor)))) @@ -176,6 +183,9 @@ (def string (primitive-gen type/string string-value)) +(def large-string + (primitive-gen type/string large-string-value)) + (def decimal (primitive-gen type/decimal decimal-value)) @@ -224,6 +234,73 @@ (def uuid (primitive-gen type/uuid uuid-value)) +(declare coding) +(declare contact-point) +(declare data-requirement) +(declare dosage) +(declare duration) +(declare human-name) +(declare identifier) +(declare meta) +(declare money) +(declare parameter-definition) +(declare period) +(declare quantity) +(declare range) +(declare ratio) +(declare reference) +(declare related-artifact) +(declare sampled-data) +(declare signature) +(declare timing) +(declare trigger-definition) +(declare usage-context) + +(defn address + [& {:keys [id extension use type text line city district state postalCode + country period] + :or {id (often-nil id-value) + extension (extensions) + use (rare-nil (code)) + type (rare-nil (code)) + text (often-nil (string)) + line (gen/vector (string)) + city (rare-nil (string)) + district (rare-nil (string)) + state (rare-nil (string)) + postalCode (rare-nil (string)) + country (rare-nil (string)) + period (often-nil (period))}}] + (->> (gen/tuple id extension use type text line city district state postalCode + country period) + (to-map [:id :extension :use :type :text :line :city :district :state + :postalCode :country :period]) + (gen/fmap type/address))) + +(defn age + [& {:keys [id extension value comparator unit system code] + :or {id (often-nil id-value) + extension (extensions) + value (rare-nil (decimal)) + comparator (often-nil (code)) + unit (rare-nil (string)) + system (rare-nil (uri)) + code (rare-nil (code))}}] + (->> (gen/tuple id extension value comparator unit system code) + (to-map [:id :extension :value :comparator :unit :system :code]) + (gen/fmap type/age))) + +(defn annotation + [& {:keys [id extension author time text] + :or {id (often-nil id-value) + extension (gen/return nil) + author (rare-nil (gen/one-of [(reference) (string)])) + time (rare-nil (dateTime)) + text (rare-nil (markdown))}}] + (->> (gen/tuple id extension author time text) + (to-map [:id :extension :author :time :text]) + (gen/fmap type/annotation))) + (defn attachment [& {:keys [id extension contentType language data url size hash title creation] :or {id (often-nil id-value) @@ -242,14 +319,15 @@ :title :creation]) (gen/fmap type/attachment))) -(defn extension - [& {:keys [id extension value] +(defn codeable-concept + [& {:keys [id extension coding text] :or {id (often-nil id-value) - extension (gen/return nil) - value (gen/return nil)}}] - (->> (gen/tuple id extension uri-value value) - (to-map [:id :extension :url :value]) - (gen/fmap type/extension))) + extension (extensions) + coding (gen/vector (coding)) + text (often-nil (string))}}] + (->> (gen/tuple id extension coding text) + (to-map [:id :extension :coding :text]) + (gen/fmap type/codeable-concept))) (defn coding [& {:keys [id extension system version code display user-selected] @@ -264,17 +342,41 @@ (to-map [:id :extension :system :version :code :display :userSelected]) (gen/fmap type/coding))) -(defn codeable-concept - [& {:keys [id extension coding text] +(defn contact-detail + [& {:keys [id extension name telecom] :or {id (often-nil id-value) extension (extensions) - coding (gen/vector (coding)) - text (often-nil (string))}}] - (->> (gen/tuple id extension coding text) - (to-map [:id :extension :coding :text]) - (gen/fmap type/codeable-concept))) + name (rare-nil (string)) + telecom (gen/vector (contact-point) 0 3)}}] + (->> (gen/tuple id extension name telecom) + (to-map [:id :extension :name :telecom]) + (gen/fmap type/contact-detail))) + +(defn contact-point + [& {:keys [id extension system value use rank period] + :or {id (often-nil id-value) + extension (extensions) + system (rare-nil (code)) + value (rare-nil (string)) + use (nilable (code)) + rank (often-nil (positiveInt)) + period (rare-nil (period))}}] + (->> (gen/tuple id extension system value use rank period) + (to-map [:id :extension :system :value :use :rank :period]) + (gen/fmap type/contact-point))) + +(defn contributor + [& {:keys [id extension type name contact] + :or {id (often-nil id-value) + extension (extensions) + type (rare-nil (code)) + name (rare-nil (string)) + contact (gen/vector (contact-detail) 0 3)}}] + (->> (gen/tuple id extension type name contact) + (to-map [:id :extension :type :name :contact]) + (gen/fmap type/contributor))) -(defn quantity +(defn count [& {:keys [id extension value comparator unit system code] :or {id (often-nil id-value) extension (extensions) @@ -285,53 +387,199 @@ code (rare-nil (code))}}] (->> (gen/tuple id extension value comparator unit system code) (to-map [:id :extension :value :comparator :unit :system :code]) - (gen/fmap type/quantity))) - -;; TODO: Range + (gen/fmap type/count))) -(defn ratio - [& {:keys [id extension numerator denominator] +(defn data-requirement-code-filter + [& {:keys [id extension path searchParam valueSet code] :or {id (often-nil id-value) extension (extensions) - numerator (nilable (quantity)) - denominator (nilable (quantity))}}] - (->> (gen/tuple id extension numerator denominator) - (to-map [:id :extension :numerator :denominator]) - (gen/fmap type/ratio))) - -;; TODO: RatioRange + path (nilable (string)) + searchParam (nilable (string)) + valueSet (nilable (canonical)) + code (gen/vector (coding))}}] + (->> (gen/tuple id extension path searchParam valueSet code) + (to-map [:id :extension :path :searchParam :valueSet :code]) + (gen/fmap type/data-requirement-code-filter))) + +(defn data-requirement-date-filter + [& {:keys [id extension path searchParam value] + :or {id (often-nil id-value) + extension (extensions) + path (nilable (string)) + searchParam (nilable (string)) + value (nilable (gen/one-of [(dateTime) (period) (duration)]))}}] + (->> (gen/tuple id extension path searchParam value) + (to-map [:id :extension :path :searchParam :value]) + (gen/fmap type/data-requirement-date-filter))) + +(defn data-requirement-sort + [& {:keys [id extension path direction] + :or {id (often-nil id-value) + extension (extensions) + path (string) + direction (code)}}] + (->> (gen/tuple id extension path direction) + (to-map [:id :extension :path :direction]) + (gen/fmap type/data-requirement-sort))) + +(defn data-requirement + [& {:keys [id extension type profile subject mustSupport codeFilter dateFilter + limit sort] + :or {id (often-nil id-value) + extension (extensions) + type (code) + profile (gen/vector (canonical)) + subject (nilable (gen/one-of [(codeable-concept) (reference)])) + mustSupport (gen/vector (string)) + codeFilter (gen/vector (data-requirement-code-filter)) + dateFilter (gen/vector (data-requirement-date-filter)) + limit (nilable (positiveInt)) + sort (gen/vector (data-requirement-sort))}}] + (->> (gen/tuple id extension type profile subject mustSupport codeFilter + dateFilter limit sort) + (to-map [:id :extension :type :profile :subject :mustSupport :codeFilter + :dateFilter :limit :sort]) + (gen/fmap type/data-requirement))) + +(defn distance + [& {:keys [id extension value comparator unit system code] + :or {id (often-nil id-value) + extension (extensions) + value (rare-nil (decimal)) + comparator (often-nil (code)) + unit (rare-nil (string)) + system (rare-nil (uri)) + code (rare-nil (code))}}] + (->> (gen/tuple id extension value comparator unit system code) + (to-map [:id :extension :value :comparator :unit :system :code]) + (gen/fmap type/distance))) -(defn period - [& {:keys [id extension start end] +(defn duration + [& {:keys [id extension value comparator unit system code] :or {id (often-nil id-value) extension (extensions) - start (nilable (dateTime)) - end (nilable (dateTime))}}] - (as-> (gen/tuple id extension start end) x - (to-map [:id :extension :start :end] x) - (gen/such-that #(<= (system/date-time-lower-bound (type/value (:start %))) - (system/date-time-upper-bound (type/value (:end %)))) - x - 1000) - (gen/fmap type/period x))) + value (rare-nil (decimal)) + comparator (often-nil (code)) + unit (rare-nil (string)) + system (rare-nil (uri)) + code (rare-nil (code))}}] + (->> (gen/tuple id extension value comparator unit system code) + (to-map [:id :extension :value :comparator :unit :system :code]) + (gen/fmap type/duration))) -;; TODO: SampledData +(defn expression + [& {:keys [id extension description name language expression reference] + :or {id (often-nil id-value) + extension (extensions) + description (often-nil (string)) + name (often-nil (blaze.fhir.spec.generators/id)) + language (rare-nil (code)) + expression (rare-nil (string)) + reference (often-nil (uri))}}] + (->> (gen/tuple id extension description name language expression reference) + (to-map [:id :extension :description :name :language :expression :reference]) + (gen/fmap type/expression))) + +(defn extension-value [] + (gen/one-of + [(base64Binary) + (boolean) + (canonical) + (code) + (date) + (dateTime) + (decimal) + (id) + (instant) + (integer) + (markdown) + (oid) + (positiveInt) + (string) + (time) + (unsignedInt) + (uri) + (url) + (uuid) + (address) + (age) + (annotation) + (attachment) + (codeable-concept) + (coding) + (contact-point) + (count) + (data-requirement) + (distance) + (duration) + (human-name) + (identifier) + (money) + (period) + (quantity) + (range) + (ratio) + (reference) + (sampled-data) + (signature) + (timing) + (contact-detail) + (contributor) + (expression) + (parameter-definition) + (related-artifact) + (trigger-definition) + (usage-context) + (dosage) + (meta)])) -(declare reference) +(defn extension + [& {:keys [id extension value] + :or {id (often-nil id-value) + extension (gen/return nil) + value (gen/return nil)}}] + (->> (gen/tuple id extension uri-value value) + (to-map [:id :extension :url :value]) + (gen/fmap type/extension))) -(defn identifier - [& {:keys [id extension use type system value period assigner] +(defn dosage-dose-and-rate + [& {:keys [id extension type dose rate] :or {id (often-nil id-value) extension (extensions) - use (rare-nil (code)) type (nilable (codeable-concept)) - system (rare-nil (uri)) - value (rare-nil (string)) - period (often-nil (period)) - assigner (gen/return nil)}}] - (->> (gen/tuple id extension use type system value period assigner) - (to-map [:id :extension :use :type :system :value :period :assigner]) - (gen/fmap type/identifier))) + dose (nilable (gen/one-of [(range) (quantity)])) + rate (nilable (gen/one-of [(ratio) (range) (quantity)]))}}] + (->> (gen/tuple id extension type dose rate) + (to-map [:id :extension :type :dose :rate]) + (gen/fmap type/dosage-dose-and-rate))) + +(defn dosage + [& {:keys [id extension modifierExtension sequence text additionalInstruction + patientInstruction timing asNeeded site route method doseAndRate + maxDosePerPeriod maxDosePerAdministration maxDosePerLifetime] + :or {id (often-nil id-value) + extension (extensions) + modifierExtension (extensions) + sequence (nilable (integer)) + text (nilable (string)) + additionalInstruction (gen/vector (codeable-concept)) + patientInstruction (nilable (string)) + timing (nilable (timing)) + asNeeded (nilable (gen/one-of [(boolean) (codeable-concept)])) + site (nilable (codeable-concept)) + route (nilable (codeable-concept)) + method (nilable (codeable-concept)) + doseAndRate (gen/vector (dosage-dose-and-rate)) + maxDosePerPeriod (nilable (ratio)) + maxDosePerAdministration (nilable (quantity)) + maxDosePerLifetime (nilable (quantity))}}] + (->> (gen/tuple id extension modifierExtension sequence text additionalInstruction + patientInstruction timing asNeeded site route method doseAndRate + maxDosePerPeriod maxDosePerAdministration maxDosePerLifetime) + (to-map [:id :extension :modifierExtension :sequence :text :additionalInstruction + :patientInstruction :timing :asNeeded :site :route :method :doseAndRate + :maxDosePerPeriod :maxDosePerAdministration :maxDosePerLifetime]) + (gen/fmap type/dosage))) (defn human-name [& {:keys [id extension use text family given prefix suffix period] @@ -348,34 +596,106 @@ (to-map [:id :extension :use :text :family :given :prefix :suffix :period]) (gen/fmap type/human-name))) -(defn address - [& {:keys [id extension use type text line city district state postalCode - country period] +(defn identifier + [& {:keys [id extension use type system value period assigner] :or {id (often-nil id-value) extension (extensions) use (rare-nil (code)) + type (nilable (codeable-concept)) + system (rare-nil (uri)) + value (rare-nil (string)) + period (often-nil (period)) + assigner (gen/return nil)}}] + (->> (gen/tuple id extension use type system value period assigner) + (to-map [:id :extension :use :type :system :value :period :assigner]) + (gen/fmap type/identifier))) + +(defn meta + [& {:keys [id extension versionId lastUpdated source profile security tag] + :or {id (often-nil id-value) + extension (extensions) + versionId (rare-nil (blaze.fhir.spec.generators/id)) + lastUpdated (rare-nil (instant)) + source (nilable (uri)) + profile (gen/vector (canonical)) + security (gen/vector (coding)) + tag (gen/vector (coding))}}] + (->> (gen/tuple id extension versionId lastUpdated source profile security tag) + (to-map [:id :extension :versionId :lastUpdated :source :profile + :security :tag]) + (gen/fmap type/meta))) + +(defn money + [& {:keys [id extension value currency] + :or {id (often-nil id-value) + extension (extensions) + value (rare-nil (decimal)) + currency (rare-nil (code))}}] + (->> (gen/tuple id extension value currency) + (to-map [:id :extension :value :currency]) + (gen/fmap type/money))) + +(defn parameter-definition + [& {:keys [id extension name use min max documentation type profile] + :or {id (often-nil id-value) + extension (extensions) + name (rare-nil (code)) + use (rare-nil (code)) + min (rare-nil (integer)) + max (rare-nil (string)) + documentation (rare-nil (string)) type (rare-nil (code)) - text (often-nil (string)) - line (gen/vector (string)) - city (rare-nil (string)) - district (rare-nil (string)) - state (rare-nil (string)) - postalCode (rare-nil (string)) - country (rare-nil (string)) - period (often-nil (period))}}] - (->> (gen/tuple id extension use type text line city district state postalCode - country period) - (to-map [:id :extension :use :type :text :line :city :district :state - :postalCode :country :period]) - (gen/fmap type/address))) + profile (rare-nil (canonical))}}] + (->> (gen/tuple id extension name use min max documentation type profile) + (to-map [:id :extension :name :use :min :max :documentation :type :profile]) + (gen/fmap type/parameter-definition))) -;; TODO: ContactPoint +(defn period + [& {:keys [id extension start end] + :or {id (often-nil id-value) + extension (extensions) + start (nilable (dateTime)) + end (nilable (dateTime))}}] + (as-> (gen/tuple id extension start end) x + (to-map [:id :extension :start :end] x) + (gen/such-that #(<= (system/date-time-lower-bound (:value (:start %))) + (system/date-time-upper-bound (:value (:end %)))) + x + 100) + (gen/fmap type/period x))) -;; TODO: Timing +(defn quantity + [& {:keys [id extension value comparator unit system code] + :or {id (often-nil id-value) + extension (extensions) + value (rare-nil (decimal)) + comparator (often-nil (code)) + unit (rare-nil (string)) + system (rare-nil (uri)) + code (rare-nil (code))}}] + (->> (gen/tuple id extension value comparator unit system code) + (to-map [:id :extension :value :comparator :unit :system :code]) + (gen/fmap type/quantity))) -;; TODO: Signature +(defn range + [& {:keys [id extension low high] + :or {id (often-nil id-value) + extension (extensions) + low (nilable (quantity)) + high (nilable (quantity))}}] + (->> (gen/tuple id extension low high) + (to-map [:id :extension :low :high]) + (gen/fmap type/range))) -;; TODO: Annotation +(defn ratio + [& {:keys [id extension numerator denominator] + :or {id (often-nil id-value) + extension (extensions) + numerator (nilable (quantity)) + denominator (nilable (quantity))}}] + (->> (gen/tuple id extension numerator denominator) + (to-map [:id :extension :numerator :denominator]) + (gen/fmap type/ratio))) (defn reference [& {:keys [id extension reference type identifier display] @@ -389,20 +709,123 @@ (to-map [:id :extension :reference :type :identifier :display]) (gen/fmap type/reference))) -(defn meta - [& {:keys [id extension versionId lastUpdated source profile security tag] +(defn related-artifact + [& {:keys [id extension type label display citation url document resource] :or {id (often-nil id-value) extension (extensions) - versionId (rare-nil (blaze.fhir.spec.generators/id)) - lastUpdated (rare-nil (instant)) - source (nilable (uri)) - profile (gen/vector (canonical)) - security (gen/vector (coding)) - tag (gen/vector (coding))}}] - (->> (gen/tuple id extension versionId lastUpdated source profile security tag) - (to-map [:id :extension :versionId :lastUpdated :source :profile - :security :tag]) - (gen/fmap type/meta))) + type (rare-nil (code)) + label (nilable (string)) + display (nilable (string)) + citation (often-nil (markdown)) + url (nilable (url)) + document (nilable (attachment)) + resource (nilable (canonical))}}] + (->> (gen/tuple id extension type label display citation url document resource) + (to-map [:id :extension :type :label :display :citation :url :document :resource]) + (gen/fmap type/related-artifact))) + +(defn sampled-data + [& {:keys [id extension origin period factor lowerLimit upperLimit dimensions data] + :or {id (often-nil id-value) + extension (extensions) + origin (quantity :comparator (gen/return nil)) + period (decimal) + factor (nilable (decimal)) + lowerLimit (nilable (decimal)) + upperLimit (nilable (decimal)) + dimensions (positiveInt) + data (nilable (string))}}] + (->> (gen/tuple id extension origin period factor lowerLimit upperLimit dimensions data) + (to-map [:id :extension :origin :period :factor :lowerLimit :upperLimit :dimensions :data]) + (gen/fmap type/sampled-data))) + +(defn signature + [& {:keys [id extension type when who onBehalfOf targetFormat sigFormat data] + :or {id (often-nil id-value) + extension (extensions) + type (gen/vector (coding)) + when (rare-nil (instant)) + who (rare-nil (reference)) + onBehalfOf (nilable (reference)) + targetFormat (nilable (code)) + sigFormat (nilable (code)) + data (nilable (base64Binary))}}] + (->> (gen/tuple id extension type when who onBehalfOf targetFormat sigFormat data) + (to-map [:id :extension :type :when :who :onBehalfOf :targetFormat :sigFormat :data]) + (gen/fmap type/signature))) + +(defn timing-repeat-bounds [] + (gen/one-of [(duration) (range) (period)])) + +(defn timing-repeat + [& {:keys [id extension bounds count countMax duration durationMax durationUnit + frequency frequencyMax period periodMax periodUnit dayOfWeek timeOfDay + when offset] + :or {id (often-nil id-value) + extension (extensions) + bounds (nilable (timing-repeat-bounds)) + count (nilable (positiveInt)) + countMax (nilable (positiveInt)) + duration (nilable (decimal)) + durationMax (nilable (decimal)) + durationUnit (nilable (code)) + frequency (nilable (positiveInt)) + frequencyMax (nilable (positiveInt)) + period (nilable (decimal)) + periodMax (nilable (decimal)) + periodUnit (nilable (code)) + dayOfWeek (gen/vector (code)) + timeOfDay (gen/vector (time)) + when (gen/vector (code)) + offset (nilable (unsignedInt))}}] + (->> (gen/tuple id extension bounds count countMax duration durationMax + durationUnit frequency frequencyMax period periodMax periodUnit + dayOfWeek timeOfDay when offset) + (to-map [:id :extension :bounds :count :countMax :duration :durationMax + :durationUnit :frequency :frequencyMax :period :periodMax + :periodUnit :dayOfWeek :timeOfDay :when :offset]) + (gen/fmap type/timing-repeat))) + +(defn timing + [& {:keys [id extension modifierExtension event repeat code] + :or {id (often-nil id-value) + extension (extensions) + modifierExtension (extensions) + event (gen/vector (dateTime)) + repeat (nilable (timing-repeat)) + code (nilable (codeable-concept))}}] + (->> (gen/tuple id extension modifierExtension event repeat code) + (to-map [:id :extension :modifierExtension :event :repeat :code]) + (gen/fmap type/timing))) + +(defn- trigger-definition-timing [] + (gen/one-of [(timing) (reference) (date) (dateTime)])) + +(defn trigger-definition + [& {:keys [id extension type name timing data condition] + :or {id (often-nil id-value) + extension (extensions) + type (code) + name (nilable (string)) + timing (nilable (trigger-definition-timing)) + data (gen/vector (data-requirement)) + condition (nilable (expression))}}] + (->> (gen/tuple id extension type name timing data condition) + (to-map [:id :extension :type :name :timing :data :condition]) + (gen/fmap type/trigger-definition))) + +(defn- usage-context-value [] + (gen/one-of [(codeable-concept) (quantity) (range) (reference)])) + +(defn usage-context + [& {:keys [id extension code value] + :or {id (often-nil id-value) + extension (extensions) + code (coding) + value (usage-context-value)}}] + (->> (gen/tuple id extension code value) + (to-map [:id :extension :code :value]) + (gen/fmap type/usage-context))) (defn bundle-entry-search [& {:keys [id extension mode score] @@ -439,95 +862,29 @@ (to-map [~@(map keyword field-syms)]) (fhir-type ~(keyword "fhir" (kebab->pascal (str type)))))))) -(def-resource-gen bundle - [id id-value - identifier (identifier) - type (rare-nil (code)) - entry (gen/vector (bundle-entry))]) - -(def-resource-gen patient +(def-resource-gen activity-definition [id id-value - gender (rare-nil (code)) - birthDate (rare-nil (date)) - multipleBirth (rare-nil (gen/one-of [(boolean) (integer)]))]) - -(defn- observation-value [] - (gen/one-of [(quantity) (codeable-concept) (string) (boolean) (integer) - #_(range) #_(ratio) #_(sampled-data) (time) (dateTime) (period)])) - -(def-resource-gen observation - [id id-value - meta (meta) identifier (gen/vector (identifier)) - status (rare-nil (code)) - category (gen/vector (codeable-concept)) - code (rare-nil (codeable-concept)) - subject (rare-nil (reference :reference (gen/return nil))) - encounter (rare-nil (reference :reference (gen/return nil))) - effective (rare-nil (gen/one-of [(dateTime) (period)])) - performer (gen/vector (reference :reference (gen/return nil))) - value (rare-nil (observation-value))]) - -(def-resource-gen encounter - [id id-value - meta (meta) - identifier (gen/vector (identifier)) - status (rare-nil (code)) - type (gen/vector (codeable-concept)) - priority (rare-nil (codeable-concept)) - subject (rare-nil (reference :reference (gen/return nil))) - period (rare-nil (period))]) - -(def-resource-gen procedure - [id id-value - meta (meta) - identifier (gen/vector (identifier)) - instantiatesCanonical (gen/vector (canonical)) - instantiatesUri (gen/vector (uri)) - status (rare-nil (code)) - category (codeable-concept) - code (rare-nil (codeable-concept)) - subject (rare-nil (reference :reference (gen/return nil))) - encounter (rare-nil (reference :reference (gen/return nil)))]) + relatedArtifact (gen/vector (related-artifact)) + timing (nilable (gen/one-of [(timing) (dateTime) (age) (period) (range) (duration)])) + dosage (gen/vector (dosage))]) (def-resource-gen allergy-intolerance [id id-value - meta (meta) + meta (rare-nil (meta)) category (gen/vector (code))]) -(def-resource-gen medication-administration - [id id-value - meta (meta) - medication (rare-nil (reference :reference (gen/return nil))) - subject (rare-nil (reference :reference (gen/return nil)))]) - -(def-resource-gen diagnostic-report +(def-resource-gen bundle [id id-value - meta (meta) - identifier (gen/vector (identifier)) - status (rare-nil (code)) - category (gen/vector (codeable-concept)) - code (rare-nil (codeable-concept)) - subject (rare-nil (reference :reference (gen/return nil))) - encounter (rare-nil (reference :reference (gen/return nil))) - effective (rare-nil (gen/one-of [(dateTime) (period)])) - issued (nilable (instant)) - performer (gen/vector (reference :reference (gen/return nil)))]) + identifier (identifier) + type (rare-nil (code)) + entry (gen/vector (bundle-entry))]) -(def-resource-gen library +(def-resource-gen claim [id id-value - meta (meta) - url (uri) identifier (gen/vector (identifier)) - version (rare-nil (string)) - name (nilable (string)) - title (nilable (string)) - subtitle (nilable (string)) status (rare-nil (code)) - experimental (nilable (boolean)) - type (codeable-concept) - subject (rare-nil (gen/one-of [(codeable-concept) (reference :reference (gen/return nil))])) - content (gen/vector (attachment))]) + total (nilable (money))]) (defn- code-system-concept [& {:keys [code] @@ -538,7 +895,7 @@ (def-resource-gen code-system [id id-value - meta (meta) + meta (rare-nil (meta)) url (uri) identifier (gen/vector (identifier)) version (rare-nil (string)) @@ -548,41 +905,14 @@ experimental (nilable (boolean)) concept (gen/vector (code-system-concept))]) -(defn- value-set-compose - [& {:keys [inactive] - :or {inactive (often-nil (boolean))}}] - (->> (gen/tuple inactive) - (to-map [:inactive]) - (fhir-type :fhir.ValueSet/compose))) +(defn condition-onset [] + (gen/one-of [(dateTime) (age) (period) (range) (string)])) -(def-resource-gen value-set +(def-resource-gen condition [id id-value - meta (meta) - url (uri) + meta (rare-nil (meta)) identifier (gen/vector (identifier)) - version (rare-nil (string)) - name (nilable (string)) - title (nilable (string)) - status (rare-nil (code)) - experimental (nilable (boolean)) - compose (value-set-compose)]) - -(defn- task-value [] - (gen/one-of [(quantity) (codeable-concept) (string) (boolean) (integer) - #_(range) #_(ratio) #_(sampled-data) (time) (dateTime) (period)])) - -(defn- task-input - [& {:keys [type value] - :or {type (codeable-concept) - value (task-value)}}] - (->> (gen/tuple type value) - (to-map [:type :value]) - (fhir-type :fhir.Task/input))) - -(def-resource-gen task - [identifier (gen/vector (identifier)) - status (rare-nil (code)) - input (gen/vector (task-input))]) + onset (nilable (condition-onset))]) (defn consent-policy [& {:keys [id extension authority uri] @@ -647,9 +977,265 @@ (fhir-type :fhir.Consent/provision))) (def-resource-gen consent - [identifier (gen/vector (identifier)) + [id id-value + identifier (gen/vector (identifier)) status (code) policy (gen/vector (consent-policy)) policyRule (nilable (codeable-concept)) verification (gen/vector (consent-verification)) provision (nilable (consent-provision {:provision (gen/vector (consent-provision))}))]) + +(def-resource-gen diagnostic-report + [id id-value + meta (rare-nil (meta)) + identifier (gen/vector (identifier)) + status (rare-nil (code)) + category (gen/vector (codeable-concept)) + code (rare-nil (codeable-concept)) + subject (rare-nil (reference :reference (gen/return nil))) + encounter (rare-nil (reference :reference (gen/return nil))) + effective (rare-nil (gen/one-of [(dateTime) (period)])) + issued (nilable (instant)) + performer (gen/vector (reference :reference (gen/return nil)))]) + +(def-resource-gen encounter + [id id-value + meta (rare-nil (meta)) + identifier (gen/vector (identifier)) + status (rare-nil (code)) + type (gen/vector (codeable-concept)) + priority (rare-nil (codeable-concept)) + subject (rare-nil (reference :reference (gen/return nil))) + period (rare-nil (period))]) + +(defn- imaging-study-series-instance + [& {:keys [id extension modifierExtension uid sopClass number title] + :or {id (often-nil id-value) + extension (extensions) + modifierExtension (extensions) + uid (rare-nil (blaze.fhir.spec.generators/id)) + sopClass (rare-nil (coding)) + number (nilable (unsignedInt)) + title (nilable (string))}}] + (->> (gen/tuple id extension modifierExtension uid sopClass number title) + (to-map [:id :extension :modifierExtension :uid :sopClass :number :title]) + (fhir-type :fhir.ImagingStudy.series/instance))) + +(defn- imaging-study-series-performer + [& {:keys [id extension modifierExtension function actor] + :or {id (often-nil id-value) + extension (extensions) + modifierExtension (extensions) + function (nilable (codeable-concept)) + actor (reference)}}] + (->> (gen/tuple id extension modifierExtension function actor) + (to-map [:id :extension :modifierExtension :function :actor]) + (fhir-type :fhir.ImagingStudy.series/performer))) + +(defn- imaging-study-series + [& {:keys [id extension modifierExtension uid number modality description + numberOfInstances endpoint bodySite laterality specimen started + performer instance] + :or {id (often-nil id-value) + extension (extensions) + modifierExtension (extensions) + uid (rare-nil (blaze.fhir.spec.generators/id)) + number (nilable (unsignedInt)) + modality (coding) + description (nilable (string)) + numberOfInstances (nilable (unsignedInt)) + endpoint (gen/vector (reference)) + bodySite (nilable (coding)) + laterality (nilable (coding)) + specimen (gen/vector (reference)) + started (nilable (dateTime)) + performer (gen/vector (imaging-study-series-performer)) + instance (gen/vector (imaging-study-series-instance))}}] + (->> (gen/tuple id extension modifierExtension uid number modality description + numberOfInstances endpoint bodySite laterality specimen started + performer instance) + (to-map [:id :extension :modifierExtension :uid :number :modality :description + :numberOfInstances :endpoint :bodySite :laterality :specimen :started + :performer :instance]) + (fhir-type :fhir.ImagingStudy/series))) + +(def-resource-gen imaging-study + [id id-value + meta (rare-nil (meta)) + identifier (gen/vector (identifier)) + status (rare-nil (code)) + modality (gen/vector (coding)) + subject (rare-nil (reference :reference (gen/return nil))) + encounter (nilable (reference :reference (gen/return nil))) + started (nilable (dateTime)) + basedOn (gen/vector (reference :reference (gen/return nil))) + referrer (nilable (reference :reference (gen/return nil))) + interpreter (gen/vector (reference :reference (gen/return nil))) + endpoint (gen/vector (reference :reference (gen/return nil))) + numberOfSeries (nilable (unsignedInt)) + numberOfInstances (nilable (unsignedInt)) + procedureReference (nilable (reference :reference (gen/return nil))) + procedureCode (gen/vector (codeable-concept)) + location (nilable (reference :reference (gen/return nil))) + reasonCode (gen/vector (codeable-concept)) + reasonReference (gen/vector (reference :reference (gen/return nil))) + note (gen/vector (annotation)) + description (nilable (string)) + series (gen/vector (imaging-study-series))]) + +(def-resource-gen library + [id id-value + meta (rare-nil (meta)) + url (uri) + identifier (gen/vector (identifier)) + version (nilable (string)) + name (nilable (string)) + title (nilable (string)) + subtitle (nilable (string)) + status (rare-nil (code)) + experimental (nilable (boolean)) + type (codeable-concept) + subject (nilable (gen/one-of [(codeable-concept) (reference :reference (gen/return nil))])) + date (nilable (dateTime)) + publisher (nilable (string)) + contact (gen/vector (contact-detail)) + description (nilable (markdown)) + useContext (gen/vector (usage-context)) + jurisdiction (gen/vector (codeable-concept)) + purpose (nilable (markdown)) + usage (nilable (string)) + copyright (nilable (markdown)) + approvalDate (nilable (blaze.fhir.spec.generators/date)) + lastReviewDate (nilable (blaze.fhir.spec.generators/date)) + effectivePeriod (nilable (period)) + topic (gen/vector (codeable-concept)) + author (gen/vector (contact-detail)) + editor (gen/vector (contact-detail)) + reviewer (gen/vector (contact-detail)) + endorser (gen/vector (contact-detail)) + relatedArtifact (gen/vector (related-artifact)) + parameter (gen/vector (parameter-definition)) + dataRequirement (gen/vector (data-requirement)) + content (gen/vector (attachment))]) + +(def-resource-gen medication-administration + [id id-value + meta (rare-nil (meta)) + medication (rare-nil (reference :reference (gen/return nil))) + subject (rare-nil (reference :reference (gen/return nil)))]) + +(defn- observation-value [] + (gen/one-of [(quantity) (codeable-concept) (string) (boolean) (integer) + (range) (ratio) (sampled-data) (time) (dateTime) (period)])) + +(defn- observation-reference-range + [& {:keys [id extension modifierExtension low high type appliesTo age text] + :or {id (often-nil id-value) + extension (extensions) + modifierExtension (extensions) + low (nilable (quantity)) + high (nilable (quantity)) + type (nilable (codeable-concept)) + appliesTo (gen/vector (codeable-concept)) + age (nilable (range)) + text (nilable (string))}}] + (->> (gen/tuple id extension modifierExtension low high type appliesTo age text) + (to-map [:id :extension :modifierExtension :low :high :type :appliesTo :age :text]) + (fhir-type :fhir.Observation/referenceRange))) + +(defn- observation-component + [& {:keys [id extension modifierExtension code value dataAbsentReason interpretation referenceRange] + :or {id (often-nil id-value) + extension (extensions) + modifierExtension (extensions) + code (codeable-concept) + value (nilable (observation-value)) + dataAbsentReason (nilable (codeable-concept)) + interpretation (gen/vector (codeable-concept)) + referenceRange (gen/vector (observation-reference-range))}}] + (->> (gen/tuple id extension modifierExtension code value dataAbsentReason interpretation referenceRange) + (to-map [:id :extension :modifierExtension :code :value :dataAbsentReason :interpretation :referenceRange]) + (fhir-type :fhir.Observation/component))) + +(def-resource-gen observation + [id id-value + meta (rare-nil (meta)) + identifier (gen/vector (identifier)) + basedOn (gen/vector (reference :reference (gen/return nil))) + partOf (gen/vector (reference :reference (gen/return nil))) + status (rare-nil (code)) + category (gen/vector (codeable-concept)) + code (rare-nil (codeable-concept)) + subject (rare-nil (reference :reference (gen/return nil))) + focus (gen/vector (reference :reference (gen/return nil))) + encounter (nilable (reference :reference (gen/return nil))) + effective (nilable (gen/one-of [(dateTime) (period) (timing) (instant)])) + issued (nilable (instant)) + performer (gen/vector (reference :reference (gen/return nil))) + value (rare-nil (observation-value)) + dataAbsentReason (often-nil (codeable-concept)) + interpretation (gen/vector (codeable-concept)) + note (gen/vector (annotation)) + bodySite (nilable (codeable-concept)) + method (nilable (codeable-concept)) + specimen (nilable (reference :reference (gen/return nil))) + device (nilable (reference :reference (gen/return nil))) + referenceRange (gen/vector (observation-reference-range)) + hasMember (gen/vector (reference :reference (gen/return nil))) + derivedFrom (gen/vector (reference :reference (gen/return nil))) + component (gen/vector (observation-component))]) + +(def-resource-gen patient + [id id-value + gender (rare-nil (code)) + birthDate (rare-nil (date)) + multipleBirth (rare-nil (gen/one-of [(boolean) (integer)]))]) + +(def-resource-gen procedure + [id id-value + meta (rare-nil (meta)) + identifier (gen/vector (identifier)) + instantiatesCanonical (gen/vector (canonical)) + instantiatesUri (gen/vector (uri)) + status (rare-nil (code)) + category (codeable-concept) + code (rare-nil (codeable-concept)) + subject (rare-nil (reference :reference (gen/return nil))) + encounter (rare-nil (reference :reference (gen/return nil)))]) + +(defn- task-value [] + (gen/one-of [(quantity) (codeable-concept) (string) (boolean) (integer) + (range) (ratio) (sampled-data) (time) (dateTime) (period)])) + +(defn- task-input + [& {:keys [type value] + :or {type (codeable-concept) + value (task-value)}}] + (->> (gen/tuple type value) + (to-map [:type :value]) + (fhir-type :fhir.Task/input))) + +(def-resource-gen task + [id id-value + identifier (gen/vector (identifier)) + status (rare-nil (code)) + input (gen/vector (task-input))]) + +(defn- value-set-compose + [& {:keys [inactive] + :or {inactive (often-nil (boolean))}}] + (->> (gen/tuple inactive) + (to-map [:inactive]) + (fhir-type :fhir.ValueSet/compose))) + +(def-resource-gen value-set + [id id-value + meta (rare-nil (meta)) + url (uri) + identifier (gen/vector (identifier)) + version (rare-nil (string)) + name (nilable (string)) + title (nilable (string)) + status (rare-nil (code)) + experimental (nilable (boolean)) + compose (value-set-compose)]) diff --git a/modules/fhir-structure/test/blaze/fhir/spec/impl/intern_test.clj b/modules/fhir-structure/test/blaze/fhir/spec/impl/intern_test.clj deleted file mode 100644 index 8eaf17d85..000000000 --- a/modules/fhir-structure/test/blaze/fhir/spec/impl/intern_test.clj +++ /dev/null @@ -1,44 +0,0 @@ -(ns blaze.fhir.spec.impl.intern-test - (:require - [blaze.executors :as ex] - [blaze.fhir.spec.impl.intern :as intern] - [blaze.test-util :as tu] - [clojure.spec.test.alpha :as st] - [clojure.test :as test :refer [deftest is testing]]) - (:import - [java.util.concurrent CountDownLatch TimeUnit])) - -(set! *warn-on-reflection* true) -(st/instrument) - -(test/use-fixtures :each tu/fixture) - -(defrecord TestType [x]) - -(def identity-intern - (intern/intern-value ->TestType)) - -(deftest intern-test - (testing "both constructions lead to the same instance" - (is (identical? (identity-intern "a") (identity-intern "a")))) - - (testing "parallel construction" - (dotimes [x 100] - (let [n 100 - pool (ex/io-pool n "constructor-%d") - atoms (repeatedly n #(atom nil)) - latch (CountDownLatch. 1) - ready (CountDownLatch. n)] - (doseq [atom atoms] - (ex/execute! - pool - #(do (.countDown ready) - (.await latch) - (reset! atom (identity-intern x))))) - ;; wait for threads to be created - (.await ready) - (.countDown latch) - (ex/shutdown! pool) - (ex/await-termination pool 10 TimeUnit/SECONDS) - (let [value (identity-intern x)] - (is (every? #(identical? value (deref %)) atoms))))))) diff --git a/modules/fhir-structure/test/blaze/fhir/spec/impl_test.clj b/modules/fhir-structure/test/blaze/fhir/spec/impl_test.clj index 637dc2e06..8e974150f 100644 --- a/modules/fhir-structure/test/blaze/fhir/spec/impl_test.clj +++ b/modules/fhir-structure/test/blaze/fhir/spec/impl_test.clj @@ -2,10 +2,10 @@ (:require [blaze.fhir.spec.impl :as impl] [blaze.fhir.spec.impl-spec] - [blaze.fhir.spec.impl.specs :as specs] [blaze.fhir.spec.impl.xml :as xml] [blaze.fhir.spec.impl.xml-spec] [blaze.fhir.spec.type :as type] + [blaze.fhir.spec.type.system :as system] [blaze.fhir.structure-definition-repo :as sdr] [blaze.test-util :as tu] [clojure.alpha.spec :as s2] @@ -57,7 +57,7 @@ (fn [~'e] (xml/value-matches? "true|false" ~'e)) (s2/conformer xml/remove-character-content xml/set-extension-tag) (s2/schema {:content (s2/coll-of :fhir.xml/Extension)}) - (s2/conformer type/xml->Boolean type/to-xml))}]))) + (s2/conformer (xml/xml-constructor type/boolean system/parse-boolean) type/to-xml))}]))) (testing "Integer" (is (= (-> (primitive-type "integer") @@ -72,7 +72,7 @@ (fn [~'e] (xml/value-matches? "-?([0]|([1-9][0-9]*))" ~'e)) (s2/conformer xml/remove-character-content xml/set-extension-tag) (s2/schema {:content (s2/coll-of :fhir.xml/Extension)}) - (s2/conformer type/xml->Integer type/to-xml))}]))) + (s2/conformer (xml/xml-constructor type/integer system/parse-integer) type/to-xml))}]))) (testing "string" (is (= (-> (primitive-type "string") @@ -86,7 +86,7 @@ xml/element? (s2/conformer xml/remove-character-content xml/set-extension-tag) (s2/schema {:content (s2/coll-of :fhir.xml/Extension)}) - (s2/conformer type/xml->String type/to-xml))}]))) + (s2/conformer (xml/xml-constructor type/string identity) type/to-xml))}]))) (testing "Decimal" (is (= (-> (primitive-type "decimal") @@ -101,7 +101,7 @@ (fn [~'e] (xml/value-matches? "-?(0|[1-9][0-9]*)(\\.[0-9]+)?([eE][+-]?[0-9]+)?" ~'e)) (s2/conformer xml/remove-character-content xml/set-extension-tag) (s2/schema {:content (s2/coll-of :fhir.xml/Extension)}) - (s2/conformer type/xml->Decimal type/to-xml))}]))) + (s2/conformer (xml/xml-constructor type/decimal system/parse-decimal) type/to-xml))}]))) (testing "uri" (is (= (-> (impl/primitive-type->spec-defs (primitive-type "uri")) @@ -115,7 +115,7 @@ (fn [~'e] (xml/value-matches? "(?U)[\\p{Print}&&[^\\p{Blank}]]*" ~'e)) (s2/conformer xml/remove-character-content xml/set-extension-tag) (s2/schema {:content (s2/coll-of :fhir.xml/Extension)}) - (s2/conformer type/xml->Uri type/to-xml))}]))) + (s2/conformer (xml/xml-constructor type/uri identity) type/to-xml))}]))) (testing "canonical" (is (= (-> (impl/primitive-type->spec-defs (primitive-type "canonical")) @@ -129,7 +129,7 @@ (fn [~'e] (xml/value-matches? "(?U)[\\p{Print}&&[^\\p{Blank}]]*" ~'e)) (s2/conformer xml/remove-character-content xml/set-extension-tag) (s2/schema {:content (s2/coll-of :fhir.xml/Extension)}) - (s2/conformer type/xml->Canonical type/to-xml))}]))) + (s2/conformer (xml/xml-constructor type/canonical identity) type/to-xml))}]))) (testing "base64Binary" (is (= (-> (impl/primitive-type->spec-defs (primitive-type "base64Binary")) @@ -143,7 +143,7 @@ (fn [~'e] (xml/value-matches? "([0-9a-zA-Z\\\\+/=]{4})+" ~'e)) (s2/conformer xml/remove-character-content xml/set-extension-tag) (s2/schema {:content (s2/coll-of :fhir.xml/Extension)}) - (s2/conformer type/xml->Base64Binary type/to-xml))}]))) + (s2/conformer (xml/xml-constructor type/base64Binary identity) type/to-xml))}]))) (testing "code" (is (= (-> (impl/primitive-type->spec-defs (primitive-type "code")) @@ -156,7 +156,7 @@ xml/element? (s2/conformer xml/remove-character-content xml/set-extension-tag) (s2/schema {:content (s2/coll-of :fhir.xml/Extension)}) - (s2/conformer type/xml->Code type/to-xml))}]))) + (s2/conformer (xml/xml-constructor type/code identity) type/to-xml))}]))) (testing "unsignedInt" (is (= (-> (impl/primitive-type->spec-defs (primitive-type "unsignedInt")) @@ -170,7 +170,7 @@ (fn [~'e] (xml/value-matches? "[0]|([1-9][0-9]*)" ~'e)) (s2/conformer xml/remove-character-content xml/set-extension-tag) (s2/schema {:content (s2/coll-of :fhir.xml/Extension)}) - (s2/conformer type/xml->UnsignedInt type/to-xml))}]))) + (s2/conformer (xml/xml-constructor type/unsignedInt system/parse-integer) type/to-xml))}]))) (testing "positiveInt" (is (= (-> (impl/primitive-type->spec-defs (primitive-type "positiveInt")) @@ -184,7 +184,7 @@ (fn [~'e] (xml/value-matches? "[1-9][0-9]*" ~'e)) (s2/conformer xml/remove-character-content xml/set-extension-tag) (s2/schema {:content (s2/coll-of :fhir.xml/Extension)}) - (s2/conformer type/xml->PositiveInt type/to-xml))}]))) + (s2/conformer (xml/xml-constructor type/positiveInt system/parse-integer) type/to-xml))}]))) (testing "uuid" (is (= (-> (impl/primitive-type->spec-defs (primitive-type "uuid")) @@ -198,7 +198,7 @@ (fn [~'e] (xml/value-matches? "urn:uuid:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" ~'e)) (s2/conformer xml/remove-character-content xml/set-extension-tag) (s2/schema {:content (s2/coll-of :fhir.xml/Extension)}) - (s2/conformer type/xml->Uuid type/to-xml))}]))) + (s2/conformer (xml/xml-constructor type/uuid identity) type/to-xml))}]))) (testing "xhtml" (is (= (impl/primitive-type->spec-defs (primitive-type "xhtml")) @@ -208,7 +208,7 @@ :spec-form `(s2/and xml/element? - (s2/conformer type/xml->Xhtml type/to-xml))}])))) + (s2/conformer type/xml->Xhtml type/xhtml-to-xml))}])))) (defn- complex-type [name] (some #(when (= name (:name %)) %) (sdr/complex-types structure-definition-repo))) @@ -226,18 +226,14 @@ :valueReference :fhir/Reference))) (testing "internal representation of complex type" - (testing "has a type checker" - (given (group-by :key (impl/struct-def->spec-def (complex-type "UsageContext"))) - [:fhir/UsageContext 0 :spec-form 1] - := `(fn [~'m] (identical? :fhir/UsageContext (type/type ~'m))))) - (testing "has a schema form" - (given (group-by :key (impl/struct-def->spec-def (complex-type "UsageContext"))) - [:fhir/UsageContext 0 :spec-form 2] - := `(s2/schema - {:id :fhir.UsageContext/id - :extension (s2/coll-of :fhir.UsageContext/extension) - :code :fhir.UsageContext/code - :value :fhir.UsageContext/value})))) + (given (group-by :key (impl/struct-def->spec-def (complex-type "UsageContext"))) + [:fhir/UsageContext 0 :spec-form] + := `(blaze.fhir.spec.impl.specs/record + blaze.fhir.spec.type.UsageContext + {:id :fhir.UsageContext/id, + :extension (s2/coll-of :fhir.UsageContext/extension), + :code :fhir.UsageContext/code, + :value :fhir.UsageContext/value}))) (testing "XML representation of Bundle.id" (given (group-by :key (impl/struct-def->spec-def (resource structure-definition-repo "Bundle"))) @@ -257,7 +253,7 @@ (when ~'m (xml-node/element* nil - (impl/select-non-nil-keys ~'m [:id]) + (impl/select-non-nil-keys ~'m #{:id}) (-> [] (impl/conj-all ::f/extension (:extension ~'m)) (impl/conj-all ::f/modifierExtension (:modifierExtension ~'m)) @@ -293,10 +289,8 @@ (testing "XML representation of Extension" (given (group-by :key (impl/struct-def->spec-def (complex-type "Extension"))) - [:fhir.Extension/url 0 :spec-form regexes->str] - := `(s2/and string? (specs/regex "(?U)[\\p{Print}&&[^\\p{Blank}]]*" impl/intern-string)) - [:fhir.xml.Extension/url 0 :spec-form regexes->str] - := `(s2/and string? (specs/regex "(?U)[\\p{Print}&&[^\\p{Blank}]]*" impl/intern-string)) + [:fhir.Extension/url 0 :spec-form regexes->str] := `string? + [:fhir.xml.Extension/url 0 :spec-form regexes->str] := `string? [:fhir.xml.Extension/url 0 :representation] := :xmlAttr)) (testing "XML representation of Coding" @@ -304,7 +298,7 @@ [:fhir.xml/Coding 0 :spec-form 1 2 2 2] := `(xml-node/element* nil - (impl/select-non-nil-keys ~'m [:id]) + (impl/select-non-nil-keys ~'m #{:id}) (-> [] (impl/conj-all ::f/extension (:extension ~'m)) @@ -317,11 +311,17 @@ (testing "XML representation of Measure unformer XML attributes" (given (group-by :key (impl/struct-def->spec-def (resource structure-definition-repo "Measure"))) [:fhir.xml/Measure 0 :spec-form 1 2 2 2 2] := - `(assoc (impl/select-non-nil-keys ~'m []) :xmlns "http://hl7.org/fhir"))) + `(assoc (impl/select-non-nil-keys ~'m #{}) :xmlns "http://hl7.org/fhir"))) (testing "XML representation of Measure.url" (given (group-by :key (impl/struct-def->spec-def (resource structure-definition-repo "Measure"))) - [:fhir.xml.Measure/url 0 :spec-form] := :fhir.xml/uri)) + [:fhir.xml.Measure/url 0 :spec-form regexes->str] + := `(s2/and + xml/element? + (fn [~'e] (xml/value-matches? "(?U)[\\p{Print}&&[^\\p{Blank}]]*" ~'e)) + (s2/conformer xml/remove-character-content xml/set-extension-tag) + (s2/schema {:content (s2/coll-of :fhir.xml/Extension)}) + (s2/conformer (xml/xml-constructor type/uri-interned identity) type/to-xml)))) (testing "XML representation of Questionnaire.item contains recursive spec to itself" (given (group-by :key (impl/struct-def->spec-def (resource structure-definition-repo "Questionnaire"))) @@ -337,7 +337,7 @@ xml/element? (s2/conformer xml/remove-character-content xml/set-extension-tag) (s2/schema {:content (s2/coll-of :fhir.xml/Extension)}) - (s2/conformer type/xml->InternedString type/to-xml))))) + (s2/conformer (xml/xml-constructor type/string-interned identity) type/to-xml))))) (deftest elem-def->spec-def-test (testing "normal type" diff --git a/modules/fhir-structure/test/blaze/fhir/spec/memory.clj b/modules/fhir-structure/test/blaze/fhir/spec/memory.clj new file mode 100644 index 000000000..c8374f5a6 --- /dev/null +++ b/modules/fhir-structure/test/blaze/fhir/spec/memory.clj @@ -0,0 +1,67 @@ +(ns blaze.fhir.spec.memory + (:import + [blaze.fhir.spec.type ExtensionData] + [clojure.lang PersistentVector] + [java.time ZoneOffset] + [org.openjdk.jol.info ClassLayout GraphLayout])) + +(set! *warn-on-reflection* true) + +(defn graph-layout ^GraphLayout [& roots] + (GraphLayout/parseInstance (object-array roots))) + +(defn class-layout ^ClassLayout [x] + (ClassLayout/parseInstance x)) + +(defn total-size [& roots] + (-> ^GraphLayout (apply graph-layout roots) + (.subtract (graph-layout ExtensionData/EMPTY)) + (.subtract (graph-layout PersistentVector/EMPTY_NODE)) + (.subtract (graph-layout PersistentVector/EMPTY)) + (.subtract (graph-layout ZoneOffset/UTC)) + (.totalSize))) + +(defn total-size* [x y] + (-> (graph-layout x y) + (.subtract (graph-layout x)) + (.totalSize))) + +(defn total-size-exclude [x & excludes] + (-> (graph-layout x) + (.subtract (graph-layout ExtensionData/EMPTY)) + (.subtract (graph-layout PersistentVector/EMPTY_NODE)) + (.subtract (graph-layout PersistentVector/EMPTY)) + (.subtract (graph-layout ZoneOffset/UTC)) + (.subtract (apply graph-layout excludes)) + (.totalSize))) + +(defn print-layout [x & excludes] + (-> (graph-layout x) + (.subtract (graph-layout ExtensionData/EMPTY)) + (.subtract (graph-layout PersistentVector/EMPTY_NODE)) + (.subtract (graph-layout PersistentVector/EMPTY)) + (.subtract (graph-layout ZoneOffset/UTC)) + (.subtract (apply graph-layout excludes)) + (.toPrintable))) + +(defn print-footprint [x & excludes] + (-> (graph-layout x) + (.subtract (graph-layout ExtensionData/EMPTY)) + (.subtract (graph-layout PersistentVector/EMPTY_NODE)) + (.subtract (graph-layout PersistentVector/EMPTY)) + (.subtract (graph-layout ZoneOffset/UTC)) + (.subtract (apply graph-layout excludes)) + (.toFootprint))) + +(defn print-footprint* [x y] + (-> (graph-layout x y) + (.subtract (graph-layout x)) + (.toFootprint))) + +(defn print-total-layout [& roots] + (-> ^GraphLayout (apply graph-layout roots) + (.toPrintable))) + +(defn print-class-layout [x] + (-> (class-layout x) + (.toPrintable))) diff --git a/modules/fhir-structure/test/blaze/fhir/spec/resource_test.clj b/modules/fhir-structure/test/blaze/fhir/spec/resource_test.clj index 96f7058e7..5607a8490 100644 --- a/modules/fhir-structure/test/blaze/fhir/spec/resource_test.clj +++ b/modules/fhir-structure/test/blaze/fhir/spec/resource_test.clj @@ -81,6 +81,11 @@ ::anom/category := ::anom/incorrect ::anom/message := "Invalid JSON representation of a resource. Error on integer value 0. Expected type is `string`.")) + (testing "end of input" + (given (parse-json "Patient" "{\"id\":") + ::anom/message := "Invalid JSON representation of a resource. Unexpected end of input." + [:fhir/issues 0 :fhir.issues/expression] := "Patient")) + (testing "type autodiscovery" (given-parse-json {:resourceType "Patient" @@ -258,13 +263,18 @@ (testing "birthDate" (given-parse-json "Patient" {:birthDate "2025"} - :birthDate := #fhir/date "2025") + :birthDate := #fhir/date #system/date "2025") (testing "invalid date" (given-parse-json "Patient" {:birthDate "2025-13"} ::anom/category := ::anom/incorrect - ::anom/message := "Invalid JSON representation of a resource. Error on value `2025-13`. Expected type is `date`." + ::anom/message := "Invalid JSON representation of a resource. Error on value `2025-13`. Expected type is `date`. Invalid value for MonthOfYear (valid values 1 - 12): 13" + [:fhir/issues 0 :fhir.issues/expression] := "Patient.birthDate") + + (given (parse-json "Patient" "{\"birthDate\": 9223372036854775808}") + ::anom/category := ::anom/incorrect + ::anom/message := "Invalid JSON representation of a resource. Numeric value (9223372036854775808) out of range of long (-9223372036854775808 - 9223372036854775807)" [:fhir/issues 0 :fhir.issues/expression] := "Patient.birthDate"))) (testing "deceasedBoolean" @@ -276,7 +286,7 @@ (testing "deceasedDateTime" (given-parse-json "Patient" {:deceasedDateTime "2025"} - :deceased := #fhir/dateTime "2025")) + :deceased := #fhir/dateTime #system/date-time "2025")) (testing "multipleBirthBoolean" (given-parse-json "Patient" @@ -290,9 +300,20 @@ :multipleBirth := #fhir/boolean{:id "id-172212" :value false}))) (testing "multipleBirthInteger" - (given-parse-json "Patient" - {:multipleBirthInteger 2} - :multipleBirth := #fhir/integer 2)) + (doseq [value [Integer/MIN_VALUE -1 0 1 Integer/MAX_VALUE]] + (given-parse-json "Patient" + {:multipleBirthInteger value} + :multipleBirth := (type/integer value))) + + (testing "too large" + (given-parse-json "Patient" + {:multipleBirthInteger (inc Integer/MAX_VALUE)} + ::anom/message := "Invalid JSON representation of a resource. Invalid integer value `2147483648`." + [:fhir/issues 0 :fhir.issues/expression] := "Patient.multipleBirth") + + (given (parse-json "Patient" "{\"multipleBirthInteger\": 9223372036854775808}") + ::anom/message := "Invalid JSON representation of a resource. Numeric value (9223372036854775808) out of range of long (-9223372036854775808 - 9223372036854775807)" + [:fhir/issues 0 :fhir.issues/expression] := "Patient.multipleBirth"))) (testing "contact" (given-parse-json "Patient" @@ -342,13 +363,13 @@ (deftest parse-json-molecular-sequence-test (testing "multiple decimal values" (doseq [[values extended-properties] - [[1 [{:id "id-140530"}]] + [[1M [{:id "id-140530"}]] [1.1M [{:id "id-140530"}]] - [[1 2] [nil {:id "id-140556"}]] - [[1 1.1M] [{:id "id-140622"}]] - [[1.1M 1] [{:id "id-140636"} {:id "id-140636"}]] - [[1 2 3] [{:id "id-142643"}]] - [[nil 2 3] [{:id "id-142643"} nil {:id "id-142842"}]]] + [[1M 2M] [nil {:id "id-140556"}]] + [[1M 1.1M] [{:id "id-140622"}]] + [[1.1M 1M] [{:id "id-140636"} {:id "id-140636"}]] + [[1M 2M 3M] [{:id "id-142643"}]] + [[nil 2M 3M] [{:id "id-142643"} nil {:id "id-142842"}]]] :let [result-values (cond-> values (number? values) vector) result (mapv #(type/decimal (assoc %2 :value %1)) result-values @@ -381,13 +402,6 @@ ::anom/message := "Invalid JSON representation of a resource. Error on value `a`. Expected type is `decimal`." [:fhir/issues 0 :fhir.issues/expression] := "MolecularSequence.quality[0].roc.precision")) - (testing "long out of range" - (doseq [value ["{\"quality\":{\"roc\":{\"precision\":9999999999999999999}}}" - "{\"quality\":{\"roc\":{\"precision\":[9999999999999999999]}}}"]] - (given (parse-json "MolecularSequence" value) - ::anom/message := "Invalid JSON representation of a resource. Numeric value (9999999999999999999) out of range of long (-9223372036854775808 - 9223372036854775807)" - [:fhir/issues 0 :fhir.issues/expression] := "MolecularSequence.quality[0].roc.precision"))) - (testing "end of input" (doseq [value ["{\"quality\":{\"roc\":{\"precision\":0" "{\"quality\":{\"roc\":{\"precision\":[0"]] @@ -420,7 +434,7 @@ (given-parse-json "Extension" {:url "foo" :valueBase64Binary value} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension :value := (type/base64Binary value))) (testing "invalid" @@ -435,7 +449,7 @@ (given-parse-cbor "Extension" {:url "foo" :valueBase64Binary "a"} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension :value := (type/base64Binary "a")))) (testing "boolean" @@ -443,7 +457,7 @@ (given-parse-json "Extension" {:url "foo" :valueBoolean value} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension :value := (type/boolean value)))) (testing "canonical" @@ -451,7 +465,7 @@ (given-parse-json "Extension" {:url "foo" :valueCanonical value} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension :value := (type/canonical value))) (testing "invalid" @@ -467,7 +481,7 @@ (given-parse-json "Extension" {:url "foo" :valueCode value} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension :value := (type/code value)))) (testing "date" @@ -475,16 +489,16 @@ (given-parse-json "Extension" {:url "url-204835" :valueDate value} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension :url := "url-204835" - :value := (type/date value)) + :value := (type/date (system/parse-date value))) (testing "extended properties before value" (given-parse-json "Extension" {:url "url-204835" :_valueDate {:id "id-162932"} :valueDate value} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension :url := "url-204835" :value := (type/date {:id "id-162932" :value (system/parse-date value)}))) @@ -493,7 +507,7 @@ {:url "url-204835" :valueDate value :_valueDate {:id "id-162932"}} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension :url := "url-204835" :value := (type/date {:id "id-162932" :value (system/parse-date value)})))) @@ -502,21 +516,21 @@ {:url "url-204835" :valueDate "foo"} ::anom/category := ::anom/incorrect - ::anom/message := "Invalid JSON representation of a resource. Error on value `foo`. Expected type is `date`." + ::anom/message := "Invalid JSON representation of a resource. Error on value `foo`. Expected type is `date`. Can't parse `foo` as System.Date because it doesn't has the right length." [:fhir/issues 0 :fhir.issues/expression] := "Extension.value") (given-parse-json "Extension" {:url "url-204835" :valueDate "abcd"} ::anom/category := ::anom/incorrect - ::anom/message := "Invalid JSON representation of a resource. Error on value `abcd`. Expected type is `date`." + ::anom/message := "Invalid JSON representation of a resource. Error on value `abcd`. Expected type is `date`. Invalid date component: For input string: \"abcd\"" [:fhir/issues 0 :fhir.issues/expression] := "Extension.value") (given-parse-json "Extension" {:url "url-204835" :valueDate "2025-02-29"} ::anom/category := ::anom/incorrect - ::anom/message := "Invalid JSON representation of a resource. Error on value `2025-02-29`. Expected type is `date`." + ::anom/message := "Invalid JSON representation of a resource. Error on value `2025-02-29`. Expected type is `date`. Invalid date 'February 29' as '2025' is not a leap year" [:fhir/issues 0 :fhir.issues/expression] := "Extension.value"))) (testing "dateTime" @@ -525,9 +539,9 @@ (given-parse-json "Extension" {:url "url-204835" :valueDateTime value} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension :url := "url-204835" - :value := (type/dateTime value))) + :value := (type/dateTime (system/parse-date-time value)))) (testing "extended properties before value" (doseq [date-time ["2025" "2025-03" "2025-03-15" "2025-03-15T15:22:13" @@ -536,7 +550,7 @@ {:url "url-204835" :_valueDateTime {:id "id-162932"} :valueDateTime date-time} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension :url := "url-204835" :value := (type/dateTime {:id "id-162932" :value (system/parse-date-time date-time)})))) @@ -547,7 +561,7 @@ {:url "url-204835" :valueDateTime value :_valueDateTime {:id "id-162932"}} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension :url := "url-204835" :value := (type/dateTime {:id "id-162932" :value (system/parse-date-time value)})))) @@ -556,29 +570,29 @@ {:url "url-204835" :valueDateTime "foo"} ::anom/category := ::anom/incorrect - ::anom/message := "Invalid JSON representation of a resource. Error on value `foo`. Expected type is `date-time`." + ::anom/message := "Invalid JSON representation of a resource. Error on value `foo`. Expected type is `date-time`. Text cannot be parsed to a DateTime" [:fhir/issues 0 :fhir.issues/expression] := "Extension.value") (given-parse-json "Extension" {:url "url-204835" :valueDateTime "abcd"} ::anom/category := ::anom/incorrect - ::anom/message := "Invalid JSON representation of a resource. Error on value `abcd`. Expected type is `date-time`." + ::anom/message := "Invalid JSON representation of a resource. Error on value `abcd`. Expected type is `date-time`. Invalid date component: For input string: \"abcd\"" [:fhir/issues 0 :fhir.issues/expression] := "Extension.value") (given-parse-json "Extension" {:url "url-204835" :valueDateTime "2025-02-29"} ::anom/category := ::anom/incorrect - ::anom/message := "Invalid JSON representation of a resource. Error on value `2025-02-29`. Expected type is `date-time`." + ::anom/message := "Invalid JSON representation of a resource. Error on value `2025-02-29`. Expected type is `date-time`. Invalid date 'February 29' as '2025' is not a leap year" [:fhir/issues 0 :fhir.issues/expression] := "Extension.value"))) (testing "decimal" - (doseq [value [-1 0 1 -1.1M 1.1M]] + (doseq [value [-1M 0M 1M -1.1M 1.1M]] (given-parse-json "Extension" {:url "foo" :valueDecimal value} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension :value := (type/decimal value))) (testing "invalid" @@ -601,7 +615,7 @@ (given-parse-json "Extension" {:url "foo" :valueId value} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension :value := (type/id value))) (testing "invalid" @@ -624,8 +638,8 @@ (given-parse-json "Extension" {:url "foo" :valueInstant value} - type/type := :fhir/Extension - :value := (type/instant value))) + :fhir/type := :fhir/Extension + :value := (type/instant (system/parse-date-time value)))) (testing "invalid" (given-parse-json "Extension" @@ -640,7 +654,7 @@ (given-parse-json "Extension" {:url "foo" :valueInteger value} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension :value := (type/integer value)))) (testing "markdown" @@ -648,7 +662,7 @@ (given-parse-json "Extension" {:url "foo" :valueMarkdown value} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension :value := (type/markdown value)))) (testing "oid" @@ -657,7 +671,7 @@ (given-parse-json "Extension" {:url "foo" :valueOid value} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension :value := (type/oid value))) (testing "invalid" @@ -669,19 +683,26 @@ [:fhir/issues 0 :fhir.issues/expression] := "Extension.value"))) (testing "positiveInt" - (doseq [value [1 2]] + (doseq [value [1 2 Integer/MAX_VALUE]] (given-parse-json "Extension" {:url "foo" :valuePositiveInt value} - type/type := :fhir/Extension - :value := (type/positiveInt value)))) + :fhir/type := :fhir/Extension + :value := (type/positiveInt value))) + + (testing "too large" + (given-parse-json "Extension" + {:url "foo" + :valuePositiveInt (inc Integer/MAX_VALUE)} + ::anom/message := "Invalid JSON representation of a resource. Invalid positiveInt value `2147483648`." + [:fhir/issues 0 :fhir.issues/expression] := "Extension.value"))) (testing "string" (doseq [value ["value-204935" "𝗔𝗗𝗗𝗜𝗧𝗜𝗢𝗡𝗔𝗟 𝗨𝗦𝗖𝗗𝗜" "\t" "\r" "\n" "" "\001e"]] (given-parse-json "Extension" {:url "url-204835" :valueString value} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension :url := "url-204835" :value := (type/string value)))) @@ -690,9 +711,9 @@ (given-parse-json "Extension" {:url "url-204835" :valueTime value} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension :url := "url-204835" - :value := (type/time value))) + :value := (type/time (system/parse-time value)))) (testing "extended properties before value" (doseq [value ["15:22:13" "15:22:13.1" "15:22:13.12" "15:22:13.123"]] @@ -700,7 +721,7 @@ {:url "url-204835" :_valueTime {:id "id-162932"} :valueTime value} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension :url := "url-204835" :value := (type/time {:id "id-162932" :value (system/parse-time value)})))) @@ -710,7 +731,7 @@ {:url "url-204835" :valueTime value :_valueTime {:id "id-162932"}} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension :url := "url-204835" :value := (type/time {:id "id-162932" :value (system/parse-time value)})))) @@ -719,23 +740,30 @@ {:url "url-204835" :valueTime "15:60:00"} ::anom/category := ::anom/incorrect - ::anom/message := "Invalid JSON representation of a resource. Error on value `15:60:00`. Expected type is `time`." + ::anom/message := "Invalid JSON representation of a resource. Error on value `15:60:00`. Expected type is `time`. Text '15:60:00' could not be parsed: Invalid value for MinuteOfHour (valid values 0 - 59): 60" [:fhir/issues 0 :fhir.issues/expression] := "Extension.value"))) (testing "unsignedInt" - (doseq [value [0 1]] + (doseq [value [0 1 Integer/MAX_VALUE]] (given-parse-json "Extension" {:url "foo" :valueUnsignedInt value} - type/type := :fhir/Extension - :value := (type/unsignedInt value)))) + :fhir/type := :fhir/Extension + :value := (type/unsignedInt value))) + + (testing "too large" + (given-parse-json "Extension" + {:url "foo" + :valueUnsignedInt (inc Integer/MAX_VALUE)} + ::anom/message := "Invalid JSON representation of a resource. Invalid unsignedInt value `2147483648`." + [:fhir/issues 0 :fhir.issues/expression] := "Extension.value"))) (testing "uri" (doseq [value ["foo" "bar"]] (given-parse-json "Extension" {:url "foo" :valueUri value} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension :value := (type/uri value))) (testing "invalid" @@ -751,7 +779,7 @@ (given-parse-json "Extension" {:url "foo" :valueUrl value} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension :value := (type/url value))) (testing "with id" @@ -759,7 +787,7 @@ {:url "foo" :valueUrl "url-171021" :_valueUrl {:id "id-170249"}} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension :value := (type/url {:id "id-170249" :value "url-171021"}))) (testing "with extension" @@ -767,7 +795,7 @@ {:url "foo" :valueUrl "url-171021" :_valueUrl {:extension {:url "url-170854"}}} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension :value := (type/url {:extension [#fhir/Extension{:url "url-170854"}] :value "url-171021"}))) (testing "invalid" @@ -783,7 +811,7 @@ (given-parse-json "Extension" {:url "foo" :valueUuid value} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension :value := (type/uuid value))) (testing "with id" @@ -791,7 +819,7 @@ {:url "foo" :valueUuid "urn:uuid:29745744-6761-44a9-ab95-fea7e45fc903" :_valueUuid {:id "id-170249"}} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension :value := (type/uuid {:id "id-170249" :value "urn:uuid:29745744-6761-44a9-ab95-fea7e45fc903"}))) (testing "with extension" @@ -799,7 +827,7 @@ {:url "foo" :valueUuid "urn:uuid:29745744-6761-44a9-ab95-fea7e45fc903" :_valueUuid {:extension {:url "url-170854"}}} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension :value := (type/uuid {:extension [#fhir/Extension{:url "url-170854"}] :value "urn:uuid:29745744-6761-44a9-ab95-fea7e45fc903"}))) (testing "invalid" @@ -814,80 +842,70 @@ (given-parse-json "Extension" {:url "foo" :valueAddress {:city "Leipzig"}} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension :value := #fhir/Address{:city #fhir/string "Leipzig"})) - ;; TODO: Age - ;; TODO: Annotation - (testing "Attachment" (given-parse-json "Extension" {:url "foo" :valueAttachment {:contentType "text/plain"}} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension :value := #fhir/Attachment{:contentType #fhir/code "text/plain"})) (testing "CodeableConcept" (given-parse-json "Extension" {:url "foo" :valueCodeableConcept {:text "text-161119"}} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension :value := #fhir/CodeableConcept{:text #fhir/string "text-161119"})) (testing "Coding" (given-parse-json "Extension" {:url "foo" :valueCoding {:code "code-161220"}} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension :value := #fhir/Coding{:code #fhir/code "code-161220"})) (testing "Coding" (given-parse-json "Extension" {:url "foo" :valueCoding {:code "code-161220"}} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension :value := #fhir/Coding{:code #fhir/code "code-161220"})) - ;; TODO: ContactPoint - ;; TODO: Count - ;; TODO: Distance - ;; TODO: Duration - (testing "HumanName" (given-parse-json "Extension" {:url "foo" :valueHumanName {:family "family-161430"}} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension :value := #fhir/HumanName{:family #fhir/string "family-161430"})) (testing "Identifier" (given-parse-json "Extension" {:url "foo" :valueIdentifier {:value "value-162019"}} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension :value := #fhir/Identifier{:value #fhir/string "value-162019"})) - ;; TODO: Money - (testing "Period" (given-parse-json "Extension" {:url "foo" :valuePeriod {:start "2025-03-21"}} - type/type := :fhir/Extension - :value := #fhir/Period{:start #fhir/dateTime "2025-03-21"})) + :fhir/type := :fhir/Extension + :value := #fhir/Period{:start #fhir/dateTime #system/date-time "2025-03-21"})) (testing "Quantity" (given-parse-json "Extension" {:url "foo" :valueQuantity {:value 3.141}} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension :value := #fhir/Quantity{:value #fhir/decimal 3.141M})) (testing "Range" (given-parse-json "Extension" {:url "foo" :valueRange {:low {:value 3.141}}} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension [:value :fhir/type] := :fhir/Range [:value :low] := #fhir/Quantity{:value #fhir/decimal 3.141M})) @@ -895,42 +913,29 @@ (given-parse-json "Extension" {:url "foo" :valueRatio {:numerator {:value 3.141}}} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension :value := #fhir/Ratio{:numerator #fhir/Quantity{:value #fhir/decimal 3.141M}})) (testing "Reference" (given-parse-json "Extension" {:url "foo" :valueReference {:reference "reference-165129"}} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension :value := #fhir/Reference{:reference #fhir/string "reference-165129"})) - ;; TODO: SampledData - ;; TODO: Signature - ;; TODO: Timing - ;; TODO: ContactDetail - ;; TODO: Contributor - ;; TODO: DataRequirement - (testing "Expression" (given-parse-json "Extension" {:url "foo" :valueExpression {:name "name-165516"}} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension [:value :fhir/type] := :fhir/Expression [:value :name] := #fhir/id "name-165516")) - ;; TODO: ParameterDefinition - ;; TODO: RelatedArtifact - ;; TODO: TriggerDefinition - ;; TODO: UsageContext - ;; TODO: Dosage - (testing "Meta" (given-parse-json "Extension" {:url "foo" :valueMeta {:source "uri-171103"}} - type/type := :fhir/Extension + :fhir/type := :fhir/Extension :value := #fhir/Meta{:source #fhir/uri "uri-171103"}))) (deftest parse-json-human-name-test @@ -962,7 +967,7 @@ (testing "Extension" (given-parse-json "HumanName" {:extension {:url "url-102118" :valueString "value-102132"}} - type/type := :fhir/HumanName + :fhir/type := :fhir/HumanName :extension := [#fhir/Extension {:url "url-102118" :value #fhir/string "value-102132"}])) @@ -970,7 +975,7 @@ (testing "family" (given-parse-json "HumanName" {:family "family-173154"} - type/type := :fhir/HumanName + :fhir/type := :fhir/HumanName :family := #fhir/string "family-173154")) (testing "given" @@ -998,7 +1003,7 @@ (doseq [extended-properties [{:id "id-165848"} [{:id "id-165848"}]]] (given-parse-json "HumanName" {:_given extended-properties} - type/type := :fhir/HumanName + :fhir/type := :fhir/HumanName :given := [#fhir/string{:id "id-165848"}])) (testing "invalid" @@ -1014,7 +1019,7 @@ extended-properties [{:extension extensions} [{:extension extensions}]]] (given-parse-json "HumanName" {:_given extended-properties} - type/type := :fhir/HumanName + :fhir/type := :fhir/HumanName :given := [#fhir/string{:extension [#fhir/Extension{:url "url-182516"}]}])) (testing "invalid" @@ -1032,7 +1037,7 @@ (given-parse-json "HumanName" {:given [] :_given extended-properties} - type/type := :fhir/HumanName + :fhir/type := :fhir/HumanName :given := [#fhir/string{:id "id-211339"}])) (testing "invalid" @@ -1049,19 +1054,19 @@ (given-parse-json "HumanName" {:_given extended-properties :given []} - type/type := :fhir/HumanName + :fhir/type := :fhir/HumanName :given := [#fhir/string{:id "id-151600"}]))))) (testing "one given name" (given-parse-json "HumanName" {:given ["given-210928"]} - type/type := :fhir/HumanName + :fhir/type := :fhir/HumanName :given := [#fhir/string "given-210928"]) (testing "without array" (given-parse-json "HumanName" {:given "given-205309"} - type/type := :fhir/HumanName + :fhir/type := :fhir/HumanName :given := [#fhir/string "given-205309"])) (testing "extended properties after value" @@ -1070,7 +1075,7 @@ (given-parse-json "HumanName" {:given ["given-210928"] :_given extended-properties} - type/type := :fhir/HumanName + :fhir/type := :fhir/HumanName :given := [#fhir/string{:id "id-211339" :value "given-210928"}])) (testing "invalid" @@ -1087,13 +1092,13 @@ (given-parse-json "HumanName" {:_given extended-properties :given ["given-210928"]} - type/type := :fhir/HumanName + :fhir/type := :fhir/HumanName :given := [#fhir/string{:id "id-151600" :value "given-210928"}])))) (testing "two given names" (given-parse-json "HumanName" {:given ["given-210928" "given-211224"]} - type/type := :fhir/HumanName + :fhir/type := :fhir/HumanName :given := [#fhir/string "given-210928" #fhir/string "given-211224"]) @@ -1101,7 +1106,7 @@ (given-parse-json "HumanName" {:_given [{:id "id-151310"} {:id "id-151315"}] :given ["given-151318" "given-151323"]} - type/type := :fhir/HumanName + :fhir/type := :fhir/HumanName :given := [#fhir/string{:id "id-151310" :value "given-151318"} #fhir/string{:id "id-151315" :value "given-151323"}]) @@ -1109,7 +1114,7 @@ (given-parse-json "HumanName" {:_given [nil {:id "id-151315"}] :given ["given-151318" "given-151323"]} - type/type := :fhir/HumanName + :fhir/type := :fhir/HumanName :given := [#fhir/string "given-151318" #fhir/string{:id "id-151315" :value "given-151323"}])) @@ -1118,7 +1123,7 @@ (given-parse-json "HumanName" {:_given extended-properties :given ["given-151318" "given-151323"]} - type/type := :fhir/HumanName + :fhir/type := :fhir/HumanName :given := [#fhir/string{:id "id-151315" :value "given-151318"} #fhir/string "given-151323"]))) @@ -1127,7 +1132,7 @@ (given-parse-json "HumanName" {:_given extended-properties :given ["given-151318" "given-151323"]} - type/type := :fhir/HumanName + :fhir/type := :fhir/HumanName :given := [#fhir/string "given-151318" #fhir/string "given-151323"])))) @@ -1135,7 +1140,7 @@ (given-parse-json "HumanName" {:given ["given-151318" "given-151323"] :_given [{:id "id-151310"} {:id "id-151315"}]} - type/type := :fhir/HumanName + :fhir/type := :fhir/HumanName :given := [#fhir/string{:id "id-151310" :value "given-151318"} #fhir/string{:id "id-151315" :value "given-151323"}]) @@ -1143,7 +1148,7 @@ (given-parse-json "HumanName" {:given ["given-151318" "given-151323"] :_given [nil {:id "id-151315"}]} - type/type := :fhir/HumanName + :fhir/type := :fhir/HumanName :given := [#fhir/string "given-151318" #fhir/string{:id "id-151315" :value "given-151323"}])) @@ -1152,7 +1157,7 @@ (given-parse-json "HumanName" {:given ["given-151318" "given-151323"] :_given extended-properties} - type/type := :fhir/HumanName + :fhir/type := :fhir/HumanName :given := [#fhir/string{:id "id-151315" :value "given-151318"} #fhir/string "given-151323"]))) @@ -1161,7 +1166,7 @@ (given-parse-json "HumanName" {:given ["given-151318" "given-151323"] :_given extended-properties} - type/type := :fhir/HumanName + :fhir/type := :fhir/HumanName :given := [#fhir/string "given-151318" #fhir/string "given-151323"]))) @@ -1170,77 +1175,77 @@ {:given [nil "given-105549"] :_given [{:id "id-105603"} nil]} ::anom/message := nil - type/type := :fhir/HumanName + :fhir/type := :fhir/HumanName :given := [#fhir/string{:id "id-105603"} #fhir/string "given-105549"]))))) (testing "period" (given-parse-json "HumanName" {:period {:start "2025"}} - type/type := :fhir/HumanName - :period := #fhir/Period{:start #fhir/dateTime "2025"}))) + :fhir/type := :fhir/HumanName + :period := #fhir/Period{:start #fhir/dateTime #system/date-time "2025"}))) (deftest parse-json-reference-test (testing "id" (given-parse-json "Reference" {:id "id-100759"} - type/type := :fhir/Reference + :fhir/type := :fhir/Reference :id := "id-100759")) (testing "extension" (given-parse-json "Reference" {:extension {:url "url-100907"}} - type/type := :fhir/Reference + :fhir/type := :fhir/Reference :extension := [#fhir/Extension{:url "url-100907"}])) (testing "reference" (given-parse-json "Reference" {:reference "reference-101044"} - type/type := :fhir/Reference + :fhir/type := :fhir/Reference :reference := #fhir/string "reference-101044")) (testing "type" (given-parse-json "Reference" {:type "type-101127"} - type/type := :fhir/Reference + :fhir/type := :fhir/Reference :type := #fhir/uri "type-101127")) (testing "identifier" (given-parse-json "Reference" {:identifier {:value "value-101215"}} - type/type := :fhir/Reference + :fhir/type := :fhir/Reference :identifier := #fhir/Identifier{:value #fhir/string "value-101215"})) (testing "display" (given-parse-json "Reference" {:display "display-101307"} - type/type := :fhir/Reference + :fhir/type := :fhir/Reference :display := #fhir/string "display-101307"))) (deftest parse-json-meta-test (testing "versionId" (given-parse-json "Meta" {:versionId "versionId-104855"} - type/type := :fhir/Meta + :fhir/type := :fhir/Meta :versionId := #fhir/id "versionId-104855")) (testing "lastUpdated" (given-parse-json "Meta" {:lastUpdated "0001-01-01T00:00:00Z"} - type/type := :fhir/Meta - :lastUpdated := #fhir/instant "0001-01-01T00:00:00Z") + :fhir/type := :fhir/Meta + :lastUpdated := #fhir/instant #system/date-time "0001-01-01T00:00:00Z") (testing "extended properties after value" (given-parse-json "Meta" {:lastUpdated "0001-01-01T00:00:00Z" :_lastUpdated {:id "id-111214"}} - type/type := :fhir/Meta - :lastUpdated := #fhir/instant{:id "id-111214" :value "0001-01-01T00:00:00Z"}))) + :fhir/type := :fhir/Meta + :lastUpdated := #fhir/instant{:id "id-111214" :value #system/date-time "0001-01-01T00:00:00Z"}))) (testing "source" (given-parse-json "Meta" {:source "source-105040"} - type/type := :fhir/Meta + :fhir/type := :fhir/Meta :source := #fhir/uri "source-105040"))) (deftest parse-json-quantity-test @@ -1250,11 +1255,6 @@ ::anom/message := "Invalid JSON representation of a resource. Error on value `a`. Expected type is `decimal`." [:fhir/issues 0 :fhir.issues/expression] := "Quantity.value") - (testing "long out of range" - (given (parse-json "Quantity" "{\"value\":9999999999999999999}") - ::anom/message := "Invalid JSON representation of a resource. Numeric value (9999999999999999999) out of range of long (-9223372036854775808 - 9223372036854775807)" - [:fhir/issues 0 :fhir.issues/expression] := "Quantity.value")) - (testing "end of input" (given (parse-json "Quantity" "{\"value\":0") ::anom/message := "Invalid JSON representation of a resource. Unexpected end of input." diff --git a/modules/fhir-structure/test/blaze/fhir/spec/type/json_test.clj b/modules/fhir-structure/test/blaze/fhir/spec/type/json_test.clj deleted file mode 100644 index 36700791f..000000000 --- a/modules/fhir-structure/test/blaze/fhir/spec/type/json_test.clj +++ /dev/null @@ -1,48 +0,0 @@ -(ns blaze.fhir.spec.type.json-test - (:require - [blaze.fhir.spec.type.json :as json] - [blaze.test-util :as tu :refer [satisfies-prop]] - [clojure.spec.test.alpha :as st] - [clojure.test :as test :refer [deftest testing]] - [clojure.test.check.generators :as gen] - [clojure.test.check.properties :as prop]) - (:import - [java.nio.charset StandardCharsets])) - -(set! *warn-on-reflection* true) -(st/instrument) - -(test/use-fixtures :each tu/fixture) - -(deftest field-name-test - (testing "getValue" - (satisfies-prop 100 - (prop/for-all [value gen/string-ascii] - (= value (.getValue (json/field-name value)))))) - - (testing "charLength" - (satisfies-prop 100 - (prop/for-all [value gen/string-ascii] - (= (count value) (.charLength (json/field-name value)))))) - - (testing "appendQuotedUTF8" - (satisfies-prop 100 - (prop/for-all [value gen/string-ascii] - (let [buffer (byte-array (count value))] - (.appendQuotedUTF8 (json/field-name value) buffer 0) - (= value (String. buffer StandardCharsets/UTF_8)))))) - - (testing "asUnquotedUTF8" - (satisfies-prop 100 - (prop/for-all [value gen/string-ascii] - (= value (String. (.asUnquotedUTF8 (json/field-name value)) StandardCharsets/UTF_8))))) - - (testing "asQuotedUTF8" - (satisfies-prop 100 - (prop/for-all [value gen/string-ascii] - (= value (String. (.asQuotedUTF8 (json/field-name value)) StandardCharsets/UTF_8))))) - - (testing "toString" - (satisfies-prop 100 - (prop/for-all [value gen/string-ascii] - (= value (str (json/field-name value))))))) diff --git a/modules/fhir-structure/test/blaze/fhir/spec/type/system_test.clj b/modules/fhir-structure/test/blaze/fhir/spec/type/system_test.clj index 865dc993e..0faddc5dc 100644 --- a/modules/fhir-structure/test/blaze/fhir/spec/type/system_test.clj +++ b/modules/fhir-structure/test/blaze/fhir/spec/type/system_test.clj @@ -9,7 +9,6 @@ [java-time.api :as time]) (:import [blaze.fhir.spec.type.system DateDate DateTimeDate DateTimeYear DateTimeYearMonth DateYear DateYearMonth] - [com.google.common.hash Hashing] [java.time LocalTime ZoneOffset])) (set! *warn-on-reflection* true) @@ -17,18 +16,13 @@ (test/use-fixtures :each tu/fixture) -(defn murmur3 [x] - (let [hasher (.newHasher (Hashing/murmur3_32_fixed))] - (system/-hash-into x hasher) - (Integer/toHexString (.asInt (.hash hasher))))) - (deftest value-test (are [x] (system/value? x) true false "" 0M - #system/date"2020") + #system/date "2020") (are [x] (not (system/value? x)) nil @@ -44,11 +38,6 @@ (is (true? (system/boolean? true))) (is (false? (system/boolean? nil)))) - (testing "hash-into" - (are [b hex] (= hex (murmur3 b)) - true "70e1a2c0" - false "30f4c306")) - (testing "system equals" (are [a b pred] (pred (system/equals a b)) true true true? @@ -69,11 +58,6 @@ (testing "type" (is (= :system/integer (system/type (int 0))))) - (testing "hash-into" - (are [i hex] (= hex (murmur3 i)) - (int 0) "60ebe6c8" - (int 1) "bec48b04")) - (testing "system equals" (are [a b pred] (pred (system/equals a b)) (int 0) (int 0) true? @@ -91,11 +75,6 @@ (is (true? (system/long? 0))) (is (false? (system/long? (int 0))))) - (testing "hash-into" - (are [l hex] (= hex (murmur3 l)) - 0 "f395bb28" - 1 "aec1a7fd")) - (testing "system equals" (are [a b pred] (pred (system/equals a b)) 0 0 true? @@ -113,12 +92,6 @@ (is (true? (system/string? ""))) (is (false? (system/string? nil)))) - (testing "hash-into" - (are [s hex] (= hex (murmur3 s)) - "" "e45ad1ab" - "a" "e7da87f3" - "b" "5edf6843")) - (testing "system equals" (are [a b pred] (pred (system/equals a b)) "a" "a" true? @@ -136,11 +109,6 @@ (is (true? (system/decimal? 1M))) (is (false? (system/decimal? 1)))) - (testing "hash-into" - (are [d hex] (= hex (murmur3 d)) - 0M "bdef11d6" - 1M "8979c5c4")) - (testing "system equals" (are [a b pred] (pred (system/equals a b)) 0M 0M true? @@ -167,38 +135,38 @@ (deftest date-test (testing "date?" (are [date] (true? (system/date? date)) - #system/date"2020" - #system/date"2020-01" - #system/date"2020-01-01") + #system/date "2020" + #system/date "2020-01" + #system/date "2020-01-01") (are [x] (false? (system/date? x)) nil - #system/date-time"2020")) + #system/date-time "2020")) (testing "date" (testing "year" (are [year date] (= date (system/date year)) - 1000 #system/date"1000" - 2024 #system/date"2024" - 9999 #system/date"9999") + 1000 #system/date "1000" + 2024 #system/date "2024" + 9999 #system/date "9999") (given-thrown (system/date -1) :message := "Invalid value for Year (valid values 1 - 9999): -1")) (testing "year-month" (are [year month date] (= date (system/date year month)) - 1000 1 #system/date"1000-01" - 2024 6 #system/date"2024-06" - 9999 12 #system/date"9999-12") + 1000 1 #system/date "1000-01" + 2024 6 #system/date "2024-06" + 9999 12 #system/date "9999-12") (given-thrown (system/date 2024 0) :message := "Invalid value for MonthOfYear (valid values 1 - 12): 0")) (testing "year-month-day" (are [year month day date] (= date (system/date year month day)) - 1000 1 1 #system/date"1000-01-01" - 2024 6 15 #system/date"2024-06-15" - 9999 12 31 #system/date"9999-12-31") + 1000 1 1 #system/date "1000-01-01" + 2024 6 15 #system/date "2024-06-15" + 9999 12 31 #system/date "9999-12-31") (given-thrown (system/date 2023 2 29) :message := "Invalid date 'February 29' as '2023' is not a leap year"))) @@ -207,72 +175,72 @@ (testing "same precision" (testing "within date" (are [a b pred] (pred (system/equals a b)) - #system/date"2020" #system/date"2020" true? - #system/date"2020" #system/date"2021" false? - #system/date"2020" nil nil? - #system/date"2021" #system/date"2020" false? - #system/date"2021" #system/date"2021" true? - #system/date"2021" nil nil? - nil #system/date"2020" nil? - nil #system/date"2021" nil? + #system/date "2020" #system/date "2020" true? + #system/date "2020" #system/date "2021" false? + #system/date "2020" nil nil? + #system/date "2021" #system/date "2020" false? + #system/date "2021" #system/date "2021" true? + #system/date "2021" nil nil? + nil #system/date "2020" nil? + nil #system/date "2021" nil? nil nil nil? - #system/date"2020-01" #system/date"2020-01" true? - #system/date"2020-01" #system/date"2020-02" false? - #system/date"2020-01" nil nil? - #system/date"2020-02" #system/date"2020-01" false? - #system/date"2020-02" #system/date"2020-02" true? - #system/date"2020-02" nil nil? - nil #system/date"2020-01" nil? - nil #system/date"2020-02" nil? + #system/date "2020-01" #system/date "2020-01" true? + #system/date "2020-01" #system/date "2020-02" false? + #system/date "2020-01" nil nil? + #system/date "2020-02" #system/date "2020-01" false? + #system/date "2020-02" #system/date "2020-02" true? + #system/date "2020-02" nil nil? + nil #system/date "2020-01" nil? + nil #system/date "2020-02" nil? nil nil nil? - #system/date"2020-01-01" #system/date"2020-01-01" true? - #system/date"2020-01-01" #system/date"2020-01-02" false? - #system/date"2020-01-01" nil nil? - #system/date"2020-01-02" #system/date"2020-01-01" false? - #system/date"2020-01-02" #system/date"2020-01-02" true? - #system/date"2020-01-02" nil nil? - nil #system/date"2020-01-01" nil? - nil #system/date"2020-01-02" nil? + #system/date "2020-01-01" #system/date "2020-01-01" true? + #system/date "2020-01-01" #system/date "2020-01-02" false? + #system/date "2020-01-01" nil nil? + #system/date "2020-01-02" #system/date "2020-01-01" false? + #system/date "2020-01-02" #system/date "2020-01-02" true? + #system/date "2020-01-02" nil nil? + nil #system/date "2020-01-01" nil? + nil #system/date "2020-01-02" nil? nil nil nil?)) (testing "with date-time" (are [a b pred] (pred (system/equals a b)) - #system/date"2020" #system/date-time"2020" true? - #system/date"2020" #system/date-time"2021" false? - #system/date"2020-01" #system/date-time"2020-01" true? - #system/date"2020-01" #system/date-time"2020-02" false? - #system/date"2020-01-01" #system/date-time"2020-01-01" true? - #system/date"2020-01-01" #system/date-time"2020-01-02" false?))) + #system/date "2020" #system/date-time "2020" true? + #system/date "2020" #system/date-time "2021" false? + #system/date "2020-01" #system/date-time "2020-01" true? + #system/date "2020-01" #system/date-time "2020-02" false? + #system/date "2020-01-01" #system/date-time "2020-01-01" true? + #system/date "2020-01-01" #system/date-time "2020-01-02" false?))) (testing "different precision" (testing "within date" (are [a b pred] (pred (system/equals a b)) - #system/date"2020" #system/date"2020-01" nil? - #system/date"2020-01" #system/date"2020" nil? - #system/date"2020-01" #system/date"2020-01-01" nil? - #system/date"2020-01-01" #system/date"2020-01" nil?)) + #system/date "2020" #system/date "2020-01" nil? + #system/date "2020-01" #system/date "2020" nil? + #system/date "2020-01" #system/date "2020-01-01" nil? + #system/date "2020-01-01" #system/date "2020-01" nil?)) (testing "with date-time" (are [a b pred] (pred (system/equals a b)) - #system/date"2020" #system/date-time"2020-01" nil? - #system/date"2020-01" #system/date-time"2020" nil? - #system/date"2020-01" #system/date-time"2020-01-01" nil? - #system/date"2020-01-01" #system/date-time"2020-01" nil?)))) + #system/date "2020" #system/date-time "2020-01" nil? + #system/date "2020-01" #system/date-time "2020" nil? + #system/date "2020-01" #system/date-time "2020-01-01" nil? + #system/date "2020-01-01" #system/date-time "2020-01" nil?)))) (testing "print" (are [date s] (= (pr-str date) s) - #system/date"2020" "#system/date\"2020\"" - #system/date"2020-01" "#system/date\"2020-01\"" - #system/date"2020-01-02" "#system/date\"2020-01-02\""))) + #system/date "2020" "#system/date \"2020\"" + #system/date "2020-01" "#system/date \"2020-01\"" + #system/date "2020-01-02" "#system/date \"2020-01-02\""))) (deftest parse-date-test (testing "valid" (are [s d] (= d (system/parse-date s)) - "2020" #system/date"2020" - "2020-01" #system/date"2020-01" - "2020-01-02" #system/date"2020-01-02")) + "2020" #system/date "2020" + "2020-01" #system/date "2020-01" + "2020-01-02" #system/date "2020-01-02")) (testing "invalid" (are [s] (ba/incorrect? (st/with-instrument-disabled (system/parse-date s))) @@ -317,9 +285,9 @@ (testing "plus days" (are [date amount res] (= res (.plusDays date amount)) (DateDate/of 9998 12 31) 1 (system/date 9999 1 1) - (DateDate/of 9999 12 30) 1 #system/date"9999-12-31" + (DateDate/of 9999 12 30) 1 #system/date "9999-12-31" (DateDate/of 2 1 1) -1 (system/date 1 12 31) - (DateDate/of 1 1 2) -1 #system/date"0001-01-01") + (DateDate/of 1 1 2) -1 #system/date "0001-01-01") (given-thrown (.plusDays (DateDate/of 1 1 1) -1) :message := "Invalid value for Year (valid values 1 - 9999): 0") @@ -330,72 +298,72 @@ (deftest date-time-test (testing "date-time?" (are [date-time] (system/date-time? date-time) - #system/date-time"2020" - #system/date-time"2020-01" - #system/date-time"2020-01-01" + #system/date-time "2020" + #system/date-time "2020-01" + #system/date-time "2020-01-01" (system/date-time 2020 1 1 0 0 0 0) (system/date-time 2020 1 1 0 0 0 0 ZoneOffset/UTC)) (are [x] (not (system/date-time? x)) nil - #system/date"2020")) + #system/date "2020")) (testing "equals" (are [a b] (= a b) - #system/date-time"2020" #system/date-time"2020" - #system/date-time"2020-01" #system/date-time"2020-01") + #system/date-time "2020" #system/date-time "2020" + #system/date-time "2020-01" #system/date-time "2020-01") (are [a b] (not= a b) - #system/date-time"2020" #system/date-time"2021" - #system/date-time"2020" #system/date"2020" + #system/date-time "2020" #system/date-time "2021" + #system/date-time "2020" #system/date "2020" - #system/date-time"2020-01" #system/date-time"2020-02" - #system/date-time"2020-01" #system/date"2020-01" + #system/date-time "2020-01" #system/date-time "2020-02" + #system/date-time "2020-01" #system/date "2020-01" - #system/date-time"2020-01-01" - #system/date-time"2020-01-02" + #system/date-time "2020-01-01" + #system/date-time "2020-01-02" - #system/date-time"2020-01-01" - #system/date"2020-01-01")) + #system/date-time "2020-01-01" + #system/date "2020-01-01")) (testing "comparable" (are [a b] (pos? (compare a b)) - #system/date-time"2021" #system/date-time"2020" - #system/date-time"2020-02" #system/date-time"2020-01" - #system/date-time"2020-01-02" #system/date-time"2020-01-01")) + #system/date-time "2021" #system/date-time "2020" + #system/date-time "2020-02" #system/date-time "2020-01" + #system/date-time "2020-01-02" #system/date-time "2020-01-01")) (testing "system equals" (testing "same precision" (testing "within date-time" (are [a b pred] (pred (system/equals a b)) - #system/date-time"2020" #system/date-time"2020" true? - #system/date-time"2020" #system/date-time"2021" false? - #system/date-time"2020" nil nil? - #system/date-time"2021" #system/date-time"2020" false? - #system/date-time"2021" #system/date-time"2021" true? - #system/date-time"2021" nil nil? - nil #system/date-time"2020" nil? - nil #system/date-time"2021" nil? + #system/date-time "2020" #system/date-time "2020" true? + #system/date-time "2020" #system/date-time "2021" false? + #system/date-time "2020" nil nil? + #system/date-time "2021" #system/date-time "2020" false? + #system/date-time "2021" #system/date-time "2021" true? + #system/date-time "2021" nil nil? + nil #system/date-time "2020" nil? + nil #system/date-time "2021" nil? nil nil nil? - #system/date-time"2020-01" #system/date-time"2020-01" true? - #system/date-time"2020-01" #system/date-time"2020-02" false? - #system/date-time"2020-01" nil nil? - #system/date-time"2020-02" #system/date-time"2020-01" false? - #system/date-time"2020-02" #system/date-time"2020-02" true? - #system/date-time"2020-02" nil nil? - nil #system/date-time"2020-01" nil? - nil #system/date-time"2020-02" nil? + #system/date-time "2020-01" #system/date-time "2020-01" true? + #system/date-time "2020-01" #system/date-time "2020-02" false? + #system/date-time "2020-01" nil nil? + #system/date-time "2020-02" #system/date-time "2020-01" false? + #system/date-time "2020-02" #system/date-time "2020-02" true? + #system/date-time "2020-02" nil nil? + nil #system/date-time "2020-01" nil? + nil #system/date-time "2020-02" nil? nil nil nil? - #system/date-time"2020-01-01" #system/date-time"2020-01-01" true? - #system/date-time"2020-01-01" #system/date-time"2020-01-02" false? - #system/date-time"2020-01-01" nil nil? - #system/date-time"2020-01-02" #system/date-time"2020-01-01" false? - #system/date-time"2020-01-02" #system/date-time"2020-01-02" true? - #system/date-time"2020-01-02" nil nil? - nil #system/date-time"2020-01-01" nil? - nil #system/date-time"2020-01-02" nil? + #system/date-time "2020-01-01" #system/date-time "2020-01-01" true? + #system/date-time "2020-01-01" #system/date-time "2020-01-02" false? + #system/date-time "2020-01-01" nil nil? + #system/date-time "2020-01-02" #system/date-time "2020-01-01" false? + #system/date-time "2020-01-02" #system/date-time "2020-01-02" true? + #system/date-time "2020-01-02" nil nil? + nil #system/date-time "2020-01-01" nil? + nil #system/date-time "2020-01-02" nil? nil nil nil? (system/date-time 2020 1 1 0 0 0 0) (system/date-time 2020 1 1 0 0 0 0) true? @@ -420,120 +388,121 @@ (testing "with date" (are [a b pred] (pred (system/equals a b)) - #system/date-time"2020" #system/date"2020" true? - #system/date-time"2020" #system/date"2021" false? - #system/date-time"2020-01" #system/date"2020-01" true? - #system/date-time"2020-02" #system/date"2020-01" false? - #system/date-time"2020-01-01" #system/date"2020-01-01" true? - #system/date-time"2020-01-02" #system/date"2020-01-01" false?))) + #system/date-time "2020" #system/date "2020" true? + #system/date-time "2020" #system/date "2021" false? + #system/date-time "2020-01" #system/date "2020-01" true? + #system/date-time "2020-02" #system/date "2020-01" false? + #system/date-time "2020-01-01" #system/date "2020-01-01" true? + #system/date-time "2020-01-02" #system/date "2020-01-01" false?))) (testing "different precision" (testing "within date-time" (are [a b pred] (pred (system/equals a b)) - #system/date-time"2020" #system/date-time"2020-01" nil? - #system/date-time"2020-01" #system/date-time"2020" nil? - #system/date-time"2020-01" #system/date-time"2020-01-01" nil? - #system/date-time"2020-01-01" #system/date-time"2020-01" nil? + #system/date-time "2020" #system/date-time "2020-01" nil? + #system/date-time "2020-01" #system/date-time "2020" nil? + #system/date-time "2020-01" #system/date-time "2020-01-01" nil? + #system/date-time "2020-01-01" #system/date-time "2020-01" nil? (system/date-time 2020 1 1 0 0 0 0) (system/date-time 2020 1 1 0 0 0 0 ZoneOffset/UTC) nil? (system/date-time 2020 1 1 0 0 0 0 ZoneOffset/UTC) (system/date-time 2020 1 1 0 0 0 0) nil?)) (testing "with date" (are [a b pred] (pred (system/equals a b)) - #system/date-time"2020-01" #system/date"2020" nil? - #system/date-time"2020" #system/date"2020-01" nil? - #system/date-time"2020-01-01" #system/date"2020-01" nil? - #system/date-time"2020-01" #system/date"2020-01-01" nil?)))) + #system/date-time "2020-01" #system/date "2020" nil? + #system/date-time "2020" #system/date "2020-01" nil? + #system/date-time "2020-01-01" #system/date "2020-01" nil? + #system/date-time "2020-01" #system/date "2020-01-01" nil?)))) (testing "hash-code" (testing "DateTimeYear hash-code equals that of DateYear" - (is (= (.hashCode #system/date-time"2020") - (.hashCode #system/date"2020")))) + (is (= (.hashCode #system/date-time "2020") + (.hashCode #system/date "2020")))) (testing "DateTimeYearMonth hash-code equals that of DateYearMonth" - (is (= (.hashCode #system/date-time"2020-01") - (.hashCode #system/date"2020-01")))) + (is (= (.hashCode #system/date-time "2020-01") + (.hashCode #system/date "2020-01")))) (testing "DateTimeDate hash-code equals that of DateDate" - (is (= (.hashCode #system/date-time"2020-01-01") - (.hashCode #system/date"2020-01-01"))))) + (is (= (.hashCode #system/date-time "2020-01-01") + (.hashCode #system/date "2020-01-01"))))) (testing "Temporal" (testing "plus" (testing "year" (are [o amount res] (= res (time/plus o amount)) - #system/date-time"2020" (time/years 0) #system/date-time"2020" - #system/date-time"2020" (time/years 1) #system/date-time"2021")) + #system/date-time "2020" (time/years 0) #system/date-time "2020" + #system/date-time "2020" (time/years 1) #system/date-time "2021")) (testing "year-month" (are [o amount res] (= res (time/plus o amount)) - #system/date-time"2020-01" (time/months 0) #system/date-time"2020-01" - #system/date-time"2020-01" (time/months 1) #system/date-time"2020-02")) + #system/date-time "2020-01" (time/months 0) #system/date-time "2020-01" + #system/date-time "2020-01" (time/months 1) #system/date-time "2020-02")) (testing "year-month-day" (are [o amount res] (= res (time/plus o amount)) - #system/date-time"2020-01-01" (time/days 0) #system/date-time"2020-01-01" - #system/date-time"2020-01-01" (time/days 1) #system/date-time"2020-01-02"))) + #system/date-time "2020-01-01" (time/days 0) #system/date-time "2020-01-01" + #system/date-time "2020-01-01" (time/days 1) #system/date-time "2020-01-02"))) (testing "time-between" (testing "year" (are [o e n] (= n (time/time-between o e :years)) - #system/date-time"2020" #system/date-time"2020" 0 - #system/date-time"2020" #system/date-time"2021" 1 - #system/date-time"2020" #system/date"2020" 0 - #system/date-time"2020" #system/date"2021" 1 - #system/date"2020" #system/date-time"2020" 0 - #system/date"2020" #system/date-time"2021" 1)) + #system/date-time "2020" #system/date-time "2020" 0 + #system/date-time "2020" #system/date-time "2021" 1 + #system/date-time "2020" #system/date "2020" 0 + #system/date-time "2020" #system/date "2021" 1 + #system/date "2020" #system/date-time "2020" 0 + #system/date "2020" #system/date-time "2021" 1)) (testing "year-month" (are [o e n] (= n (time/time-between o e :months)) - #system/date-time"2020-01" - #system/date-time"2020-01" 0 - #system/date-time"2020-01" - #system/date-time"2020-02" 1 - #system/date-time"2020-01" #system/date"2020-01" 0 - #system/date-time"2020-01" #system/date"2020-02" 1 - #system/date"2020-01" #system/date-time"2020-01" 0 - #system/date"2020-01" #system/date-time"2020-02" 1)) + #system/date-time "2020-01" + #system/date-time "2020-01" 0 + #system/date-time "2020-01" + #system/date-time "2020-02" 1 + #system/date-time "2020-01" #system/date "2020-01" 0 + #system/date-time "2020-01" #system/date "2020-02" 1 + #system/date "2020-01" #system/date-time "2020-01" 0 + #system/date "2020-01" #system/date-time "2020-02" 1)) (testing "year-month-day" (are [o e n] (= n (time/time-between o e :days)) - #system/date-time"2020-01-01" - #system/date-time"2020-01-01" 0 - #system/date-time"2020-01-01" - #system/date-time"2020-01-02" 1 - #system/date-time"2020-01-01" #system/date"2020-01-01" 0 - #system/date-time"2020-01-01" #system/date"2020-01-02" 1 - #system/date"2020-01-01" #system/date-time"2020-01-01" 0 - #system/date"2020-01-01" #system/date-time"2020-01-02" 1)))) + #system/date-time "2020-01-01" + #system/date-time "2020-01-01" 0 + #system/date-time "2020-01-01" + #system/date-time "2020-01-02" 1 + #system/date-time "2020-01-01" #system/date "2020-01-01" 0 + #system/date-time "2020-01-01" #system/date "2020-01-02" 1 + #system/date "2020-01-01" #system/date-time "2020-01-01" 0 + #system/date "2020-01-01" #system/date-time "2020-01-02" 1)))) (testing "TemporalAccessor" (are [o ps] (every? #(time/supports? o %) ps) - #system/date-time"2020" + #system/date-time "2020" [:year] - #system/date-time"2020-01" + #system/date-time "2020-01" [:year :month-of-year] - #system/date-time"2020-01-01" + #system/date-time "2020-01-01" [:year :month-of-year :day-of-month]) (are [o p v] (= v (time/value (time/property o p))) - #system/date-time"2020" :year 2020 - #system/date-time"2020-01" :year 2020 - #system/date-time"2020-01" :month-of-year 1 - #system/date-time"2020-01-02" :year 2020 - #system/date-time"2020-01-02" :month-of-year 1 - #system/date-time"2020-01-02" :day-of-month 2)) + #system/date-time "2020" :year 2020 + #system/date-time "2020-01" :year 2020 + #system/date-time "2020-01" :month-of-year 1 + #system/date-time "2020-01-02" :year 2020 + #system/date-time "2020-01-02" :month-of-year 1 + #system/date-time "2020-01-02" :day-of-month 2)) (testing "print" (are [date s] (= (pr-str date) s) - #system/date-time"2020" "#system/date-time\"2020\"" - #system/date-time"2020-01" "#system/date-time\"2020-01\"" - #system/date-time"2020-01-02" "#system/date-time\"2020-01-02\"" - #system/date-time"2020-12-31T23:59:59" "#system/date-time\"2020-12-31T23:59:59\"" - #system/date-time"2020-12-31T23:59:59Z" "#system/date-time\"2020-12-31T23:59:59Z\"" - #system/date-time"2020-12-31T23:59:59.001" "#system/date-time\"2020-12-31T23:59:59.001\"" - #system/date-time"2020-12-31T23:59:59.001Z" "#system/date-time\"2020-12-31T23:59:59.001Z\""))) + #system/date-time "2020" "#system/date-time \"2020\"" + #system/date-time "2020-01" "#system/date-time \"2020-01\"" + #system/date-time "2020-01-02" "#system/date-time \"2020-01-02\"" + #system/date-time "2020-01-02T00:00:00" "#system/date-time \"2020-01-02T00:00:00\"" + #system/date-time "2020-12-31T23:59:59" "#system/date-time \"2020-12-31T23:59:59\"" + #system/date-time "2020-12-31T23:59:59Z" "#system/date-time \"2020-12-31T23:59:59Z\"" + #system/date-time "2020-12-31T23:59:59.001" "#system/date-time \"2020-12-31T23:59:59.001\"" + #system/date-time "2020-12-31T23:59:59.001Z" "#system/date-time \"2020-12-31T23:59:59.001Z\""))) (deftest parse-date-time-test (testing "valid" @@ -550,6 +519,7 @@ "2020-01-02T03:04-01:00" (system/date-time 2020 1 2 3 4 0 0 (ZoneOffset/ofHours -1)) "2020-01-02T03:04:05-01:00" (system/date-time 2020 1 2 3 4 5 0 (ZoneOffset/ofHours -1)) "2020-01-02T03:04:05+01:00" (system/date-time 2020 1 2 3 4 5 0 (ZoneOffset/ofHours 1)) + "0001-01-01T00:00:00Z" (system/date-time 1 1 1 0 0 0 0 ZoneOffset/UTC) "2020-01-02T03:04:05.006Z" (system/date-time 2020 1 2 3 4 5 6 ZoneOffset/UTC) "2020-01-02T03:04:05.006000Z" (system/date-time 2020 1 2 3 4 5 6 ZoneOffset/UTC) "2020-01-02T03:04:05.006-01:00" (system/date-time 2020 1 2 3 4 5 6 (ZoneOffset/ofHours -1)) @@ -585,44 +555,44 @@ (deftest date-time-lower-bound-test (testing "date-times with increasing precision have the same lower bound" (are [dt] (= 1577836800 (system/date-time-lower-bound dt)) - #system/date"2020" - #system/date"2020-01" - #system/date"2020-01-01" - #system/date-time"2020" - #system/date-time"2020-01" - #system/date-time"2020-01-01" - #system/date-time"2020-01-01T00:00:00" - #system/date-time"2020-01-01T00:00:00Z" - #system/date-time"2020-01-01T00:00:00.000" - #system/date-time"2020-01-01T00:00:00.000Z") + #system/date "2020" + #system/date "2020-01" + #system/date "2020-01-01" + #system/date-time "2020" + #system/date-time "2020-01" + #system/date-time "2020-01-01" + #system/date-time "2020-01-01T00:00:00" + #system/date-time "2020-01-01T00:00:00Z" + #system/date-time "2020-01-01T00:00:00.000" + #system/date-time "2020-01-01T00:00:00.000Z") (testing "nil has the same lower bound as year 1" - (is (= (system/date-time-lower-bound #system/date"0001") + (is (= (system/date-time-lower-bound #system/date "0001") (system/date-time-lower-bound nil)))))) (deftest date-time-upper-bound-test (testing "date-times with increasing precision have the same upper bound" (are [dt] (= 1609459199 (system/date-time-upper-bound dt)) - #system/date"2020" - #system/date"2020-12" - #system/date"2020-12-31" - #system/date-time"2020" - #system/date-time"2020-12" - #system/date-time"2020-12-31" - #system/date-time"2020-12-31T23:59:59" - #system/date-time"2020-12-31T23:59:59Z" - #system/date-time"2020-12-31T23:59:59.000" - #system/date-time"2020-12-31T23:59:59.000Z")) + #system/date "2020" + #system/date "2020-12" + #system/date "2020-12-31" + #system/date-time "2020" + #system/date-time "2020-12" + #system/date-time "2020-12-31" + #system/date-time "2020-12-31T23:59:59" + #system/date-time "2020-12-31T23:59:59Z" + #system/date-time "2020-12-31T23:59:59.000" + #system/date-time "2020-12-31T23:59:59.000Z")) (testing "nil has the same upper bound as year 9999" - (is (= (system/date-time-upper-bound #system/date"9999") + (is (= (system/date-time-upper-bound #system/date "9999") (system/date-time-upper-bound nil))))) (deftest date-time-year-test (testing "plus years" (are [date amount res] (= res (.plusYears date amount)) - (DateTimeYear/of 9998) 1 #system/date-time"9999" - (DateTimeYear/of 2) -1 #system/date-time"0001") + (DateTimeYear/of 9998) 1 #system/date-time "9999" + (DateTimeYear/of 2) -1 #system/date-time "0001") (given-thrown (.plusYears (DateTimeYear/of 1) -1) :message := "Invalid value for Year (valid values 1 - 9999): 0") @@ -633,10 +603,10 @@ (deftest date-time-year-month-test (testing "plus months" (are [date amount res] (= res (.plusMonths date amount)) - (DateTimeYearMonth/of 9998 12) 1 #system/date-time"9999-01" - (DateTimeYearMonth/of 9999 11) 1 #system/date-time"9999-12" - (DateTimeYearMonth/of 2 1) -1 #system/date-time"0001-12" - (DateTimeYearMonth/of 1 2) -1 #system/date-time"0001-01") + (DateTimeYearMonth/of 9998 12) 1 #system/date-time "9999-01" + (DateTimeYearMonth/of 9999 11) 1 #system/date-time "9999-12" + (DateTimeYearMonth/of 2 1) -1 #system/date-time "0001-12" + (DateTimeYearMonth/of 1 2) -1 #system/date-time "0001-01") (given-thrown (.plusMonths (DateTimeYearMonth/of 1 1) -1) :message := "Invalid value for Year (valid values 1 - 9999): 0") @@ -647,10 +617,10 @@ (deftest date-time-date-test (testing "plus days" (are [date amount res] (= res (.plusDays date amount)) - (DateTimeDate/of 9998 12 31) 1 #system/date-time"9999-01-01" - (DateTimeDate/of 9999 12 30) 1 #system/date-time"9999-12-31" - (DateTimeDate/of 2 1 1) -1 #system/date-time"0001-12-31" - (DateTimeDate/of 1 1 2) -1 #system/date-time"0001-01-01") + (DateTimeDate/of 9998 12 31) 1 #system/date-time "9999-01-01" + (DateTimeDate/of 9999 12 30) 1 #system/date-time "9999-12-31" + (DateTimeDate/of 2 1 1) -1 #system/date-time "0001-12-31" + (DateTimeDate/of 1 1 2) -1 #system/date-time "0001-01-01") (given-thrown (.plusDays (DateTimeDate/of 1 1 1) -1) :message := "Invalid value for Year (valid values 1 - 9999): 0") @@ -665,7 +635,7 @@ (are [x] (false? (system/time? x)) nil - #system/date-time"2020")) + #system/date-time "2020")) (testing "type" (is (= :system/time (system/type (LocalTime/of 0 0 0))))) @@ -686,7 +656,13 @@ nil nil nil? (LocalTime/of 0 0 0) (Object.) false? - (Object.) (LocalTime/of 0 0 0) false?))) + (Object.) (LocalTime/of 0 0 0) false?)) + + (testing "print" + (are [date s] (= (pr-str date) s) + #system/time "03:04:00" "#system/time \"03:04:00\"" + #system/time "03:04:05" "#system/time \"03:04:05\"" + #system/time "03:04:05.001" "#system/time \"03:04:05.001\""))) (deftest parse-time-test (testing "valid" diff --git a/modules/fhir-structure/test/blaze/fhir/spec/type_test.clj b/modules/fhir-structure/test/blaze/fhir/spec/type_test.clj index e7494924a..2ae09cf44 100644 --- a/modules/fhir-structure/test/blaze/fhir/spec/type_test.clj +++ b/modules/fhir-structure/test/blaze/fhir/spec/type_test.clj @@ -1,33 +1,28 @@ (ns blaze.fhir.spec.type-test (:require - [blaze.byte-buffer :as bb] [blaze.fhir.spec.generators :as fg] [blaze.fhir.spec.type :as type] [blaze.fhir.spec.type-spec] - [blaze.fhir.spec.type.protocols :as p] [blaze.fhir.spec.type.system.spec] [blaze.test-util :as tu :refer [satisfies-prop]] - [clojure.alpha.spec :as s2] [clojure.data.xml :as xml] [clojure.data.xml.name :as xml-name] [clojure.data.xml.node :as xml-node] [clojure.data.xml.prxml :as prxml] - [clojure.spec.alpha :as s] [clojure.spec.test.alpha :as st] [clojure.test :as test :refer [are deftest is testing]] [clojure.test.check.generators :as gen] [clojure.test.check.properties :as prop] - [jsonista.core :as j]) + [cognitect.anomalies :as anom] + [jsonista.core :as j] + [juxt.iota :refer [given]]) (:import - [com.fasterxml.jackson.core SerializableString] - [com.fasterxml.jackson.core.io JsonStringEncoder] + [blaze.fhir.spec.type Base Primitive] + [blaze.fhir.spec.type.system DateTimes] [com.fasterxml.jackson.databind ObjectMapper] [com.fasterxml.jackson.databind.module SimpleModule] [com.fasterxml.jackson.databind.ser.std StdSerializer] - [com.google.common.hash Hashing] - [java.nio.charset StandardCharsets] - [java.time Instant OffsetDateTime ZoneOffset] - [java.time.format DateTimeFormatter])) + [com.google.common.hash Hashing])) (xml-name/alias-uri 'f "http://hl7.org/fhir") (xml-name/alias-uri 'xhtml "http://www.w3.org/1999/xhtml") @@ -39,13 +34,13 @@ (defn murmur3 [x] (let [hasher (.newHasher (Hashing/murmur3_32_fixed))] - (type/hash-into x hasher) + (Base/hashInto x hasher) (Integer/toHexString (.asInt (.hash hasher))))) (def ^:private object-serializer (proxy [StdSerializer] [Object] (serialize [obj generator _] - (p/-serialize-json obj generator)))) + (.serializeJsonPrimitiveValue ^Primitive obj generator)))) (def ^:private fhir-module (doto (SimpleModule.) @@ -55,8 +50,10 @@ (doto (ObjectMapper.) (.registerModule fhir-module))) -(defn- gen-json-string [x] - (String. ^bytes (j/write-value-as-bytes x object-mapper) StandardCharsets/UTF_8)) +(defn- gen-json-value [x] + (-> (j/write-value-as-bytes x object-mapper) + (j/read-value (j/object-mapper {:decode-key-fn true + :bigdecimals true})))) (def ^:private sexp prxml/sexp-as-element) @@ -64,16 +61,16 @@ (sexp [nil {:value value}])) (def ^:private string-extension - #fhir/Extension{:url "foo" :value #fhir/string "bar"}) + #fhir/Extension{:url "foo" :valueString #fhir/string "bar"}) (defn interned? [x y] - (and (identical? x y) (p/-interned x) (p/-interned y))) + (and (identical? x y) (Base/isInterned x) (Base/isInterned y))) (defn not-interned? [x y] (and (= x y) (not (identical? x y)) - (not (p/-interned x)) - (not (p/-interned y)))) + (not (Base/isInterned x)) + (not (Base/isInterned y)))) (def ^:private internable-extension #fhir/Extension{:url "url-130945" :value #fhir/code "value-130953"}) @@ -81,42 +78,6 @@ (def ^:private not-internable-extension #fhir/Extension{:url "url-205325" :value #fhir/string "value-205336"}) -(deftest nil-test - (testing "all FhirType methods can be called on nil" - (testing "type" - (is (nil? (type/type nil)))) - - (testing "interned" - (is (interned? nil nil))) - - (testing "assoc id" - (is (nil? (type/assoc-id nil "foo")))) - - (testing "assoc extension" - (is (nil? (type/assoc-extension nil #fhir/Extension{:url "foo"})))) - - (testing "value" - (is (nil? (type/value nil)))) - - (testing "assoc value" - (is (nil? (type/assoc-value nil "foo")))) - - (testing "to-json" - (is (= "null" (gen-json-string nil)))) - - (testing "to-xml" - (is (nil? (type/to-xml nil)))) - - (testing "hash-into" - (is (= "0" (murmur3 nil)))) - - (testing "references" - (is (empty? (type/references nil)))))) - -(deftest Object-test - (testing "arbitrary instances have no fhir type" - (is (nil? (type/type (Object.)))))) - (deftest boolean-test (testing "boolean?" (are [x] (type/boolean? x) @@ -124,17 +85,19 @@ #fhir/boolean{:id "foo"})) (testing "invalid" - (is (s2/invalid? (st/with-instrument-disabled (type/boolean "a"))))) + (given (st/with-instrument-disabled (type/boolean "a")) + ::anom/category := ::anom/incorrect + ::anom/message := "Invalid boolean value `a`.")) (testing "type" - (are [x] (= :fhir/boolean (type/type x)) + (are [x] (= :fhir/boolean (:fhir/type x)) #fhir/boolean true #fhir/boolean{:id "foo"})) (testing "Boolean" (is (= #fhir/boolean{:value true} #fhir/boolean true))) - (testing "interned" + (testing "interning" (is (interned? #fhir/boolean true #fhir/boolean true)) (testing "with extension" @@ -155,41 +118,46 @@ (testing "assoc id" (testing "non-extended" - (is (= (type/assoc-id #fhir/boolean true "id-111030") + (is (= (assoc #fhir/boolean true :id "id-111030") #fhir/boolean{:id "id-111030" :value true}))) (testing "already extended" - (is (= (type/assoc-id #fhir/boolean{:id "foo"} "bar") + (is (= (assoc #fhir/boolean{:id "foo"} :id "bar") #fhir/boolean{:id "bar"})) - (is (= (type/assoc-id #fhir/boolean{:extension [#fhir/Extension{:url "foo"}]} "id-111902") + (is (= (assoc #fhir/boolean{:extension [#fhir/Extension{:url "foo"}]} :id "id-111902") #fhir/boolean{:id "id-111902" :extension [#fhir/Extension{:url "foo"}]})))) (testing "assoc extension" (testing "non-extended" - (is (= (type/assoc-extension #fhir/boolean true [#fhir/Extension{:url "foo"}]) + (is (= (assoc #fhir/boolean true :extension [#fhir/Extension{:url "foo"}]) #fhir/boolean{:extension [#fhir/Extension{:url "foo"}] :value true}))) (testing "already extended" - (is (= (type/assoc-extension #fhir/boolean{:id "id-111953"} [#fhir/Extension{:url "foo"}]) + (is (= (assoc #fhir/boolean{:id "id-111953"} :extension [#fhir/Extension{:url "foo"}]) #fhir/boolean{:id "id-111953" :extension [#fhir/Extension{:url "foo"}]})) - (is (= (type/assoc-extension #fhir/boolean{:extension [#fhir/Extension{:url "foo"}]} [#fhir/Extension{:url "bar"}]) + (is (= (assoc #fhir/boolean{:extension [#fhir/Extension{:url "foo"}]} :extension [#fhir/Extension{:url "bar"}]) #fhir/boolean{:extension [#fhir/Extension{:url "bar"}]})))) + (testing "id" + (are [x res] (= res (:id x)) + #fhir/boolean true nil + #fhir/boolean{:id "foo"} "foo")) + (testing "value" - (are [x] (true? (type/value x)) + (are [x] (true? (:value x)) #fhir/boolean true #fhir/boolean{:id "foo" :value true})) (testing "assoc value" - (is (= #fhir/boolean false (type/assoc-value #fhir/boolean true false))) + (is (= #fhir/boolean false (assoc #fhir/boolean true :value false)))) - (testing "invalid" - (is (s2/invalid? (st/with-instrument-disabled (type/assoc-value #fhir/boolean true "a")))))) + (testing "metadata" + (is (nil? (meta #fhir/boolean true))) + (is (= {:foo "bar"} (meta (with-meta #fhir/boolean true {:foo "bar"}))))) (testing "to-json" - (are [b s] (= s (gen-json-string b)) - true "true" - false "false")) + (is (true? (gen-json-value #fhir/boolean true))) + (is (false? (gen-json-value #fhir/boolean false)))) (testing "to-xml" (are [b s] (= (sexp-value s) (type/to-xml b)) @@ -217,31 +185,47 @@ #fhir/boolean{:extension [#fhir/Extension{:url "1"}]} "1293ee18" #fhir/boolean{:extension [#fhir/Extension{:url "0"} #fhir/Extension{:url "0"}]} "d1fda5de")) + (testing "mem-size" + (are [x mem-size] (= mem-size (Base/memSize x)) + #fhir/boolean true 0 + #fhir/boolean{:id "foo"} 80)) + (testing "references" (is (empty? (type/references #fhir/boolean true)))) (testing "print" - (are [boolean s] (= (pr-str boolean) s) - #fhir/boolean{:id "0"} "#fhir/boolean{:id \"0\"}"))) + (are [x s] (= (pr-str x) s) + #fhir/boolean true "#fhir/boolean true" + #fhir/boolean{:id "foo"} "#fhir/boolean{:id \"foo\"}"))) (deftest integer-test (testing "integer?" (are [x] (type/integer? x) + #fhir/integer -1 + #fhir/integer 0 #fhir/integer 1 #fhir/integer{:id "foo"})) (testing "invalid" - (is (s2/invalid? (st/with-instrument-disabled (type/integer "a"))))) + (given (st/with-instrument-disabled (type/integer "a")) + ::anom/category := ::anom/incorrect + ::anom/message := "Invalid integer value `a`.") + + (testing "too large" + (doseq [x [(inc Integer/MAX_VALUE) {:value (inc Integer/MAX_VALUE)}]] + (given (type/integer x) + ::anom/category := ::anom/incorrect + ::anom/message := "Invalid integer value `2147483648`.")))) (testing "type" - (are [x] (= :fhir/integer (type/type x)) + (are [x] (= :fhir/integer (:fhir/type x)) #fhir/integer 1 #fhir/integer{:id "foo"})) (testing "Integer" (is (= #fhir/integer{:value 1} #fhir/integer 1))) - (testing "interned" + (testing "interning" (is (not-interned? #fhir/integer 165519 #fhir/integer 165519)) (testing "with extension" @@ -260,39 +244,36 @@ (testing "assoc id" (testing "non-extended" - (is (= (type/assoc-id #fhir/integer 1 "id-111030") + (is (= (assoc #fhir/integer 1 :id "id-111030") #fhir/integer{:id "id-111030" :value 1}))) (testing "already extended" - (is (= (type/assoc-id #fhir/integer{:id "foo"} "bar") + (is (= (assoc #fhir/integer{:id "foo"} :id "bar") #fhir/integer{:id "bar"})) - (is (= (type/assoc-id #fhir/integer{:extension [#fhir/Extension{:url "foo"}]} "id-111902") + (is (= (assoc #fhir/integer{:extension [#fhir/Extension{:url "foo"}]} :id "id-111902") #fhir/integer{:id "id-111902" :extension [#fhir/Extension{:url "foo"}]})))) (testing "assoc extension" (testing "non-extended" - (is (= (type/assoc-extension #fhir/integer 1 [#fhir/Extension{:url "foo"}]) + (is (= (assoc #fhir/integer 1 :extension [#fhir/Extension{:url "foo"}]) #fhir/integer{:extension [#fhir/Extension{:url "foo"}] :value 1}))) (testing "already extended" - (is (= (type/assoc-extension #fhir/integer{:id "id-111953"} [#fhir/Extension{:url "foo"}]) + (is (= (assoc #fhir/integer{:id "id-111953"} :extension [#fhir/Extension{:url "foo"}]) #fhir/integer{:id "id-111953" :extension [#fhir/Extension{:url "foo"}]})) - (is (= (type/assoc-extension #fhir/integer{:extension [#fhir/Extension{:url "foo"}]} [#fhir/Extension{:url "bar"}]) + (is (= (assoc #fhir/integer{:extension [#fhir/Extension{:url "foo"}]} :extension [#fhir/Extension{:url "bar"}]) #fhir/integer{:extension [#fhir/Extension{:url "bar"}]})))) (testing "value" - (are [x] (= 1 (type/value x)) + (are [x] (= 1 (:value x)) #fhir/integer 1 #fhir/integer{:id "foo" :value 1})) (testing "assoc value" - (is (= #fhir/integer 2 (type/assoc-value #fhir/integer 1 2))) - - (testing "invalid" - (is (s2/invalid? (st/with-instrument-disabled (type/assoc-value #fhir/integer 1 "a")))))) + (is (= #fhir/integer 2 (assoc #fhir/integer 1 :value 2)))) (testing "to-json" - (is (= "1" (gen-json-string #fhir/integer 1)))) + (is (= 1 (gen-json-value #fhir/integer 1)))) (testing "to-xml" (is (= (sexp-value "1") (type/to-xml #fhir/integer 1)))) @@ -305,94 +286,18 @@ #fhir/integer{:id "foo" :value 0} "fdd4f126" #fhir/integer{:extension [#fhir/Extension{:url "foo"}]} "b353ef83")) - (testing "references" - (is (empty? (type/references #fhir/integer 0))))) - -(deftest long-test - (testing "long?" - (are [x] (type/long? x) - #fhir/long 1 - #fhir/long{:id "foo"})) - - (testing "invalid" - (is (s2/invalid? (st/with-instrument-disabled (type/long "a"))))) - - (testing "type" - (are [x] (= :fhir/long (type/type x)) - #fhir/long 1 - #fhir/long{:id "foo"})) - - (testing "Long" - (is (= #fhir/long{:value 1} #fhir/long 1))) - - (testing "interned" - (is (not-interned? #fhir/long 165519 #fhir/long 165519)) - - (testing "with extension" - (are [x y] (not-interned? x y) - (type/long {:extension [internable-extension] - :value 165519}) - (type/long {:extension [internable-extension] - :value 165519}) - - (type/long {:id "id-162329" :extension [internable-extension]}) - (type/long {:id "id-162329" :extension [internable-extension]})) - - (are [x y] (interned? x y) - (type/long {:extension [internable-extension]}) - (type/long {:extension [internable-extension]})))) - - (testing "assoc id" - (testing "non-extended" - (is (= (type/assoc-id #fhir/long 1 "id-111030") - #fhir/long{:id "id-111030" :value 1}))) - - (testing "already extended" - (is (= (type/assoc-id #fhir/long{:id "foo"} "bar") - #fhir/long{:id "bar"})) - (is (= (type/assoc-id #fhir/long{:extension #fhir/Extension{:url "foo"}} "id-111902") - #fhir/long{:id "id-111902" :extension #fhir/Extension{:url "foo"}})))) - - (testing "assoc extension" - (testing "non-extended" - (is (= (type/assoc-extension #fhir/long 1 #fhir/Extension{:url "foo"}) - #fhir/long{:extension #fhir/Extension{:url "foo"} :value 1}))) - - (testing "already extended" - (is (= (type/assoc-extension #fhir/long{:id "id-111953"} #fhir/Extension{:url "foo"}) - #fhir/long{:id "id-111953" :extension #fhir/Extension{:url "foo"}})) - (is (= (type/assoc-extension #fhir/long{:extension #fhir/Extension{:url "foo"}} #fhir/Extension{:url "bar"}) - #fhir/long{:extension #fhir/Extension{:url "bar"}})))) - - (testing "value" - (are [x] (= 1 (type/value x)) - #fhir/long 1 - #fhir/long{:id "foo" :value 1})) - - (testing "assoc value" - (is (= #fhir/long 2 (type/assoc-value #fhir/long 1 2))) - - (testing "invalid" - (is (s2/invalid? (st/with-instrument-disabled (type/assoc-value #fhir/long 1 "a")))))) - - (testing "to-json" - (is (= "1" (gen-json-string #fhir/long 1)))) - - (testing "to-xml" - (is (= (sexp-value "1") (type/to-xml #fhir/long 1)))) - - (testing "hash-into" - (are [i hex] (= hex (murmur3 i)) - #fhir/long 0 "9bc977cc" - #fhir/long 1 "fac0175c" - #fhir/long{:id "foo"} "943aa9b2" - #fhir/long{:id "foo" :value 0} "a5e71473" - #fhir/long{:extension [#fhir/Extension{:url "foo"}]} "589558b6")) + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/integer 0 24 + #fhir/integer{:id "foo"} 88)) (testing "references" - (are [x refs] (= refs (type/references x)) - #fhir/long 0 - nil))) + (is (empty? (type/references #fhir/integer 0)))) + + (testing "print" + (are [x s] (= (pr-str x) s) + #fhir/integer 0 "#fhir/integer 0" + #fhir/integer{:id "foo"} "#fhir/integer{:id \"foo\"}"))) (deftest string-test (testing "string?" @@ -401,60 +306,60 @@ #fhir/string{:id "foo"})) (testing "type" - (are [x] (= :fhir/string (type/type x)) + (are [x] (= :fhir/string (:fhir/type x)) #fhir/string "" #fhir/string{:id "foo"})) (testing "string" (is (= #fhir/string{:value "181312"} #fhir/string "181312"))) - (testing "interned" - (is (not-interned? (String. "165645") (String. "165645"))) + (testing "interning" + (are [x y] (interned? x y) + (type/string {:extension [internable-extension]}) + (type/string {:extension [internable-extension]}) - (is (identical? (type/intern-string (String. "165645")) - (type/intern-string (String. "165645")))) + #fhir/string "1234" #fhir/string "1234" - (testing "with extension" - (are [x y] (not-interned? x y) - (type/string {:extension [internable-extension] :value "174230"}) - (type/string {:extension [internable-extension] :value "174230"})) + #fhir/string "1234" (assoc #fhir/string "5678" :value "1234")) - (are [x y] (interned? x y) - (type/string {:extension [internable-extension]}) - (type/string {:extension [internable-extension]})))) + (are [x y] (not-interned? x y) + #fhir/string "165645" #fhir/string "165645" + + (type/string {:extension [internable-extension] :value "174230"}) + (type/string {:extension [internable-extension] :value "174230"}))) (testing "assoc id" - (testing "non-extended" - (is (= (type/assoc-id #fhir/string "165645" "id-111030") - #fhir/string{:id "id-111030" :value "165645"}))) + (are [s id r] (= r (assoc s :id id)) + #fhir/string "165645" "id-111030" + #fhir/string{:id "id-111030" :value "165645"} - (testing "already extended" - (is (= (type/assoc-id #fhir/string{:id "foo"} "bar") - #fhir/string{:id "bar"})) - (is (= (type/assoc-id #fhir/string{:extension [#fhir/Extension{:url "foo"}]} "id-111902") - #fhir/string{:id "id-111902" :extension [#fhir/Extension{:url "foo"}]})))) + #fhir/string{:id "foo"} "bar" + #fhir/string{:id "bar"} + + #fhir/string{:extension [#fhir/Extension{:url "foo"}]} "id-111902" + #fhir/string{:id "id-111902" :extension [#fhir/Extension{:url "foo"}]})) (testing "assoc extension" (testing "non-extended" - (is (= (type/assoc-extension #fhir/string "165645" [#fhir/Extension{:url "foo"}]) + (is (= (assoc #fhir/string "165645" :extension [#fhir/Extension{:url "foo"}]) #fhir/string{:extension [#fhir/Extension{:url "foo"}] :value "165645"}))) (testing "already extended" - (is (= (type/assoc-extension #fhir/string{:id "id-111953"} [#fhir/Extension{:url "foo"}]) + (is (= (assoc #fhir/string{:id "id-111953"} :extension [#fhir/Extension{:url "foo"}]) #fhir/string{:id "id-111953" :extension [#fhir/Extension{:url "foo"}]})) - (is (= (type/assoc-extension #fhir/string{:extension [#fhir/Extension{:url "foo"}]} [#fhir/Extension{:url "bar"}]) + (is (= (assoc #fhir/string{:extension [#fhir/Extension{:url "foo"}]} :extension [#fhir/Extension{:url "bar"}]) #fhir/string{:extension [#fhir/Extension{:url "bar"}]})))) (testing "value" - (are [x] (= "175227" (type/value x)) + (are [x] (= "175227" (:value x)) #fhir/string "175227" #fhir/string{:value "175227"})) (testing "assoc value" - (is (= #fhir/string "bar" (type/assoc-value #fhir/string "foo" "bar")))) + (is (= #fhir/string "bar" (assoc #fhir/string "foo" :value "bar")))) (testing "to-json" - (is (= "\"105406\"" (gen-json-string #fhir/string "105406")))) + (is (= "105406" (gen-json-value #fhir/string "105406")))) (testing "to-xml" (is (= (sexp-value "121344") (type/to-xml #fhir/string "121344")))) @@ -471,13 +376,33 @@ #fhir/string{:id "foo" :value "foo"} "28b14e8f" #fhir/string{:extension [#fhir/Extension{:url "foo"}]} "b2f98d95")) + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/string{} 0 + #fhir/string{:id "foo"} 80 + #fhir/string{:extension [#fhir/Extension{:url "foo"}]} 0 + #fhir/string "" 0 + #fhir/string "1234" 0 + #fhir/string "12345" 64 + #fhir/string "123456" 64 + #fhir/string "1234567" 64 + #fhir/string "12345678" 64 + #fhir/string "123456789" 64 + #fhir/string "1234567890" 64 + #fhir/string "12345678901" 64 + #fhir/string "123456789012" 64 + #fhir/string "1234567890123" 72)) + (testing "references" (is (empty? (type/references #fhir/string "151736")))) - (testing "toString" - (satisfies-prop 10 - (prop/for-all [value fg/string-value] - (= value (str (type/string value))))))) + (testing "print" + (are [x s] (= (pr-str x) s) + #fhir/string "1234" "#fhir/string-interned \"1234\"" + #fhir/string-interned "12345" "#fhir/string-interned \"12345\"" + #fhir/string "142600" "#fhir/string \"142600\"" + #fhir/string{:id "0"} "#fhir/string{:id \"0\"}" + #fhir/string{:extension [#fhir/Extension{:url "foo"}]} "#fhir/string-interned{:extension [#fhir/Extension{:url \"foo\"}]}"))) (deftest decimal-test (testing "decimal?" @@ -485,58 +410,55 @@ #fhir/decimal 1M #fhir/decimal{:id "foo"})) - (testing "invalid" - (is (s2/invalid? (st/with-instrument-disabled (type/decimal 1.1)))) - (is (s2/invalid? (st/with-instrument-disabled (type/decimal "a"))))) - (testing "type" - (are [x] (= :fhir/decimal (type/type x)) + (are [x] (= :fhir/decimal (:fhir/type x)) #fhir/decimal 1M #fhir/decimal{:id "foo"})) (testing "Decimal" (is (= #fhir/decimal{:value 1M} #fhir/decimal 1M))) - (testing "interned" + (testing "interning" (is (not-interned? #fhir/decimal 165746M #fhir/decimal 165746M))) (testing "assoc id" (testing "non-extended" - (is (= (type/assoc-id #fhir/decimal 1M "id-111030") + (is (= (assoc #fhir/decimal 1M :id "id-111030") #fhir/decimal{:id "id-111030" :value 1M}))) (testing "already extended" - (is (= (type/assoc-id #fhir/decimal{:id "foo"} "bar") + (is (= (assoc #fhir/decimal{:id "foo"} :id "bar") #fhir/decimal{:id "bar"})) - (is (= (type/assoc-id #fhir/decimal{:extension [#fhir/Extension{:url "foo"}]} "id-111902") + (is (= (assoc #fhir/decimal{:extension [#fhir/Extension{:url "foo"}]} :id "id-111902") #fhir/decimal{:id "id-111902" :extension [#fhir/Extension{:url "foo"}]})))) (testing "assoc extension" (testing "non-extended" - (is (= (type/assoc-extension #fhir/decimal 1M [#fhir/Extension{:url "foo"}]) + (is (= (assoc #fhir/decimal 1M :extension [#fhir/Extension{:url "foo"}]) #fhir/decimal{:extension [#fhir/Extension{:url "foo"}] :value 1M}))) (testing "already extended" - (is (= (type/assoc-extension #fhir/decimal{:id "id-111953"} [#fhir/Extension{:url "foo"}]) + (is (= (assoc #fhir/decimal{:id "id-111953"} :extension [#fhir/Extension{:url "foo"}]) #fhir/decimal{:id "id-111953" :extension [#fhir/Extension{:url "foo"}]})) - (is (= (type/assoc-extension #fhir/decimal{:extension [#fhir/Extension{:url "foo"}]} [#fhir/Extension{:url "bar"}]) + (is (= (assoc #fhir/decimal{:extension [#fhir/Extension{:url "foo"}]} :extension [#fhir/Extension{:url "bar"}]) #fhir/decimal{:extension [#fhir/Extension{:url "bar"}]})))) (testing "value" - (are [x] (= 1M (type/value x)) + (are [x] (= 1M (:value x)) #fhir/decimal 1M #fhir/decimal{:id "foo" :value 1M})) (testing "assoc value" - (is (= #fhir/decimal 2M (type/assoc-value #fhir/decimal 1M 2M))) + (is (= #fhir/decimal 2M (assoc #fhir/decimal 1M :value 2M)))) - (testing "invalid" - (is (s2/invalid? (st/with-instrument-disabled (type/assoc-value #fhir/decimal 1M "a")))))) + (testing "merge" + (is (= #fhir/decimal 2M (merge #fhir/decimal 1M {:value 2M}))) + (is (= #fhir/decimal{:id "id-153510" :value 2M} (merge #fhir/decimal 1M {:id "id-153510" :value 2M})))) (testing "to-json" - (are [decimal json] (= json (gen-json-string decimal)) - 1M "1" - 1.1M "1.1")) + (are [decimal json] (= json (gen-json-value decimal)) + #fhir/decimal 1M 1 + #fhir/decimal 1.1M 1.1M)) (testing "to-xml" (is (= (sexp-value "1.1") (type/to-xml #fhir/decimal 1.1M)))) @@ -550,8 +472,18 @@ #fhir/decimal{:id "foo" :value 0M} "4e9f9211" #fhir/decimal{:extension [#fhir/Extension{:url "foo"}]} "df35c8c9")) + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/decimal 0M 48 + #fhir/decimal{:id "foo"} 80)) + (testing "references" - (is (empty? (type/references #fhir/decimal 0M))))) + (is (empty? (type/references #fhir/decimal 0M)))) + + (testing "print" + (are [x s] (= (pr-str x) s) + #fhir/decimal 0M "#fhir/decimal 0M" + #fhir/decimal{:id "foo"} "#fhir/decimal{:id \"foo\"}"))) (deftest uri-test (testing "uri?" @@ -560,67 +492,69 @@ #fhir/uri{:id "foo"})) (testing "type" - (are [x] (= :fhir/uri (type/type x)) + (are [x] (= :fhir/uri (:fhir/type x)) #fhir/uri "" #fhir/uri{:id "foo"})) (testing "uri" (is (= #fhir/uri{:value "181424"} #fhir/uri "181424"))) - (testing "interned" - (is (interned? #fhir/uri "165823" #fhir/uri "165823")) + (testing "interning" + (is (interned? #fhir/uri-interned "165823" #fhir/uri-interned "165823")) + (is (interned? (type/uri-interned {:extension [] :value "145932"}) + (type/uri-interned "145932"))) (testing "with extension" (are [x y] (interned? x y) - (type/uri {:extension [internable-extension]}) - (type/uri {:extension [internable-extension]}) + (type/uri-interned {:extension [internable-extension]}) + (type/uri-interned {:extension [internable-extension]}) - (type/uri {:extension [internable-extension] :value "185838"}) - (type/uri {:extension [internable-extension] :value "185838"})) + (type/uri-interned {:extension [internable-extension] :value "185838"}) + (type/uri-interned {:extension [internable-extension] :value "185838"})) (are [x y] (not-interned? x y) - (type/uri {:extension [not-internable-extension]}) - (type/uri {:extension [not-internable-extension]}) + (type/uri-interned {:extension [not-internable-extension]}) + (type/uri-interned {:extension [not-internable-extension]}) - (type/uri {:extension [not-internable-extension] :value "185838"}) - (type/uri {:extension [not-internable-extension] :value "185838"})))) + (type/uri-interned {:extension [not-internable-extension] :value "185838"}) + (type/uri-interned {:extension [not-internable-extension] :value "185838"})))) (testing "assoc id" (testing "non-extended" - (is (= (type/assoc-id #fhir/uri "165645" "id-111030") + (is (= (assoc #fhir/uri "165645" :id "id-111030") #fhir/uri{:id "id-111030" :value "165645"}))) (testing "already extended" - (is (= (type/assoc-id #fhir/uri{:id "foo"} "bar") + (is (= (assoc #fhir/uri{:id "foo"} :id "bar") #fhir/uri{:id "bar"})) - (is (= (type/assoc-id #fhir/uri{:extension [#fhir/Extension{:url "foo"}]} "id-111902") + (is (= (assoc #fhir/uri{:extension [#fhir/Extension{:url "foo"}]} :id "id-111902") #fhir/uri{:id "id-111902" :extension [#fhir/Extension{:url "foo"}]})))) (testing "assoc extension" (testing "non-extended" - (is (= (type/assoc-extension #fhir/uri "165645" [#fhir/Extension{:url "foo"}]) + (is (= (assoc #fhir/uri "165645" :extension [#fhir/Extension{:url "foo"}]) #fhir/uri{:extension [#fhir/Extension{:url "foo"}] :value "165645"}))) (testing "already extended" - (is (= (type/assoc-extension #fhir/uri{:id "id-111953"} [#fhir/Extension{:url "foo"}]) + (is (= (assoc #fhir/uri{:id "id-111953"} :extension [#fhir/Extension{:url "foo"}]) #fhir/uri{:id "id-111953" :extension [#fhir/Extension{:url "foo"}]})) - (is (= (type/assoc-extension #fhir/uri{:extension [#fhir/Extension{:url "foo"}]} [#fhir/Extension{:url "bar"}]) + (is (= (assoc #fhir/uri{:extension [#fhir/Extension{:url "foo"}]} :extension [#fhir/Extension{:url "bar"}]) #fhir/uri{:extension [#fhir/Extension{:url "bar"}]})))) (testing "value" - (are [x] (= "105614" (type/value x) (:value x) (:value x ::foo)) + (are [x] (= "105614" (:value x) (:value x) (:value x ::foo)) #fhir/uri "105614" #fhir/uri{:id "foo" :value "105614"})) (testing "assoc value" - (is (= #fhir/uri "bar" (type/assoc-value #fhir/uri "foo" "bar")))) + (is (= #fhir/uri "bar" (assoc #fhir/uri "foo" :value "bar")))) (testing "lookup" (testing "other keys are not found" (is (= ::not-found (::other-key #fhir/uri "foo" ::not-found))))) (testing "to-json" - (is (= "\"105846\"" (gen-json-string #fhir/uri "105846")))) + (is (= "105846" (gen-json-value #fhir/uri "105846")))) (testing "to-xml" (is (= (sexp-value "105846") (type/to-xml #fhir/uri "105846")))) @@ -639,44 +573,21 @@ #fhir/uri{:id "foo" :value "foo"} "52e1c640" #fhir/uri{:extension [#fhir/Extension{:url "foo"}]} "435d07d9")) + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/uri "" 0 + #fhir/uri{:id "foo"} 80)) + (testing "references" (is (empty? (type/references #fhir/uri "151758")))) (testing "print" (are [uri s] (= (pr-str uri) s) - #fhir/uri "142600" "#fhir/uri\"142600\"" - #fhir/uri{:id "0"} "#fhir/uri{:id \"0\"}")) - - (testing "toString" - (satisfies-prop 10 - (prop/for-all [value fg/uri-value] - (= value (str (type/uri value)))))) - - (testing "SerializableString" - (testing "getValue" - (satisfies-prop 10 - (prop/for-all [value fg/uri-value] - (= value (.getValue ^SerializableString (type/uri value)))))) - - (testing "appendQuotedUTF8" - (satisfies-prop 100 - (prop/for-all [value fg/uri-value] - (let [expected-buffer (.quoteAsUTF8 (JsonStringEncoder/getInstance) value) - buffer (byte-array (count expected-buffer))] - (.appendQuotedUTF8 ^SerializableString (type/uri value) buffer 0) - (= (bb/wrap expected-buffer) (bb/wrap buffer)))))) - - (testing "asUnquotedUTF8" - (satisfies-prop 100 - (prop/for-all [value fg/uri-value] - (= (bb/wrap (.encodeAsUTF8 (JsonStringEncoder/getInstance) ^String value)) - (bb/wrap (.asUnquotedUTF8 ^SerializableString (type/uri value))))))) - - (testing "asQuotedUTF8" - (satisfies-prop 100 - (prop/for-all [value fg/uri-value] - (= (bb/wrap (.quoteAsUTF8 (JsonStringEncoder/getInstance) value)) - (bb/wrap (.asQuotedUTF8 ^SerializableString (type/uri value))))))))) + #fhir/uri "1426" "#fhir/uri-interned \"1426\"" + #fhir/uri-interned "142600" "#fhir/uri-interned \"142600\"" + #fhir/uri "142600" "#fhir/uri \"142600\"" + #fhir/uri{:id "0"} "#fhir/uri{:id \"0\"}" + #fhir/uri{:extension [#fhir/Extension{:url "foo"}]} "#fhir/uri-interned{:extension [#fhir/Extension{:url \"foo\"}]}"))) (deftest url-test (testing "url?" @@ -685,11 +596,11 @@ #fhir/url{})) (testing "type" - (are [x] (= :fhir/url (type/type x)) + (are [x] (= :fhir/url (:fhir/type x)) #fhir/url "" #fhir/url{})) - (testing "interned" + (testing "interning" (is (not-interned? #fhir/url "165852" #fhir/url "165852")) (testing "with extension" @@ -703,40 +614,40 @@ (testing "assoc id" (testing "non-extended" - (is (= (type/assoc-id #fhir/url "165645" "id-111030") + (is (= (assoc #fhir/url "165645" :id "id-111030") #fhir/url{:id "id-111030" :value "165645"}))) (testing "already extended" - (is (= (type/assoc-id #fhir/url{:id "foo"} "bar") + (is (= (assoc #fhir/url{:id "foo"} :id "bar") #fhir/url{:id "bar"})) - (is (= (type/assoc-id #fhir/url{:extension [#fhir/Extension{:url "foo"}]} "id-111902") + (is (= (assoc #fhir/url{:extension [#fhir/Extension{:url "foo"}]} :id "id-111902") #fhir/url{:id "id-111902" :extension [#fhir/Extension{:url "foo"}]})))) (testing "assoc extension" (testing "non-extended" - (is (= (type/assoc-extension #fhir/url "165645" [#fhir/Extension{:url "foo"}]) + (is (= (assoc #fhir/url "165645" :extension [#fhir/Extension{:url "foo"}]) #fhir/url{:extension [#fhir/Extension{:url "foo"}] :value "165645"}))) (testing "already extended" - (is (= (type/assoc-extension #fhir/url{:id "id-111953"} [#fhir/Extension{:url "foo"}]) + (is (= (assoc #fhir/url{:id "id-111953"} :extension [#fhir/Extension{:url "foo"}]) #fhir/url{:id "id-111953" :extension [#fhir/Extension{:url "foo"}]})) - (is (= (type/assoc-extension #fhir/url{:extension [#fhir/Extension{:url "foo"}]} [#fhir/Extension{:url "bar"}]) + (is (= (assoc #fhir/url{:extension [#fhir/Extension{:url "foo"}]} :extension [#fhir/Extension{:url "bar"}]) #fhir/url{:extension [#fhir/Extension{:url "bar"}]})))) (testing "value" - (are [x] (= "105614" (type/value x) (:value x) (:value x ::foo)) + (are [x] (= "105614" (:value x) (:value x) (:value x ::foo)) #fhir/url "105614" #fhir/url{:id "foo" :value "105614"})) (testing "assoc value" - (is (= #fhir/url "bar" (type/assoc-value #fhir/url "foo" "bar")))) + (is (= #fhir/url "bar" (assoc #fhir/url "foo" :value "bar")))) (testing "lookup" (testing "other keys are not found" (is (= ::not-found (::other-key #fhir/url "foo" ::not-found))))) (testing "to-json" - (is (= "\"105846\"" (gen-json-string #fhir/url "105846")))) + (is (= "105846" (gen-json-value #fhir/url "105846")))) (testing "to-xml" (is (= (sexp-value "105846") (type/to-xml #fhir/url "105846")))) @@ -755,27 +666,29 @@ #fhir/url{:id "foo" :value "foo"} "43940bd2" #fhir/url{:extension [#fhir/Extension{:url "foo"}]} "95f50bf4")) + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/url "" 56 + #fhir/url "1234" 56 + #fhir/url "12345" 64 + #fhir/url{:id "foo"} 80)) + (testing "references" (is (empty? (type/references #fhir/url "151809")))) (testing "print" (are [x s] (= (pr-str x) s) #fhir/url "142600" - "#fhir/url\"142600\"" + "#fhir/url \"142600\"" #fhir/url{:id "id-191655"} "#fhir/url{:id \"id-191655\"}" #fhir/url{:id "id-191655" :value "191802"} - "#fhir/url{:id \"id-191655\", :value \"191802\"}" + "#fhir/url{:id \"id-191655\" :value \"191802\"}" #fhir/url{:extension [#fhir/Extension{:url "url-191551"}]} - "#fhir/url{:extension [#fhir/Extension{:url \"url-191551\"}]}")) - - (testing "toString" - (satisfies-prop 10 - (prop/for-all [value fg/url-value] - (= value (str (type/url value))))))) + "#fhir/url{:extension [#fhir/Extension{:url \"url-191551\"}]}"))) (deftest canonical-test (testing "canonical?" @@ -784,14 +697,14 @@ #fhir/canonical{:id "foo"})) (testing "type" - (are [x] (= :fhir/canonical (type/type x)) + (are [x] (= :fhir/canonical (:fhir/type x)) #fhir/canonical "" #fhir/canonical{:id "foo"})) (testing "canonical" (is (= #fhir/canonical{:value "182040"} #fhir/canonical "182040"))) - (testing "interned" + (testing "interning" (is (interned? #fhir/canonical "165936" #fhir/canonical "165936")) (testing "with extension" @@ -811,40 +724,40 @@ (testing "assoc id" (testing "non-extended" - (is (= (type/assoc-id #fhir/canonical "165645" "id-111030") + (is (= (assoc #fhir/canonical "165645" :id "id-111030") #fhir/canonical{:id "id-111030" :value "165645"}))) (testing "already extended" - (is (= (type/assoc-id #fhir/canonical{:id "foo"} "bar") + (is (= (assoc #fhir/canonical{:id "foo"} :id "bar") #fhir/canonical{:id "bar"})) - (is (= (type/assoc-id #fhir/canonical{:extension [#fhir/Extension{:url "foo"}]} "id-111902") + (is (= (assoc #fhir/canonical{:extension [#fhir/Extension{:url "foo"}]} :id "id-111902") #fhir/canonical{:id "id-111902" :extension [#fhir/Extension{:url "foo"}]})))) (testing "assoc extension" (testing "non-extended" - (is (= (type/assoc-extension #fhir/canonical "165645" [#fhir/Extension{:url "foo"}]) + (is (= (assoc #fhir/canonical "165645" :extension [#fhir/Extension{:url "foo"}]) #fhir/canonical{:extension [#fhir/Extension{:url "foo"}] :value "165645"}))) (testing "already extended" - (is (= (type/assoc-extension #fhir/canonical{:id "id-111953"} [#fhir/Extension{:url "foo"}]) + (is (= (assoc #fhir/canonical{:id "id-111953"} :extension [#fhir/Extension{:url "foo"}]) #fhir/canonical{:id "id-111953" :extension [#fhir/Extension{:url "foo"}]})) - (is (= (type/assoc-extension #fhir/canonical{:extension [#fhir/Extension{:url "foo"}]} [#fhir/Extension{:url "bar"}]) + (is (= (assoc #fhir/canonical{:extension [#fhir/Extension{:url "foo"}]} :extension [#fhir/Extension{:url "bar"}]) #fhir/canonical{:extension [#fhir/Extension{:url "bar"}]})))) (testing "value" - (are [x] (= "105614" (type/value x) (:value x) (:value x ::foo)) + (are [x] (= "105614" (:value x) (:value x) (:value x ::foo)) #fhir/canonical "105614" #fhir/canonical{:id "foo" :value "105614"})) (testing "assoc value" - (is (= #fhir/canonical "bar" (type/assoc-value #fhir/canonical "foo" "bar")))) + (is (= #fhir/canonical "bar" (assoc #fhir/canonical "foo" :value "bar")))) (testing "lookup" (testing "other keys are not found" (is (= ::not-found (::other-key #fhir/canonical "foo" ::not-found))))) (testing "to-json" - (is (= "\"105846\"" (gen-json-string #fhir/canonical "105846")))) + (is (= "105846" (gen-json-value #fhir/canonical "105846")))) (testing "to-xml" (is (= (sexp-value "105846") (type/to-xml #fhir/canonical "105846")))) @@ -862,50 +775,25 @@ #fhir/canonical{:id "foo" :value "foo"} "83587524" #fhir/canonical{:extension [#fhir/Extension{:url "foo"}]} "3f1c8be1")) + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/canonical "" 0 + #fhir/canonical{:id "foo"} 80 + #fhir/canonical{:id "foo" :value ""} 152)) + (testing "references" (is (empty? (type/references #fhir/canonical "151819")))) (testing "print" (are [c s] (= s (pr-str c)) #fhir/canonical "142600" - "#fhir/canonical\"142600\"" + "#fhir/canonical \"142600\"" #fhir/canonical{:id "211202"} "#fhir/canonical{:id \"211202\"}" #fhir/canonical{:value "213644"} - "#fhir/canonical\"213644\"")) - - (testing "toString" - (satisfies-prop 10 - (prop/for-all [value fg/canonical-value] - (= value (str (type/canonical value)))))) - - (testing "SerializableString" - (testing "getValue" - (satisfies-prop 10 - (prop/for-all [value fg/canonical-value] - (= value (.getValue ^SerializableString (type/canonical value)))))) - - (testing "appendQuotedUTF8" - (satisfies-prop 100 - (prop/for-all [value fg/canonical-value] - (let [expected-buffer (.quoteAsUTF8 (JsonStringEncoder/getInstance) value) - buffer (byte-array (count expected-buffer))] - (.appendQuotedUTF8 ^SerializableString (type/canonical value) buffer 0) - (= (bb/wrap expected-buffer) (bb/wrap buffer)))))) - - (testing "asUnquotedUTF8" - (satisfies-prop 100 - (prop/for-all [value fg/canonical-value] - (= (bb/wrap (.encodeAsUTF8 (JsonStringEncoder/getInstance) ^String value)) - (bb/wrap (.asUnquotedUTF8 ^SerializableString (type/canonical value))))))) - - (testing "asQuotedUTF8" - (satisfies-prop 100 - (prop/for-all [value fg/canonical-value] - (= (bb/wrap (.quoteAsUTF8 (JsonStringEncoder/getInstance) value)) - (bb/wrap (.asQuotedUTF8 ^SerializableString (type/canonical value))))))))) + "#fhir/canonical \"213644\""))) (deftest base64Binary-test (testing "base64Binary?" @@ -914,14 +802,14 @@ #fhir/base64Binary{:id "foo"})) (testing "type" - (are [x] (= :fhir/base64Binary (type/type x)) + (are [x] (= :fhir/base64Binary (:fhir/type x)) #fhir/base64Binary "" #fhir/base64Binary{:id "foo"})) (testing "base64Binary" (is (= #fhir/base64Binary{:value "MTA1NjE0Cg=="} #fhir/base64Binary "MTA1NjE0Cg=="))) - (testing "interned" + (testing "interning" (is (not-interned? #fhir/base64Binary "MTA1NjE0Cg==" #fhir/base64Binary "MTA1NjE0Cg==")) (testing "with extension" @@ -935,40 +823,40 @@ (testing "assoc id" (testing "non-extended" - (is (= (type/assoc-id #fhir/base64Binary "MTA1NjE0Cg==" "id-111030") + (is (= (assoc #fhir/base64Binary "MTA1NjE0Cg==" :id "id-111030") #fhir/base64Binary{:id "id-111030" :value "MTA1NjE0Cg=="}))) (testing "already extended" - (is (= (type/assoc-id #fhir/base64Binary{:id "foo"} "bar") + (is (= (assoc #fhir/base64Binary{:id "foo"} :id "bar") #fhir/base64Binary{:id "bar"})) - (is (= (type/assoc-id #fhir/base64Binary{:extension [#fhir/Extension{:url "foo"}]} "id-111902") + (is (= (assoc #fhir/base64Binary{:extension [#fhir/Extension{:url "foo"}]} :id "id-111902") #fhir/base64Binary{:id "id-111902" :extension [#fhir/Extension{:url "foo"}]})))) (testing "assoc extension" (testing "non-extended" - (is (= (type/assoc-extension #fhir/base64Binary "MTA1NjE0Cg==" [#fhir/Extension{:url "foo"}]) + (is (= (assoc #fhir/base64Binary "MTA1NjE0Cg==" :extension [#fhir/Extension{:url "foo"}]) #fhir/base64Binary{:extension [#fhir/Extension{:url "foo"}] :value "MTA1NjE0Cg=="}))) (testing "already extended" - (is (= (type/assoc-extension #fhir/base64Binary{:id "id-111953"} [#fhir/Extension{:url "foo"}]) + (is (= (assoc #fhir/base64Binary{:id "id-111953"} :extension [#fhir/Extension{:url "foo"}]) #fhir/base64Binary{:id "id-111953" :extension [#fhir/Extension{:url "foo"}]})) - (is (= (type/assoc-extension #fhir/base64Binary{:extension [#fhir/Extension{:url "foo"}]} [#fhir/Extension{:url "bar"}]) + (is (= (assoc #fhir/base64Binary{:extension [#fhir/Extension{:url "foo"}]} :extension [#fhir/Extension{:url "bar"}]) #fhir/base64Binary{:extension [#fhir/Extension{:url "bar"}]})))) (testing "value" - (are [x] (= "MTA1NjE0Cg==" (type/value x) (:value x) (:value x ::foo)) + (are [x] (= "MTA1NjE0Cg==" (:value x) (:value x) (:value x ::foo)) #fhir/base64Binary "MTA1NjE0Cg==" #fhir/base64Binary{:id "foo" :value "MTA1NjE0Cg=="})) (testing "assoc value" - (is (= #fhir/base64Binary "bar" (type/assoc-value #fhir/base64Binary "foo" "bar")))) + (is (= #fhir/base64Binary "bar" (assoc #fhir/base64Binary "foo" :value "bar")))) (testing "lookup" (testing "other keys are not found" (is (= ::not-found (::other-key #fhir/base64Binary "foo" ::not-found))))) (testing "to-json" - (is (= "\"MTA1NjE0Cg==\"" (gen-json-string #fhir/base64Binary "MTA1NjE0Cg==")))) + (is (= "MTA1NjE0Cg==" (gen-json-value #fhir/base64Binary "MTA1NjE0Cg==")))) (testing "to-xml" (is (= (sexp-value "MTA1NjE0Cg==") (type/to-xml #fhir/base64Binary "MTA1NjE0Cg==")))) @@ -987,62 +875,59 @@ #fhir/base64Binary{:id "foo"} "331c84dc" #fhir/base64Binary{:extension [#fhir/Extension{:url "foo"}]} "4d9fc231")) + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/base64Binary "" 56 + #fhir/base64Binary "YQo" 56 + #fhir/base64Binary "MTA1NjE0Cg===" 72 + #fhir/base64Binary{:id "foo"} 80)) + (testing "references" (is (empty? (type/references #fhir/base64Binary "YQo=")))) (testing "print" - (is (= "#fhir/base64Binary\"YQo=\"" (pr-str #fhir/base64Binary "YQo=")))) - - (testing "toString" - (satisfies-prop 10 - (prop/for-all [value fg/base64Binary-value] - (= value (str (type/base64Binary value))))))) + (are [x s] (= (pr-str x) s) + #fhir/base64Binary "YQo" "#fhir/base64Binary \"YQo\"" + #fhir/base64Binary{:id "foo"} "#fhir/base64Binary{:id \"foo\"}"))) (deftest instant-test (testing "instant?" (are [x] (type/instant? x) - #fhir/instant "1970-01-02T00:00:00Z" - #fhir/instant "1970-01-02T00:00:00+01:00" + #fhir/instant #system/date-time "1970-01-02T00:00:00Z" + #fhir/instant #system/date-time "1970-01-02T00:00:00+01:00" #fhir/instant{:id "foo"} - #fhir/instant{:value "1970-01-02T00:00:00Z"} - #fhir/instant{:value "1970-01-02T00:00:00+01:00"})) - - (testing "invalid" - (is (s2/invalid? (type/instant "a")))) + #fhir/instant{:value #system/date-time "1970-01-02T00:00:00Z"} + #fhir/instant{:value #system/date-time "1970-01-02T00:00:00+01:00"})) (testing "type" - (are [x] (= :fhir/instant (type/type x)) - #fhir/instant "1970-01-02T00:00:00Z" - #fhir/instant "1970-01-02T00:00:00+01:00" + (are [x] (= :fhir/instant (:fhir/type x)) + #fhir/instant #system/date-time "1970-01-02T00:00:00Z" + #fhir/instant #system/date-time "1970-01-02T00:00:00+01:00" #fhir/instant{:id "foo"} - #fhir/instant{:value "1970-01-02T00:00:00Z"} - #fhir/instant{:value "1970-01-02T00:00:00+01:00"})) + #fhir/instant{:value #system/date-time "1970-01-02T00:00:00Z"} + #fhir/instant{:value #system/date-time "1970-01-02T00:00:00+01:00"})) (testing "with extension" (testing "without value" - (is (nil? (type/value #fhir/instant{:extension [#fhir/Extension{:url "url-130945"}]}))))) + (is (nil? (:value #fhir/instant{:extension [#fhir/Extension{:url "url-130945"}]}))))) (testing "instant" - (is (= #fhir/instant{:value "1970-01-02T00:00:00Z"} - #fhir/instant "1970-01-02T00:00:00Z")) - - (is (instance? Instant #fhir/instant "1970-01-02T00:00:00Z")) - - (is (instance? Instant (type/instant #system/date-time"2020-01-02T03:04:05Z")))) + (is (= #fhir/instant{:value #system/date-time "1970-01-02T00:00:00Z"} + #fhir/instant #system/date-time "1970-01-02T00:00:00Z"))) - (testing "interned" - (is (not-interned? #fhir/instant "2020-01-01T00:00:00+02:00" - #fhir/instant "2020-01-01T00:00:00+02:00")) + (testing "interning" + (is (not-interned? #fhir/instant #system/date-time "2020-01-01T00:00:00+02:00" + #fhir/instant #system/date-time "2020-01-01T00:00:00+02:00")) - (is (not-interned? #fhir/instant "1970-01-02T00:00:00Z" - #fhir/instant "1970-01-02T00:00:00Z")) + (is (not-interned? #fhir/instant #system/date-time "1970-01-02T00:00:00Z" + #fhir/instant #system/date-time "1970-01-02T00:00:00Z")) (testing "with extension" (are [x y] (not-interned? x y) (type/instant {:extension [internable-extension] - :value "1970-01-02T00:00:00Z"}) + :value #system/date-time "1970-01-02T00:00:00Z"}) (type/instant {:extension [internable-extension] - :value "1970-01-02T00:00:00Z"}) + :value #system/date-time "1970-01-02T00:00:00Z"}) (type/instant {:extension [not-internable-extension]}) (type/instant {:extension [not-internable-extension]})) @@ -1053,123 +938,116 @@ (testing "assoc id" (testing "non-extended" - (is (= (type/assoc-id #fhir/instant "1970-01-02T00:00:00Z" "id-111030") - #fhir/instant{:id "id-111030" :value "1970-01-02T00:00:00Z"}))) + (is (= (assoc #fhir/instant #system/date-time "1970-01-02T00:00:00Z" :id "id-111030") + #fhir/instant{:id "id-111030" :value #system/date-time "1970-01-02T00:00:00Z"}))) (testing "already extended" - (is (= (type/assoc-id #fhir/instant{:id "foo"} "bar") + (is (= (assoc #fhir/instant{:id "foo"} :id "bar") #fhir/instant{:id "bar"})) - (is (= (type/assoc-id #fhir/instant{:extension [#fhir/Extension{:url "foo"}]} "id-111902") + (is (= (assoc #fhir/instant{:extension [#fhir/Extension{:url "foo"}]} :id "id-111902") #fhir/instant{:id "id-111902" :extension [#fhir/Extension{:url "foo"}]})))) (testing "assoc extension" (testing "non-extended" - (is (= (type/assoc-extension #fhir/instant "1970-01-02T00:00:00Z" [#fhir/Extension{:url "foo"}]) - #fhir/instant{:extension [#fhir/Extension{:url "foo"}] :value "1970-01-02T00:00:00Z"}))) + (is (= (assoc #fhir/instant #system/date-time "1970-01-02T00:00:00Z" :extension [#fhir/Extension{:url "foo"}]) + #fhir/instant{:extension [#fhir/Extension{:url "foo"}] :value #system/date-time "1970-01-02T00:00:00Z"}))) (testing "already extended" - (is (= (type/assoc-extension #fhir/instant{:id "id-111953"} [#fhir/Extension{:url "foo"}]) + (is (= (assoc #fhir/instant{:id "id-111953"} :extension [#fhir/Extension{:url "foo"}]) #fhir/instant{:id "id-111953" :extension [#fhir/Extension{:url "foo"}]})) - (is (= (type/assoc-extension #fhir/instant{:extension [#fhir/Extension{:url "foo"}]} [#fhir/Extension{:url "bar"}]) + (is (= (assoc #fhir/instant{:extension [#fhir/Extension{:url "foo"}]} :extension [#fhir/Extension{:url "bar"}]) #fhir/instant{:extension [#fhir/Extension{:url "bar"}]})))) - (testing "value is a System.DateTime which is a OffsetDateTime" - (are [x] (= (OffsetDateTime/of 2020 1 1 0 0 0 0 (ZoneOffset/ofHours 2)) (type/value x)) - #fhir/instant "2020-01-01T00:00:00+02:00" - #fhir/instant{:id "foo" :value "2020-01-01T00:00:00+02:00"}) + (testing "value" + (are [x] (= #system/date-time "2020-01-01T00:00:00+02:00" (:value x) (:value x) (:value x ::foo)) + #fhir/instant #system/date-time "2020-01-01T00:00:00+02:00" + #fhir/instant{:id "foo" :value #system/date-time "2020-01-01T00:00:00+02:00"}) - (are [x] (= (OffsetDateTime/of 1970 1 1 0 0 0 0 ZoneOffset/UTC) (type/value x)) - #fhir/instant "1970-01-01T00:00:00Z" - #fhir/instant{:id "foo" :value "1970-01-01T00:00:00Z"})) + (are [x] (= #system/date-time "1970-01-01T00:00:00Z" (:value x) (:value x) (:value x ::foo)) + #fhir/instant #system/date-time "1970-01-01T00:00:00Z" + #fhir/instant{:id "foo" :value #system/date-time "1970-01-01T00:00:00Z"})) (testing "assoc value" - (is (= #fhir/instant "1970-01-02T00:00:00Z" (type/assoc-value #fhir/instant "2020-01-01T00:00:00+02:00" "1970-01-02T00:00:00Z"))) - - (testing "invalid" - (is (s2/invalid? (type/assoc-value #fhir/instant "2020-01-01T00:00:00+02:00" "a"))))) + (is (= #fhir/instant #system/date-time "1970-01-02T00:00:00Z" (assoc #fhir/instant #system/date-time "2020-01-01T00:00:00+02:00" :value #system/date-time "1970-01-02T00:00:00Z")))) (testing "to-json" - (are [instant json] (= json (gen-json-string instant)) - #fhir/instant "2020-01-01T00:00:00+02:00" "\"2020-01-01T00:00:00+02:00\"" - Instant/EPOCH "\"1970-01-01T00:00:00Z\"")) + (are [instant json] (= json (gen-json-value instant)) + #fhir/instant #system/date-time "2020-01-01T00:00:00.123456789+02:00" "2020-01-01T00:00:00.123456789+02:00" + #fhir/instant #system/date-time "2020-01-01T00:00:00+02:00" "2020-01-01T00:00:00+02:00" + #fhir/instant #system/date-time "1970-01-01T00:00:00Z" "1970-01-01T00:00:00Z")) (testing "to-xml" (is (= (sexp-value "2020-01-01T00:00:00+02:00") - (type/to-xml #fhir/instant "2020-01-01T00:00:00+02:00"))) + (type/to-xml #fhir/instant #system/date-time "2020-01-01T00:00:00+02:00"))) (is (= (sexp-value "1970-01-01T00:00:00Z") - (type/to-xml Instant/EPOCH)))) + (type/to-xml #fhir/instant #system/date-time "1970-01-01T00:00:00Z")))) (testing "equals" - (is (let [instant #fhir/instant "2020-01-01T00:00:00+02:00"] (= instant instant))) - (is (= #fhir/instant "2020-01-01T00:00:00+02:00" - #fhir/instant "2020-01-01T00:00:00+02:00")) - (is (not= #fhir/instant "2020-01-01T00:00:00+01:00" - #fhir/instant "2020-01-01T00:00:00+02:00")) - (is (= Instant/EPOCH #fhir/instant "1970-01-01T00:00:00Z")) - (is (= Instant/EPOCH #fhir/instant "1970-01-01T00:00:00+00:00"))) + (is (let [instant #fhir/instant #system/date-time "2020-01-01T00:00:00+02:00"] (= instant instant))) + (is (= #fhir/instant #system/date-time "2020-01-01T00:00:00+02:00" + #fhir/instant #system/date-time "2020-01-01T00:00:00+02:00")) + (is (not= #fhir/instant #system/date-time "2020-01-01T00:00:00+01:00" + #fhir/instant #system/date-time "2020-01-01T00:00:00+02:00")) + (is (= #fhir/instant #system/date-time "1970-01-01T00:00:00Z" #fhir/instant #system/date-time "1970-01-01T00:00:00Z"))) (testing "hash-into" (are [x hex] (= hex (murmur3 x)) - #fhir/instant "2020-01-01T00:00:00+00:00" "d81f6bc2" - #fhir/instant "2020-01-01T00:00:00+01:00" "4225df0d" - #fhir/instant "2020-01-01T00:00:00Z" "d81f6bc2" - #fhir/instant "1970-01-01T00:00:00Z" "93344244" - #fhir/instant{:value "1970-01-01T00:00:00Z"} "93344244" - Instant/EPOCH "93344244" + #fhir/instant #system/date-time "2020-01-01T00:00:00+00:00" "d81f6bc2" + #fhir/instant #system/date-time "2020-01-01T00:00:00+01:00" "4225df0d" + #fhir/instant #system/date-time "2020-01-01T00:00:00Z" "d81f6bc2" + #fhir/instant #system/date-time "1970-01-01T00:00:00Z" "93344244" + #fhir/instant{:value #system/date-time "1970-01-01T00:00:00Z"} "93344244" #fhir/instant{:id "foo"} "b4705bd6" - #fhir/instant{:id "foo" :value "1970-01-01T00:00:00Z"} "e5a31add" + #fhir/instant{:id "foo" :value #system/date-time "1970-01-01T00:00:00Z"} "6ae7daa" #fhir/instant{:extension [#fhir/Extension{:url "foo"}]} "8a7f7ddc")) + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/instant #system/date-time "2020-01-01T00:00:00+00:00" 80 + #fhir/instant{:id "foo"} 80)) + (testing "references" - (are [x refs] (= refs (type/references x)) - Instant/EPOCH - nil)) + (is (empty? (type/references #fhir/instant #system/date-time "1970-01-01T00:00:00Z")))) (testing "print" (are [i s] (= s (pr-str i)) - #fhir/instant "2020-01-01T00:00:00Z" - "#java/instant\"2020-01-01T00:00:00Z\"" + #fhir/instant #system/date-time "2020-01-01T00:00:00Z" + "#fhir/instant #system/date-time \"2020-01-01T00:00:00Z\"" - #fhir/instant "2020-01-01T00:00:00+01:00" - "#fhir/instant\"2020-01-01T00:00:00+01:00\"" + #fhir/instant #system/date-time "2020-01-01T00:00:00+01:00" + "#fhir/instant #system/date-time \"2020-01-01T00:00:00+01:00\"" #fhir/instant{:id "211213"} "#fhir/instant{:id \"211213\"}" - #fhir/instant{:value "2020-01-01T00:00:00Z"} - "#java/instant\"2020-01-01T00:00:00Z\"")) - - (testing "toString" - (is (= "2020-01-01T00:00:00Z" (str #fhir/instant "2020-01-01T00:00:00Z"))))) + #fhir/instant{:value #system/date-time "2020-01-01T00:00:00Z"} + "#fhir/instant #system/date-time \"2020-01-01T00:00:00Z\""))) (deftest date-test (testing "with year precision" (testing "date?" (are [x] (type/date? x) - #fhir/date "0001" - #fhir/date "9999" - #fhir/date "2022" + #fhir/date #system/date "0001" + #fhir/date #system/date "9999" + #fhir/date #system/date "2022" #fhir/date{:id "foo"})) - (testing "invalid" - (is (s2/invalid? (type/date "a")))) - (testing "type" - (are [x] (= :fhir/date (type/type x)) - #fhir/date "2022" + (are [x] (= :fhir/date (:fhir/type x)) + #fhir/date #system/date "2022" #fhir/date{:id "foo"})) (testing "date" - (is (= #fhir/date{:value "2022"} #fhir/date "2022")) - (is (= #fhir/date{:value #system/date"2022"} #fhir/date "2022"))) + (is (= #fhir/date{:value #system/date "2022"} #fhir/date #system/date "2022")) + (is (= #fhir/date{:value #system/date "2022"} #fhir/date #system/date "2022"))) - (testing "interned" - (is (not-interned? #fhir/date "2020" #fhir/date "2020")) + (testing "interning" + (is (not-interned? #fhir/date #system/date "2020" #fhir/date #system/date "2020")) (testing "with extension" (are [x y] (not-interned? x y) - (type/date {:extension [internable-extension] :value "2022"}) - (type/date {:extension [internable-extension] :value "2022"}) + (type/date {:extension [internable-extension] :value #system/date "2022"}) + (type/date {:extension [internable-extension] :value #system/date "2022"}) (type/date {:id "id-164735" :extension [internable-extension]}) (type/date {:id "id-164735" :extension [internable-extension]}) @@ -1183,263 +1061,274 @@ (testing "assoc id" (testing "non-extended" - (is (= (type/assoc-id #fhir/date "2020" "id-111030") - #fhir/date{:id "id-111030" :value #system/date"2020"}))) + (is (= (assoc #fhir/date #system/date "2020" :id "id-111030") + #fhir/date{:id "id-111030" :value #system/date "2020"}))) (testing "already extended" - (is (= (type/assoc-id #fhir/date{:id "foo"} "bar") + (is (= (assoc #fhir/date{:id "foo"} :id "bar") #fhir/date{:id "bar"})) - (is (= (type/assoc-id #fhir/date{:extension [#fhir/Extension{:url "foo"}]} "id-111902") + (is (= (assoc #fhir/date{:extension [#fhir/Extension{:url "foo"}]} :id "id-111902") #fhir/date{:id "id-111902" :extension [#fhir/Extension{:url "foo"}]})))) (testing "assoc extension" (testing "non-extended" - (is (= (type/assoc-extension #fhir/date "2020" [#fhir/Extension{:url "foo"}]) - #fhir/date{:extension [#fhir/Extension{:url "foo"}] :value #system/date"2020"}))) - + (is (= (assoc #fhir/date #system/date "2020" :extension [#fhir/Extension{:url "foo"}]) + #fhir/date{:extension [#fhir/Extension{:url "foo"}] :value #system/date "2020"}))) (testing "already extended" - (is (= (type/assoc-extension #fhir/date{:id "id-111953"} [#fhir/Extension{:url "foo"}]) - #fhir/date{:id "id-111953" :extension [#fhir/Extension{:url "foo"}]})) - (is (= (type/assoc-extension #fhir/date{:extension [#fhir/Extension{:url "foo"}]} [#fhir/Extension{:url "bar"}]) - #fhir/date{:extension [#fhir/Extension{:url "bar"}]})))) + (is (= (assoc #fhir/date{:id "id-111953"} :extension [#fhir/Extension{:url "foo"}]) + #fhir/date{:id "id-111953" :extension [#fhir/Extension{:url "foo"}]})) (is (= (assoc #fhir/date{:extension [#fhir/Extension{:url "foo"}]} :extension [#fhir/Extension{:url "bar"}]) + #fhir/date{:extension [#fhir/Extension{:url "bar"}]})))) (testing "value" - (are [x] (= #system/date"2020" (type/value x) (:value x) (:value x ::foo)) - #fhir/date "2020" - #fhir/date{:id "foo" :value #system/date"2020"})) + (are [x] (= #system/date "2020" (:value x) (:value x) (:value x ::foo)) + #fhir/date #system/date "2020" + #fhir/date{:id "foo" :value #system/date "2020"})) (testing "assoc value" (testing "non-extended" - (is (= (type/assoc-value #fhir/date "2020" #system/date"2022") - #fhir/date "2022")) - (is (= (type/assoc-value #fhir/date "2020" #system/date"2022-03") - #fhir/date "2022-03")) - (is (= (type/assoc-value #fhir/date "2020" #system/date"2022-03-16") - #fhir/date "2022-03-16"))) + (is (= (assoc #fhir/date #system/date "2020" :value #system/date "2022") + #fhir/date #system/date "2022")) + (is (= (assoc #fhir/date #system/date "2020" :value #system/date "2022-03") + #fhir/date #system/date "2022-03")) + (is (= (assoc #fhir/date #system/date "2020" :value #system/date "2022-03-16") + #fhir/date #system/date "2022-03-16"))) (testing "already extended" - (is (= (type/assoc-value #fhir/date{:id "foo"} #system/date"2020") - #fhir/date{:id "foo" :value #system/date"2020"})))) + (is (= (assoc #fhir/date{:id "foo"} :value #system/date "2020") + #fhir/date{:id "foo" :value #system/date "2020"})))) (testing "lookup" (testing "other keys are not found" - (is (= ::not-found (::other-key #fhir/date "1970" ::not-found))))) + (is (= ::not-found (::other-key #fhir/date #system/date "1970" ::not-found))))) (testing "to-json" - (are [date json] (= json (gen-json-string date)) - #fhir/date "0001" "\"0001\"" - #fhir/date "9999" "\"9999\"" - #fhir/date "2020" "\"2020\"")) + (are [date json] (= json (gen-json-value date)) + #fhir/date #system/date "0001" "0001" + #fhir/date #system/date "9999" "9999" + #fhir/date #system/date "2020" "2020")) (testing "to-xml" (are [date xml] (= (sexp-value xml) (type/to-xml date)) - #fhir/date "0001" "0001" - #fhir/date "9999" "9999" - #fhir/date "2020" "2020")) + #fhir/date #system/date "0001" "0001" + #fhir/date #system/date "9999" "9999" + #fhir/date #system/date "2020" "2020")) (testing "equals" - (is (= #fhir/date "0001" #fhir/date{:id nil :value #system/date"0001"})) - (is (= #fhir/date "2020" #fhir/date "2020")) - (is (not= #fhir/date "2020" #fhir/date "2021")) - (is (not= #fhir/date "2020" "2020"))) + (is (= #fhir/date #system/date "0001" #fhir/date{:id nil :value #system/date "0001"})) + (is (= #fhir/date #system/date "2020" #fhir/date #system/date "2020")) + (is (not= #fhir/date #system/date "2020" #fhir/date #system/date "2021")) + (is (not= #fhir/date #system/date "2020" #system/date "2020"))) (testing "hash-into" (are [x hex] (= hex (murmur3 x)) - #fhir/date "2020" "c92be432" - #fhir/date{:value "2020"} "c92be432" + #fhir/date #system/date "2020" "c92be432" + #fhir/date{:value #system/date "2020"} "c92be432" #fhir/date{:id "foo"} "20832903" - #fhir/date{:id "foo" :value #system/date"2020"} "e983029c" + #fhir/date{:id "foo" :value #system/date "2020"} "e983029c" #fhir/date{:extension [#fhir/Extension{:url "foo"}]} "707470a9")) + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/date #system/date "2020" 32 + #fhir/date{:id "foo"} 80)) + (testing "references" - (is (empty? (type/references #fhir/date "2020")))) + (is (empty? (type/references #fhir/date #system/date "2020")))) (testing "print" - (is (= "#fhir/date\"2020\"" (pr-str #fhir/date "2020"))))) + (are [x s] (= (pr-str x) s) + #fhir/date #system/date "2020" "#fhir/date #system/date \"2020\"" + #fhir/date{:id "foo"} "#fhir/date{:id \"foo\"}"))) (testing "with year-month precision" (testing "date?" (are [x] (type/date? x) - #fhir/date "0001-01" - #fhir/date "9999-12" - #fhir/date "2022-05" + #fhir/date #system/date "0001-01" + #fhir/date #system/date "9999-12" + #fhir/date #system/date "2022-05" #fhir/date{:id "foo"})) (testing "type" - (are [x] (= :fhir/date (type/type x)) - #fhir/date "2022-05" + (are [x] (= :fhir/date (:fhir/type x)) + #fhir/date #system/date "2022-05" #fhir/date{:id "foo"})) (testing "date" - (is (= #fhir/date{:value "2022-05"} #fhir/date "2022-05"))) + (is (= #fhir/date{:value #system/date "2022-05"} #fhir/date #system/date "2022-05"))) - (testing "interned" - (is (not-interned? #fhir/date "2020-01" #fhir/date "2020-01"))) + (testing "interning" + (is (not-interned? #fhir/date #system/date "2020-01" #fhir/date #system/date "2020-01"))) (testing "value" - (are [x] (= #system/date"2020-01" (type/value x) (:value x) (:value x ::foo)) - #fhir/date "2020-01" - #fhir/date{:id "foo" :value "2020-01"})) + (are [x] (= #system/date "2020-01" (:value x) (:value x) (:value x ::foo)) + #fhir/date #system/date "2020-01" + #fhir/date{:id "foo" :value #system/date "2020-01"})) (testing "assoc value" (testing "non-extended" - (is (= (type/assoc-value #fhir/date "2020-01" #system/date"2022") - #fhir/date "2022")) - (is (= (type/assoc-value #fhir/date "2020-01" #system/date"2022-03") - #fhir/date "2022-03")) - (is (= (type/assoc-value #fhir/date "2020-01" #system/date"2022-03-16") - #fhir/date "2022-03-16"))) + (is (= (assoc #fhir/date #system/date "2020-01" :value #system/date "2022") + #fhir/date #system/date "2022")) + (is (= (assoc #fhir/date #system/date "2020-01" :value #system/date "2022-03") + #fhir/date #system/date "2022-03")) + (is (= (assoc #fhir/date #system/date "2020-01" :value #system/date "2022-03-16") + #fhir/date #system/date "2022-03-16"))) (testing "already extended" - (is (= (type/assoc-value #fhir/date{:id "foo"} #system/date"2020-01") - #fhir/date{:id "foo" :value #system/date"2020-01"})))) + (is (= (assoc #fhir/date{:id "foo"} :value #system/date "2020-01") + #fhir/date{:id "foo" :value #system/date "2020-01"})))) (testing "lookup" (testing "other keys are not found" - (is (= ::not-found (::other-key #fhir/date "1970-01" ::not-found))))) + (is (= ::not-found (::other-key #fhir/date #system/date "1970-01" ::not-found))))) (testing "to-json" - (are [date json] (= json (gen-json-string date)) - #fhir/date "0001-01" "\"0001-01\"" - #fhir/date "9999-12" "\"9999-12\"" - #fhir/date "2020-01" "\"2020-01\"")) + (are [date json] (= json (gen-json-value date)) + #fhir/date #system/date "0001-01" "0001-01" + #fhir/date #system/date "9999-12" "9999-12" + #fhir/date #system/date "2020-01" "2020-01")) (testing "to-xml" (are [date xml] (= (sexp-value xml) (type/to-xml date)) - #fhir/date "0001-01" "0001-01" - #fhir/date "9999-12" "9999-12" - #fhir/date "2020-01" "2020-01")) + #fhir/date #system/date "0001-01" "0001-01" + #fhir/date #system/date "9999-12" "9999-12" + #fhir/date #system/date "2020-01" "2020-01")) (testing "equals" - (is (= #fhir/date "2020-01" #fhir/date "2020-01")) - (is (not= #fhir/date "2020-01" #fhir/date "2020-02")) - (is (not= #fhir/date "2020-01" "2020-01"))) + (is (= #fhir/date #system/date "2020-01" #fhir/date #system/date "2020-01")) + (is (not= #fhir/date #system/date "2020-01" #fhir/date #system/date "2020-02")) + (is (not= #fhir/date #system/date "2020-01" #system/date "2020-01"))) (testing "hash-into" (are [x hex] (= hex (murmur3 x)) - #fhir/date "2020-01" "fbcdf97f" - #fhir/date{:value #system/date"2020-01"} "fbcdf97f" + #fhir/date #system/date "2020-01" "fbcdf97f" + #fhir/date{:value #system/date "2020-01"} "fbcdf97f" #fhir/date{:id "foo"} "20832903" - #fhir/date{:id "foo" :value #system/date"2020-01"} "4e6aead7" + #fhir/date{:id "foo" :value #system/date "2020-01"} "4e6aead7" #fhir/date{:extension [#fhir/Extension{:url "foo"}]} "707470a9")) + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/date #system/date "2020-01" 32 + #fhir/date{:id "foo"} 80)) + (testing "references" - (is (empty? (type/references #fhir/date "2020-01")))) + (is (empty? (type/references #fhir/date #system/date "2020-01")))) (testing "print" - (is (= "#fhir/date\"2020-01\"" (pr-str #fhir/date "2020-01"))))) + (is (= "#fhir/date #system/date \"2020-01\"" (pr-str #fhir/date #system/date "2020-01"))))) (testing "with date precision" (testing "date?" (are [x] (type/date? x) - #fhir/date "2022-05-23" + #fhir/date #system/date "2022-05-23" #fhir/date{:id "foo"})) (testing "type" - (are [x] (= :fhir/date (type/type x)) - #fhir/date "2022-05-23" + (are [x] (= :fhir/date (:fhir/type x)) + #fhir/date #system/date "2022-05-23" #fhir/date{:id "foo"})) (testing "date" - (is (= #fhir/date{:value #system/date"2022-05-23"} #fhir/date "2022-05-23"))) + (is (= #fhir/date{:value #system/date "2022-05-23"} #fhir/date #system/date "2022-05-23"))) - (testing "interned" - (is (not-interned? #fhir/date "2020-01-01" #fhir/date "2020-01-01"))) + (testing "interning" + (is (not-interned? #fhir/date #system/date "2020-01-01" #fhir/date #system/date "2020-01-01"))) (testing "value" - (are [x] (= #system/date"2020-01-02" (type/value x) (:value x) (:value x ::foo)) - #fhir/date "2020-01-02" - #fhir/date{:id "foo" :value "2020-01-02"})) + (are [x] (= #system/date "2020-01-02" (:value x) (:value x) (:value x ::foo)) + #fhir/date #system/date "2020-01-02" + #fhir/date{:id "foo" :value #system/date "2020-01-02"})) (testing "assoc value" (testing "non-extended" - (is (= (type/assoc-value #fhir/date "2022-05-23" #system/date"2022") - #fhir/date "2022")) - (is (= (type/assoc-value #fhir/date "2022-05-23" #system/date"2022-03") - #fhir/date "2022-03")) - (is (= (type/assoc-value #fhir/date "2022-05-23" #system/date"2022-03-16") - #fhir/date "2022-03-16"))) + (is (= (assoc #fhir/date #system/date "2022-05-23" :value #system/date "2022") + #fhir/date #system/date "2022")) + (is (= (assoc #fhir/date #system/date "2022-05-23" :value #system/date "2022-03") + #fhir/date #system/date "2022-03")) + (is (= (assoc #fhir/date #system/date "2022-05-23" :value #system/date "2022-03-16") + #fhir/date #system/date "2022-03-16"))) (testing "already extended" - (is (= (type/assoc-value #fhir/date{:id "foo"} #system/date"2022-05-23") - #fhir/date{:id "foo" :value #system/date"2022-05-23"})))) + (is (= (assoc #fhir/date{:id "foo"} :value #system/date "2022-05-23") + #fhir/date{:id "foo" :value #system/date "2022-05-23"})))) (testing "lookup" (testing "other keys are not found" - (is (= ::not-found (::other-key #fhir/date "1970-01-01" ::not-found))))) + (is (= ::not-found (::other-key #fhir/date #system/date "1970-01-01" ::not-found))))) (testing "to-json" - (are [date json] (= json (gen-json-string date)) - #fhir/date "0001-01-01" "\"0001-01-01\"" - #fhir/date "9999-12-31" "\"9999-12-31\"") + (are [date json] (= json (gen-json-value date)) + #fhir/date #system/date "0001-01-01" "0001-01-01" + #fhir/date #system/date "9999-12-31" "9999-12-31") (satisfies-prop 100 - (prop/for-all [date (gen/fmap type/create-date (s/gen :system/date))] - (= (format "\"%s\"" date) (gen-json-string date))))) + (prop/for-all [date fg/date-value] + (= (str date) (gen-json-value (type/date date)))))) (testing "to-xml" (are [date xml] (= (sexp-value xml) (type/to-xml date)) - #fhir/date "0001-01-01" "0001-01-01" - #fhir/date "9999-12-31" "9999-12-31") + #fhir/date #system/date "0001-01-01" "0001-01-01" + #fhir/date #system/date "9999-12-31" "9999-12-31") (satisfies-prop 100 - (prop/for-all [date (gen/fmap type/create-date (s/gen :system/date))] - (= (sexp-value (str date)) (type/to-xml date))))) + (prop/for-all [date fg/date-value] + (= (sexp-value (str date)) (type/to-xml (type/date date)))))) (testing "equals" (satisfies-prop 100 - (prop/for-all [date (gen/fmap type/create-date (s/gen :system/date))] + (prop/for-all [date fg/date-value] (= date date))) - (is (not= #fhir/date "2020-01-01" #fhir/date "2020-01-02")) - (is (not= #fhir/date "2020-01-01" "2020-01-01"))) + (is (not= #fhir/date #system/date "2020-01-01" #fhir/date #system/date "2020-01-02")) + (is (not= #fhir/date #system/date "2020-01-01" #system/date "2020-01-01"))) (testing "hash-into" (are [x hex] (= hex (murmur3 x)) - #fhir/date "2020-01-01" "cd20e081" - #fhir/date{:value "2020-01-01"} "cd20e081" + #fhir/date #system/date "2020-01-01" "cd20e081" + #fhir/date{:value #system/date "2020-01-01"} "cd20e081" #fhir/date{:id "foo"} "20832903" - #fhir/date{:id "foo" :value #system/date"2020-01-01"} "ef736a41" + #fhir/date{:id "foo" :value #system/date "2020-01-01"} "ef736a41" #fhir/date{:extension [#fhir/Extension{:url "foo"}]} "707470a9")) + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/date #system/date "2020-01-01" 32 + #fhir/date{:id "foo"} 80)) + (testing "references" - (is (empty? (type/references #fhir/date "2020-01-01")))) + (is (empty? (type/references #fhir/date #system/date "2020-01-01")))) (testing "print" - (is (= "#fhir/date\"2020-01-01\"" (pr-str #fhir/date "2020-01-01")))))) + (is (= "#fhir/date #system/date \"2020-01-01\"" (pr-str #fhir/date #system/date "2020-01-01")))))) (deftest dateTime-test (testing "with year precision" (testing "dateTime?" (are [x] (type/dateTime? x) - #fhir/dateTime "0001" - #fhir/dateTime "9999" - #fhir/dateTime "2022" - #fhir/dateTime "2022-01" - #fhir/dateTime "2022-01-01" + #fhir/dateTime #system/date-time "0001" + #fhir/dateTime #system/date-time "9999" + #fhir/dateTime #system/date-time "2022" + #fhir/dateTime #system/date-time "2022-01" + #fhir/dateTime #system/date-time "2022-01-01" #fhir/dateTime{:id "foo"} #fhir/dateTime{:extension [#fhir/Extension{:url "foo"}]})) - (testing "invalid" - (is (s2/invalid? (type/dateTime "a")))) - (testing "type" - (are [x] (= :fhir/dateTime (type/type x)) - #fhir/dateTime "0001" - #fhir/dateTime "9999" - #fhir/dateTime "2022" + (are [x] (= :fhir/dateTime (:fhir/type x)) + #fhir/dateTime #system/date-time "0001" + #fhir/dateTime #system/date-time "9999" + #fhir/dateTime #system/date-time "2022" #fhir/dateTime{:id "foo"})) (testing "dateTime" - (is (= #fhir/dateTime{:value "2022"} #fhir/dateTime "2022")) - (is (= #fhir/dateTime{:value #system/date-time"2022"} #fhir/dateTime "2022"))) + (is (= #fhir/dateTime{:value #system/date-time "2022"} #fhir/dateTime #system/date-time "2022"))) - (testing "interned" - (is (not-interned? #fhir/dateTime "2020" #fhir/dateTime "2020")) + (testing "interning" + (is (not-interned? #fhir/dateTime #system/date-time "2020" #fhir/dateTime #system/date-time "2020")) (testing "with extension" (are [x y] (not-interned? x y) - (type/dateTime {:extension [internable-extension] :value "2022"}) - (type/dateTime {:extension [internable-extension] :value "2022"}) + (type/dateTime {:extension [internable-extension] :value #system/date-time "2022"}) + (type/dateTime {:extension [internable-extension] :value #system/date-time "2022"}) (type/dateTime {:id "id-164735" :extension [internable-extension]}) (type/dateTime {:id "id-164735" :extension [internable-extension]}) @@ -1452,466 +1341,503 @@ (type/dateTime {:extension [internable-extension]})))) (testing "value" - (are [x] (= #system/date-time"2020" (type/value x) (:value x) (:value x ::foo)) - #fhir/dateTime "2020" - #fhir/dateTime{:id "foo" :value #system/date-time"2020"})) + (are [x] (= #system/date-time "2020" (:value x) (:value x) (:value x ::foo)) + #fhir/dateTime #system/date-time "2020" + #fhir/dateTime{:id "foo" :value #system/date-time "2020"})) (testing "assoc value" (testing "non-extended" - (is (= (type/assoc-value #fhir/dateTime "2021" #system/date-time"2022") - #fhir/dateTime "2022")) - (is (= (type/assoc-value #fhir/dateTime "2021" #system/date-time"2022-03") - #fhir/dateTime "2022-03")) - (is (= (type/assoc-value #fhir/dateTime "2021" #system/date-time"2022-03-16") - #fhir/dateTime "2022-03-16"))) + (is (= (assoc #fhir/dateTime #system/date-time "2021" :value #system/date-time "2022") + #fhir/dateTime #system/date-time "2022")) + (is (= (assoc #fhir/dateTime #system/date-time "2021" :value #system/date-time "2022-03") + #fhir/dateTime #system/date-time "2022-03")) + (is (= (assoc #fhir/dateTime #system/date-time "2021" :value #system/date-time "2022-03-16") + #fhir/dateTime #system/date-time "2022-03-16"))) (testing "already extended" - (is (= (type/assoc-value #fhir/dateTime{:id "foo"} #system/date-time"2020") - #fhir/dateTime{:id "foo" :value #system/date-time"2020"})))) + (is (= (assoc #fhir/dateTime{:id "foo"} :value #system/date-time "2020") + #fhir/dateTime{:id "foo" :value #system/date-time "2020"})))) (testing "lookup" (testing "other keys are not found" - (is (= ::not-found (::other-key #fhir/dateTime "1970" ::not-found))))) + (is (= ::not-found (::other-key #fhir/dateTime #system/date-time "1970" ::not-found))))) (testing "to-json" - (are [date-time json] (= json (gen-json-string date-time)) - #fhir/dateTime "0001" "\"0001\"" - #fhir/dateTime "9999" "\"9999\"" - #fhir/dateTime "2020" "\"2020\"") + (are [date-time json] (= json (gen-json-value date-time)) + #fhir/dateTime #system/date-time "0001" "0001" + #fhir/dateTime #system/date-time "9999" "9999" + #fhir/dateTime #system/date-time "2020" "2020") (satisfies-prop 100 - (prop/for-all [date-time (gen/fmap type/create-date-time (s/gen :system/date-time))] - (= (format "\"%s\"" (.format DateTimeFormatter/ISO_LOCAL_DATE_TIME date-time)) - (gen-json-string date-time))))) + (prop/for-all [date-time (fg/dateTime-value)] + (= (DateTimes/toString date-time) (gen-json-value (type/dateTime date-time)))))) (testing "to-xml" (are [date-time xml] (= (sexp-value xml) (type/to-xml date-time)) - #fhir/dateTime "0001" "0001" - #fhir/dateTime "9999" "9999" - #fhir/dateTime "2020" "2020")) + #fhir/dateTime #system/date-time "0001" "0001" + #fhir/dateTime #system/date-time "9999" "9999" + #fhir/dateTime #system/date-time "2020" "2020")) (testing "equals" - (is (= #fhir/dateTime "2020" #fhir/dateTime "2020")) - (is (not= #fhir/dateTime "2020" #fhir/dateTime "2021")) - (is (not= #fhir/dateTime "2020" "2020"))) + (is (= #fhir/dateTime #system/date-time "2020" #fhir/dateTime #system/date-time "2020")) + (is (not= #fhir/dateTime #system/date-time "2020" #fhir/dateTime #system/date-time "2021")) + (is (not= #fhir/dateTime #system/date-time "2020" #system/date-time "2020"))) (testing "hash-into" (are [x hex] (= hex (murmur3 x)) - #fhir/dateTime "2020" "41e906ff" - #fhir/dateTime{:value #system/date-time"2020"} "41e906ff" + #fhir/dateTime #system/date-time "2020" "41e906ff" + #fhir/dateTime{:value #system/date-time "2020"} "41e906ff" #fhir/dateTime{:id "foo"} "fde903da" - #fhir/dateTime{:id "foo" :value #system/date-time"2020"} "c7361227" + #fhir/dateTime{:id "foo" :value #system/date-time "2020"} "c7361227" #fhir/dateTime{:extension [#fhir/Extension{:url "foo"}]} "15062059")) + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/dateTime #system/date-time "2020" 32 + #fhir/dateTime{:id "foo"} 80)) + (testing "references" - (are [x refs] (= refs (type/references x)) - #fhir/dateTime "2020" - nil)) + (is (empty? (type/references #fhir/dateTime #system/date-time "2020")))) (testing "print" - (is (= "#fhir/dateTime\"2020\"" (pr-str #fhir/dateTime "2020"))))) + (are [x s] (= (pr-str x) s) + #fhir/dateTime #system/date-time "2020" "#fhir/dateTime #system/date-time \"2020\"" + #fhir/dateTime{:id "foo"} "#fhir/dateTime{:id \"foo\"}"))) (testing "with year-month precision" (testing "dateTime?" (are [x] (type/dateTime? x) - #fhir/dateTime "2022-05" + #fhir/dateTime #system/date-time "2022-05" #fhir/dateTime{:id "foo"})) (testing "type" - (are [x] (= :fhir/dateTime (type/type x)) - #fhir/dateTime "2022-05" + (are [x] (= :fhir/dateTime (:fhir/type x)) + #fhir/dateTime #system/date-time "2022-05" #fhir/dateTime{:id "foo"})) (testing "dateTime" - (is (= #fhir/dateTime{:value "2022-05"} #fhir/dateTime "2022-05")) - (is (= #fhir/dateTime{:value #system/date-time"2022-05"} #fhir/dateTime "2022-05"))) + (is (= #fhir/dateTime{:value #system/date-time "2022-05"} #fhir/dateTime #system/date-time "2022-05"))) - (testing "interned" - (is (not-interned? #fhir/dateTime "2022-05" #fhir/dateTime "2022-05"))) + (testing "interning" + (is (not-interned? #fhir/dateTime #system/date-time "2022-05" #fhir/dateTime #system/date-time "2022-05"))) (testing "value" - (are [x] (= #system/date-time"2020-01" (type/value x) (:value x) (:value x ::foo)) - #fhir/dateTime "2020-01" - #fhir/dateTime{:id "foo" :value #system/date-time"2020-01"})) + (are [x] (= #system/date-time "2020-01" (:value x) (:value x) (:value x ::foo)) + #fhir/dateTime #system/date-time "2020-01" + #fhir/dateTime{:id "foo" :value #system/date-time "2020-01"})) (testing "assoc value" (testing "non-extended" - (is (= (type/assoc-value #fhir/dateTime "2021-04" #system/date-time"2022") - #fhir/dateTime "2022")) - (is (= (type/assoc-value #fhir/dateTime "2021-04" #system/date-time"2022-03") - #fhir/dateTime "2022-03")) - (is (= (type/assoc-value #fhir/dateTime "2021-04" #system/date-time"2022-03-16") - #fhir/dateTime "2022-03-16"))) + (is (= (assoc #fhir/dateTime #system/date-time "2021-04" :value #system/date-time "2022") + #fhir/dateTime #system/date-time "2022")) + (is (= (assoc #fhir/dateTime #system/date-time "2021-04" :value #system/date-time "2022-03") + #fhir/dateTime #system/date-time "2022-03")) + (is (= (assoc #fhir/dateTime #system/date-time "2021-04" :value #system/date-time "2022-03-16") + #fhir/dateTime #system/date-time "2022-03-16"))) (testing "already extended" - (is (= (type/assoc-value #fhir/dateTime{:id "foo"} #system/date-time"2020-04") - #fhir/dateTime{:id "foo" :value #system/date-time"2020-04"})))) + (is (= (assoc #fhir/dateTime{:id "foo"} :value #system/date-time "2020-04") + #fhir/dateTime{:id "foo" :value #system/date-time "2020-04"})))) (testing "lookup" (testing "other keys are not found" - (is (= ::not-found (::other-key #fhir/dateTime "1970-01" ::not-found))))) + (is (= ::not-found (::other-key #fhir/dateTime #system/date-time "1970-01" ::not-found))))) (testing "to-json" - (are [date-time json] (= json (gen-json-string date-time)) - #fhir/dateTime "0001-01" "\"0001-01\"" - #fhir/dateTime "9999-12" "\"9999-12\"" - #fhir/dateTime "2020-01" "\"2020-01\"")) + (are [date-time json] (= json (gen-json-value date-time)) + #fhir/dateTime #system/date-time "0001-01" "0001-01" + #fhir/dateTime #system/date-time "9999-12" "9999-12" + #fhir/dateTime #system/date-time "2020-01" "2020-01")) (testing "to-xml" (are [date-time xml] (= (sexp-value xml) (type/to-xml date-time)) - #fhir/dateTime "0001-01" "0001-01" - #fhir/dateTime "9999-12" "9999-12" - #fhir/dateTime "2020-01" "2020-01")) + #fhir/dateTime #system/date-time "0001-01" "0001-01" + #fhir/dateTime #system/date-time "9999-12" "9999-12" + #fhir/dateTime #system/date-time "2020-01" "2020-01")) (testing "equals" - (is (= #fhir/dateTime "2020-01" #fhir/dateTime "2020-01")) - (is (not= #fhir/dateTime "2020-01" #fhir/dateTime "2020-02")) - (is (not= #fhir/dateTime "2020-01" "2020-01"))) + (is (= #fhir/dateTime #system/date-time "2020-01" #fhir/dateTime #system/date-time "2020-01")) + (is (not= #fhir/dateTime #system/date-time "2020-01" #fhir/dateTime #system/date-time "2020-02")) + (is (not= #fhir/dateTime #system/date-time "2020-01" #system/date-time "2020-01"))) (testing "hash-into" (are [x hex] (= hex (murmur3 x)) - #fhir/dateTime "2020-01" "9d6c5bd3" - #fhir/dateTime{:value "2020-01"} "9d6c5bd3" + #fhir/dateTime #system/date-time "2020-01" "9d6c5bd3" + #fhir/dateTime{:value #system/date-time "2020-01"} "9d6c5bd3" #fhir/dateTime{:id "foo"} "fde903da" - #fhir/dateTime{:id "foo" :value "2020-01"} "aa78aa13" + #fhir/dateTime{:id "foo" :value #system/date-time "2020-01"} "aa78aa13" #fhir/dateTime{:extension [#fhir/Extension{:url "foo"}]} "15062059")) + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/dateTime #system/date-time "2020-01" 32 + #fhir/dateTime{:id "foo"} 80)) + (testing "references" - (are [x refs] (= refs (type/references x)) - #fhir/dateTime "2020-01" - nil)) + (is (empty? (type/references #fhir/dateTime #system/date-time "2020-01")))) (testing "print" - (is (= "#fhir/dateTime\"2020-01\"" (pr-str #fhir/dateTime "2020-01"))))) + (is (= "#fhir/dateTime #system/date-time \"2020-01\"" (pr-str #fhir/dateTime #system/date-time "2020-01"))))) (testing "with date precision" (testing "dateTime?" (are [x] (type/dateTime? x) - #fhir/dateTime "2022-05-23" + #fhir/dateTime #system/date-time "2022-05-23" #fhir/dateTime{:id "foo"})) (testing "type" - (are [x] (= :fhir/dateTime (type/type x)) - #fhir/dateTime "2022-05-23" + (are [x] (= :fhir/dateTime (:fhir/type x)) + #fhir/dateTime #system/date-time "2022-05-23" #fhir/dateTime{:id "foo"})) (testing "dateTime" - (is (= #fhir/dateTime{:value "2022-05-23"} #fhir/dateTime "2022-05-23")) - (is (= #fhir/dateTime{:value #system/date-time"2022-05-23"} #fhir/dateTime "2022-05-23"))) + (is (= #fhir/dateTime{:value #system/date-time "2022-05-23"} #fhir/dateTime #system/date-time "2022-05-23"))) - (testing "interned" - (is (not-interned? #fhir/dateTime "2022-05-23" #fhir/dateTime "2022-05-23"))) + (testing "interning" + (is (not-interned? #fhir/dateTime #system/date-time "2022-05-23" #fhir/dateTime #system/date-time "2022-05-23"))) (testing "value" - (are [x] (= #system/date-time"2022-05-23" (type/value x) (:value x) (:value x ::foo)) - #fhir/dateTime "2022-05-23" - #fhir/dateTime{:id "foo" :value #system/date-time"2022-05-23"})) + (are [x] (= #system/date-time "2022-05-23" (:value x) (:value x) (:value x ::foo)) + #fhir/dateTime #system/date-time "2022-05-23" + #fhir/dateTime{:id "foo" :value #system/date-time "2022-05-23"})) (testing "assoc value" (testing "non-extended" - (is (= (type/assoc-value #fhir/dateTime "2022-05-23" #system/date-time"2022") - #fhir/dateTime "2022")) - (is (= (type/assoc-value #fhir/dateTime "2022-05-23" #system/date-time"2022-03") - #fhir/dateTime "2022-03")) - (is (= (type/assoc-value #fhir/dateTime "2022-05-23" #system/date-time"2022-03-16") - #fhir/dateTime "2022-03-16"))) + (is (= (assoc #fhir/dateTime #system/date-time "2022-05-23" :value #system/date-time "2022") + #fhir/dateTime #system/date-time "2022")) + (is (= (assoc #fhir/dateTime #system/date-time "2022-05-23" :value #system/date-time "2022-03") + #fhir/dateTime #system/date-time "2022-03")) + (is (= (assoc #fhir/dateTime #system/date-time "2022-05-23" :value #system/date-time "2022-03-16") + #fhir/dateTime #system/date-time "2022-03-16"))) (testing "already extended" - (is (= (type/assoc-value #fhir/dateTime{:id "foo"} #system/date-time"2022-05-23") - #fhir/dateTime{:id "foo" :value #system/date-time"2022-05-23"})))) + (is (= (assoc #fhir/dateTime{:id "foo"} :value #system/date-time "2022-05-23") + #fhir/dateTime{:id "foo" :value #system/date-time "2022-05-23"})))) (testing "lookup" (testing "other keys are not found" - (is (= ::not-found (::other-key #fhir/dateTime "1970-01-01" ::not-found))))) + (is (= ::not-found (::other-key #fhir/dateTime #system/date-time "1970-01-01" ::not-found))))) (testing "to-json" - (are [date-time json] (= json (gen-json-string date-time)) - #fhir/dateTime "0001-01-01" "\"0001-01-01\"" - #fhir/dateTime "9999-12-31" "\"9999-12-31\"" - #fhir/dateTime "2020-01-01" "\"2020-01-01\"")) + (are [date-time json] (= json (gen-json-value date-time)) + #fhir/dateTime #system/date-time "0001-01-01" "0001-01-01" + #fhir/dateTime #system/date-time "9999-12-31" "9999-12-31" + #fhir/dateTime #system/date-time "2020-01-01" "2020-01-01")) (testing "to-xml" (are [date-time xml] (= (sexp-value xml) (type/to-xml date-time)) - #fhir/dateTime "0001-01-01" "0001-01-01" - #fhir/dateTime "9999-12-31" "9999-12-31" - #fhir/dateTime "2020-01-01" "2020-01-01")) + #fhir/dateTime #system/date-time "0001-01-01" "0001-01-01" + #fhir/dateTime #system/date-time "9999-12-31" "9999-12-31" + #fhir/dateTime #system/date-time "2020-01-01" "2020-01-01")) (testing "equals" - (is (= #fhir/dateTime "2020-01-01" #fhir/dateTime "2020-01-01"))) + (is (= #fhir/dateTime #system/date-time "2020-01-01" #fhir/dateTime #system/date-time "2020-01-01"))) (testing "hash-into" (are [x hex] (= hex (murmur3 x)) - #fhir/dateTime "2020-01-01" "39fe9bdb" - #fhir/dateTime{:value "2020-01-01"} "39fe9bdb" + #fhir/dateTime #system/date-time "2020-01-01" "39fe9bdb" + #fhir/dateTime{:value #system/date-time "2020-01-01"} "39fe9bdb" #fhir/dateTime{:id "foo"} "fde903da" - #fhir/dateTime{:id "foo" :value "2020-01-01"} "7e36d416" + #fhir/dateTime{:id "foo" :value #system/date-time "2020-01-01"} "7e36d416" #fhir/dateTime{:extension [#fhir/Extension{:url "foo"}]} "15062059")) + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/dateTime #system/date-time "2020-01-01" 32 + #fhir/dateTime{:id "foo"} 80)) + (testing "references" - (is (empty? (type/references #fhir/dateTime "2020-01-01")))) + (is (empty? (type/references #fhir/dateTime #system/date-time "2020-01-01")))) (testing "print" - (is (= "#fhir/dateTime\"2020-01-01\"" (pr-str #fhir/dateTime "2020-01-01"))))) + (is (= "#fhir/dateTime #system/date-time \"2020-01-01\"" (pr-str #fhir/dateTime #system/date-time "2020-01-01"))))) (testing "without timezone" (testing "dateTime?" (are [x] (type/dateTime? x) - #fhir/dateTime "0001-01-01T00:00:00" - #fhir/dateTime "9999-12-31T12:59:59" - #fhir/dateTime "2020-01-01T00:00:00" + #fhir/dateTime #system/date-time "0001-01-01T00:00:00" + #fhir/dateTime #system/date-time "9999-12-31T12:59:59" + #fhir/dateTime #system/date-time "2020-01-01T00:00:00" #fhir/dateTime{:id "foo"})) (testing "type" - (are [x] (= :fhir/dateTime (type/type x)) - #fhir/dateTime "2020-01-01T00:00:00" + (are [x] (= :fhir/dateTime (:fhir/type x)) + #fhir/dateTime #system/date-time "2020-01-01T00:00:00" #fhir/dateTime{:id "foo"})) (testing "dateTime" - (is (= #fhir/dateTime{:value "2020-01-01T00:00:00"} #fhir/dateTime "2020-01-01T00:00:00"))) + (is (= #fhir/dateTime{:value #system/date-time "2020-01-01T00:00:00"} #fhir/dateTime #system/date-time "2020-01-01T00:00:00"))) - (testing "interned" - (is (not-interned? #fhir/dateTime "2020-01-01T00:00:00" - #fhir/dateTime "2020-01-01T00:00:00"))) + (testing "interning" + (is (not-interned? #fhir/dateTime #system/date-time "2020-01-01T00:00:00" + #fhir/dateTime #system/date-time "2020-01-01T00:00:00"))) (testing "to-json" - (is (= "\"2020-01-01T00:00:00\"" - (gen-json-string #fhir/dateTime "2020-01-01T00:00:00")))) + (is (= "2020-01-01T00:00:00" (gen-json-value #fhir/dateTime #system/date-time "2020-01-01T00:00:00")))) (testing "to-xml" - (is (= (sexp-value "2020-01-01T00:00:00") (type/to-xml #fhir/dateTime "2020-01-01T00:00:00")))) + (is (= (sexp-value "2020-01-01T00:00:00") (type/to-xml #fhir/dateTime #system/date-time "2020-01-01T00:00:00")))) (testing "equals" - (is (= #fhir/dateTime "2020-01-01T00:00:00" - #fhir/dateTime "2020-01-01T00:00:00"))) + (is (= #fhir/dateTime #system/date-time "2020-01-01T00:00:00" + #fhir/dateTime #system/date-time "2020-01-01T00:00:00"))) (testing "hash-into" (are [x hex] (= hex (murmur3 x)) - #fhir/dateTime "2020-01-01T00:00:00" "da537591" - #fhir/dateTime{:value "2020-01-01T00:00:00"} "da537591" - #fhir/dateTime{:id "foo" :value "2020-01-01T00:00:00"} "f33b7808")) + #fhir/dateTime #system/date-time "2020-01-01T00:00:00" "da537591" + #fhir/dateTime{:value #system/date-time "2020-01-01T00:00:00"} "da537591" + #fhir/dateTime{:id "foo" :value #system/date-time "2020-01-01T00:00:00"} "f33b7808")) + + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/dateTime #system/date-time "2020-01-01T00:00:00" 64 + #fhir/dateTime{:id "foo"} 80)) (testing "references" - (is (empty? (type/references #fhir/dateTime "2020-01-01T00:00:00"))))) + (is (empty? (type/references #fhir/dateTime #system/date-time "2020-01-01T00:00:00"))))) (testing "without timezone but millis" (testing "dateTime?" (are [x] (type/dateTime? x) - #fhir/dateTime "2020-01-01T00:00:00.000" + #fhir/dateTime #system/date-time "2020-01-01T00:00:00.000" #fhir/dateTime{:id "foo"})) (testing "type" - (are [x] (= :fhir/dateTime (type/type x)) - #fhir/dateTime "2020-01-01T00:00:00.000" + (are [x] (= :fhir/dateTime (:fhir/type x)) + #fhir/dateTime #system/date-time "2020-01-01T00:00:00.000" #fhir/dateTime{:id "foo"})) (testing "dateTime" - (is (= #fhir/dateTime{:value "2020-01-01T00:00:00.000"} #fhir/dateTime "2020-01-01T00:00:00.000"))) + (is (= #fhir/dateTime{:value #system/date-time "2020-01-01T00:00:00.000"} #fhir/dateTime #system/date-time "2020-01-01T00:00:00.000"))) - (testing "interned" - (is (not-interned? #fhir/dateTime "2020-01-01T00:00:00.000" - #fhir/dateTime "2020-01-01T00:00:00.000"))) + (testing "interning" + (is (not-interned? #fhir/dateTime #system/date-time "2020-01-01T00:00:00.000" + #fhir/dateTime #system/date-time "2020-01-01T00:00:00.000"))) (testing "to-json" - (is (= "\"2020-01-01T00:00:00.001\"" - (gen-json-string #fhir/dateTime "2020-01-01T00:00:00.001")))) + (is (= "2020-01-01T00:00:00.001" (gen-json-value #fhir/dateTime #system/date-time "2020-01-01T00:00:00.001")))) (testing "to-xml" - (is (= (sexp-value "2020-01-01T00:00:00.001") (type/to-xml #fhir/dateTime "2020-01-01T00:00:00.001")))) + (is (= (sexp-value "2020-01-01T00:00:00.001") (type/to-xml #fhir/dateTime #system/date-time "2020-01-01T00:00:00.001")))) (testing "equals" - (is (= #fhir/dateTime "2020-01-01T00:00:00.000" - #fhir/dateTime "2020-01-01T00:00:00.000"))) + (is (= #fhir/dateTime #system/date-time "2020-01-01T00:00:00.000" + #fhir/dateTime #system/date-time "2020-01-01T00:00:00.000"))) (testing "hash-into" (are [x hex] (= hex (murmur3 x)) - #fhir/dateTime "2020-01-01T00:00:00.000" "da537591" - #fhir/dateTime{:value "2020-01-01T00:00:00.000"} "da537591" - #fhir/dateTime{:id "foo" :value "2020-01-01T00:00:00.000"} "f33b7808")) + #fhir/dateTime #system/date-time "2020-01-01T00:00:00.000" "da537591" + #fhir/dateTime{:value #system/date-time "2020-01-01T00:00:00.000"} "da537591" + #fhir/dateTime{:id "foo" :value #system/date-time "2020-01-01T00:00:00.000"} "f33b7808")) + + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/dateTime #system/date-time "2020-01-01T00:00:00.000" 64 + #fhir/dateTime{:id "foo"} 80)) (testing "references" - (is (empty? (type/references #fhir/dateTime "2020-01-01T00:00:00.000"))))) + (is (empty? (type/references #fhir/dateTime #system/date-time "2020-01-01T00:00:00.000"))))) (testing "with zulu timezone" (testing "dateTime?" (are [x] (type/dateTime? x) - #fhir/dateTime "2020-01-01T00:00:00Z" + #fhir/dateTime #system/date-time "2020-01-01T00:00:00Z" #fhir/dateTime{:id "foo"})) (testing "type" - (are [x] (= :fhir/dateTime (type/type x)) - #fhir/dateTime "2020-01-01T00:00:00Z" + (are [x] (= :fhir/dateTime (:fhir/type x)) + #fhir/dateTime #system/date-time "2020-01-01T00:00:00Z" #fhir/dateTime{:id "foo"})) (testing "dateTime" - (is (= #fhir/dateTime{:value "2020-01-01T00:00:00Z"} #fhir/dateTime "2020-01-01T00:00:00Z"))) + (is (= #fhir/dateTime{:value #system/date-time "2020-01-01T00:00:00Z"} #fhir/dateTime #system/date-time "2020-01-01T00:00:00Z"))) - (testing "interned" - (is (not-interned? #fhir/dateTime "2020-01-01T00:00:00Z" - #fhir/dateTime "2020-01-01T00:00:00Z"))) + (testing "interning" + (is (not-interned? #fhir/dateTime #system/date-time "2020-01-01T00:00:00Z" + #fhir/dateTime #system/date-time "2020-01-01T00:00:00Z"))) (testing "to-json" - (is (= "\"2020-01-01T00:00:00Z\"" - (gen-json-string #fhir/dateTime "2020-01-01T00:00:00Z")))) + (is (= "2020-01-01T00:00:00Z" (gen-json-value #fhir/dateTime #system/date-time "2020-01-01T00:00:00Z")))) (testing "to-xml" - (is (= (sexp-value "2020-01-01T00:00:00Z") (type/to-xml #fhir/dateTime "2020-01-01T00:00:00Z")))) + (is (= (sexp-value "2020-01-01T00:00:00Z") (type/to-xml #fhir/dateTime #system/date-time "2020-01-01T00:00:00Z")))) (testing "equals" - (is (= #fhir/dateTime "2020-01-01T00:00:00Z" - #fhir/dateTime "2020-01-01T00:00:00Z"))) + (is (= #fhir/dateTime #system/date-time "2020-01-01T00:00:00Z" + #fhir/dateTime #system/date-time "2020-01-01T00:00:00Z"))) (testing "hash-into" (are [x hex] (= hex (murmur3 x)) - #fhir/dateTime "2020-01-01T00:00:00Z" "d541a45" - #fhir/dateTime{:value "2020-01-01T00:00:00Z"} "d541a45" - #fhir/dateTime{:id "foo" :value "2020-01-01T00:00:00Z"} "14a5cd29")) + #fhir/dateTime #system/date-time "2020-01-01T00:00:00Z" "d541a45" + #fhir/dateTime{:value #system/date-time "2020-01-01T00:00:00Z"} "d541a45" + #fhir/dateTime{:id "foo" :value #system/date-time "2020-01-01T00:00:00Z"} "14a5cd29")) + + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/dateTime #system/date-time "2020-01-01T00:00:00Z" 80 + #fhir/dateTime{:id "foo"} 80)) (testing "references" - (is (empty? (type/references #fhir/dateTime "2020-01-01T00:00:00Z"))))) + (is (empty? (type/references #fhir/dateTime #system/date-time "2020-01-01T00:00:00Z"))))) (testing "with positive timezone offset" (testing "dateTime?" (are [x] (type/dateTime? x) - #fhir/dateTime "2020-01-01T00:00:00+01:00" + #fhir/dateTime #system/date-time "2020-01-01T00:00:00+01:00" #fhir/dateTime{:id "foo"})) (testing "type" - (are [x] (= :fhir/dateTime (type/type x)) - #fhir/dateTime "2020-01-01T00:00:00+01:00" + (are [x] (= :fhir/dateTime (:fhir/type x)) + #fhir/dateTime #system/date-time "2020-01-01T00:00:00+01:00" #fhir/dateTime{:id "foo"})) (testing "dateTime" - (is (= #fhir/dateTime{:value "2020-01-01T00:00:00+01:00"} #fhir/dateTime "2020-01-01T00:00:00+01:00"))) + (is (= #fhir/dateTime{:value #system/date-time "2020-01-01T00:00:00+01:00"} #fhir/dateTime #system/date-time "2020-01-01T00:00:00+01:00"))) - (testing "interned" - (is (not-interned? #fhir/dateTime "2020-01-01T00:00:00+01:00" - #fhir/dateTime "2020-01-01T00:00:00+01:00"))) + (testing "interning" + (is (not-interned? #fhir/dateTime #system/date-time "2020-01-01T00:00:00+01:00" + #fhir/dateTime #system/date-time "2020-01-01T00:00:00+01:00"))) (testing "to-json" - (is (= "\"2020-01-01T00:00:00+01:00\"" - (gen-json-string #fhir/dateTime "2020-01-01T00:00:00+01:00")))) + (is (= "2020-01-01T00:00:00+01:00" (gen-json-value #fhir/dateTime #system/date-time "2020-01-01T00:00:00+01:00")))) (testing "to-xml" - (is (= (sexp-value "2020-01-01T00:00:00+01:00") (type/to-xml #fhir/dateTime "2020-01-01T00:00:00+01:00")))) + (is (= (sexp-value "2020-01-01T00:00:00+01:00") (type/to-xml #fhir/dateTime #system/date-time "2020-01-01T00:00:00+01:00")))) (testing "equals" - (is (= #fhir/dateTime "2020-01-01T00:00:00+01:00" - #fhir/dateTime "2020-01-01T00:00:00+01:00"))) + (is (= #fhir/dateTime #system/date-time "2020-01-01T00:00:00+01:00" + #fhir/dateTime #system/date-time "2020-01-01T00:00:00+01:00"))) (testing "hash-into" (are [x hex] (= hex (murmur3 x)) - #fhir/dateTime "2020-01-01T00:00:00+01:00" "9c535d0d" - #fhir/dateTime{:value "2020-01-01T00:00:00+01:00"} "9c535d0d" - #fhir/dateTime{:id "foo" :value "2020-01-01T00:00:00+01:00"} "dbf5aa43")) + #fhir/dateTime #system/date-time "2020-01-01T00:00:00+01:00" "9c535d0d" + #fhir/dateTime{:value #system/date-time "2020-01-01T00:00:00+01:00"} "9c535d0d" + #fhir/dateTime{:id "foo" :value #system/date-time "2020-01-01T00:00:00+01:00"} "dbf5aa43")) + + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/dateTime #system/date-time "2020-01-01T00:00:00+01:00" 80 + #fhir/dateTime{:id "foo"} 80)) (testing "references" - (is (empty? (type/references #fhir/dateTime "2020-01-01T00:00:00+01:00"))))) + (is (empty? (type/references #fhir/dateTime #system/date-time "2020-01-01T00:00:00+01:00"))))) (testing "with negative timezone offset" (testing "dateTime?" (are [x] (type/dateTime? x) - #fhir/dateTime "2020-01-01T00:00:00-01:00" + #fhir/dateTime #system/date-time "2020-01-01T00:00:00-01:00" #fhir/dateTime{:id "foo"})) (testing "type" - (are [x] (= :fhir/dateTime (type/type x)) - #fhir/dateTime "2020-01-01T00:00:00-01:00" + (are [x] (= :fhir/dateTime (:fhir/type x)) + #fhir/dateTime #system/date-time "2020-01-01T00:00:00-01:00" #fhir/dateTime{:id "foo"})) (testing "dateTime" - (is (= #fhir/dateTime{:value "2020-01-01T00:00:00-01:00"} #fhir/dateTime "2020-01-01T00:00:00-01:00"))) + (is (= #fhir/dateTime{:value #system/date-time "2020-01-01T00:00:00-01:00"} #fhir/dateTime #system/date-time "2020-01-01T00:00:00-01:00"))) - (testing "interned" - (is (not-interned? #fhir/dateTime "2020-01-01T00:00:00-01:00" - #fhir/dateTime "2020-01-01T00:00:00-01:00"))) + (testing "interning" + (is (not-interned? #fhir/dateTime #system/date-time "2020-01-01T00:00:00-01:00" + #fhir/dateTime #system/date-time "2020-01-01T00:00:00-01:00"))) (testing "to-json" - (is (= "\"2020-01-01T00:00:00-01:00\"" - (gen-json-string #fhir/dateTime "2020-01-01T00:00:00-01:00")))) + (is (= "2020-01-01T00:00:00-01:00" (gen-json-value #fhir/dateTime #system/date-time "2020-01-01T00:00:00-01:00")))) (testing "to-xml" - (is (= (sexp-value "2020-01-01T00:00:00-01:00") (type/to-xml #fhir/dateTime "2020-01-01T00:00:00-01:00")))) + (is (= (sexp-value "2020-01-01T00:00:00-01:00") (type/to-xml #fhir/dateTime #system/date-time "2020-01-01T00:00:00-01:00")))) (testing "equals" - (is (= #fhir/dateTime "2020-01-01T00:00:00-01:00" - #fhir/dateTime "2020-01-01T00:00:00-01:00"))) + (is (= #fhir/dateTime #system/date-time "2020-01-01T00:00:00-01:00" + #fhir/dateTime #system/date-time "2020-01-01T00:00:00-01:00"))) (testing "hash-into" (are [x hex] (= hex (murmur3 x)) - #fhir/dateTime "2020-01-01T00:00:00-01:00" "839fd8a6" - #fhir/dateTime{:value "2020-01-01T00:00:00-01:00"} "839fd8a6" - #fhir/dateTime{:id "foo" :value "2020-01-01T00:00:00-01:00"} "c3a7cc0e")) + #fhir/dateTime #system/date-time "2020-01-01T00:00:00-01:00" "839fd8a6" + #fhir/dateTime{:value #system/date-time "2020-01-01T00:00:00-01:00"} "839fd8a6" + #fhir/dateTime{:id "foo" :value #system/date-time "2020-01-01T00:00:00-01:00"} "c3a7cc0e")) + + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/dateTime #system/date-time "2020-01-01T00:00:00-01:00" 80 + #fhir/dateTime{:id "foo"} 80)) (testing "references" - (is (empty? (type/references #fhir/dateTime "2020-01-01T00:00:00-01:00"))))) + (is (empty? (type/references #fhir/dateTime #system/date-time "2020-01-01T00:00:00-01:00"))))) (testing "with zulu timezone and millis" (testing "dateTime?" (are [x] (type/dateTime? x) - #fhir/dateTime "2020-01-01T00:00:00.001Z" + #fhir/dateTime #system/date-time "2020-01-01T00:00:00.001Z" #fhir/dateTime{:id "foo"})) (testing "type" - (are [x] (= :fhir/dateTime (type/type x)) - #fhir/dateTime "2020-01-01T00:00:00.001Z" + (are [x] (= :fhir/dateTime (:fhir/type x)) + #fhir/dateTime #system/date-time "2020-01-01T00:00:00.001Z" #fhir/dateTime{:id "foo"})) (testing "dateTime" - (is (= #fhir/dateTime{:value "2020-01-01T00:00:00.001Z"} #fhir/dateTime "2020-01-01T00:00:00.001Z"))) + (is (= #fhir/dateTime{:value #system/date-time "2020-01-01T00:00:00.001Z"} #fhir/dateTime #system/date-time "2020-01-01T00:00:00.001Z"))) - (testing "interned" - (is (not-interned? #fhir/dateTime "2020-01-01T00:00:00.001Z" - #fhir/dateTime "2020-01-01T00:00:00.001Z"))) + (testing "interning" + (is (not-interned? #fhir/dateTime #system/date-time "2020-01-01T00:00:00.001Z" + #fhir/dateTime #system/date-time "2020-01-01T00:00:00.001Z"))) - (testing "value is a System.DateTime which is a OffsetDateTime" - (is (= (OffsetDateTime/of 2020 1 1 0 0 0 1000000 ZoneOffset/UTC) - (type/value #fhir/dateTime "2020-01-01T00:00:00.001Z")))) + (testing "value" + (is (= #system/date-time "2020-01-01T00:00:00.001Z" (:value #fhir/dateTime #system/date-time "2020-01-01T00:00:00.001Z")))) (testing "to-json" - (is (= "\"2020-01-01T00:00:00.001Z\"" - (gen-json-string #fhir/dateTime "2020-01-01T00:00:00.001Z")))) + (is (= "2020-01-01T00:00:00.001Z" (gen-json-value #fhir/dateTime #system/date-time "2020-01-01T00:00:00.001Z")))) (testing "to-xml" - (is (= (sexp-value "2020-01-01T00:00:00.001Z") (type/to-xml #fhir/dateTime "2020-01-01T00:00:00.001Z")))) + (is (= (sexp-value "2020-01-01T00:00:00.001Z") (type/to-xml #fhir/dateTime #system/date-time "2020-01-01T00:00:00.001Z")))) (testing "equals" - (is (= #fhir/dateTime "2020-01-01T00:00:00.001Z" - #fhir/dateTime "2020-01-01T00:00:00.001Z"))) + (is (= #fhir/dateTime #system/date-time "2020-01-01T00:00:00.001Z" + #fhir/dateTime #system/date-time "2020-01-01T00:00:00.001Z"))) (testing "hash-into" (are [x hex] (= hex (murmur3 x)) - #fhir/dateTime "2020-01-01T00:00:00.001Z" "f46a0b1b" - #fhir/dateTime{:value "2020-01-01T00:00:00.001Z"} "f46a0b1b" - #fhir/dateTime{:id "foo" :value "2020-01-01T00:00:00.001Z"} "c6a5ea73")) + #fhir/dateTime #system/date-time "2020-01-01T00:00:00.001Z" "f46a0b1b" + #fhir/dateTime{:value #system/date-time "2020-01-01T00:00:00.001Z"} "f46a0b1b" + #fhir/dateTime{:id "foo" :value #system/date-time "2020-01-01T00:00:00.001Z"} "c6a5ea73")) + + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/dateTime #system/date-time "2020-01-01T00:00:00.001Z" 80 + #fhir/dateTime{:id "foo"} 80)) (testing "references" - (is (empty? (type/references #fhir/dateTime "2020-01-01T00:00:00.001Z"))))) + (is (empty? (type/references #fhir/dateTime #system/date-time "2020-01-01T00:00:00.001Z"))))) (testing "with extensions" - (let [extended-date-time (type/dateTime {:extension [string-extension] :value #system/date-time"2020"}) + (let [extended-date-time (type/dateTime {:extension [string-extension] :value #system/date-time "2020"}) extended-date-time-element (xml-node/element nil {:value "2020"} string-extension)] (testing "date-time?" (is (type/dateTime? extended-date-time))) (testing "type" - (is (= :fhir/dateTime (type/type extended-date-time)))) + (is (= :fhir/dateTime (:fhir/type extended-date-time)))) - (testing "interned" - (is (not-interned? (type/dateTime {:extension [string-extension] :value "2020"}) - (type/dateTime {:extension [string-extension] :value "2020"})))) + (testing "interning" + (is (not-interned? (type/dateTime {:extension [string-extension] :value #system/date-time "2020"}) + (type/dateTime {:extension [string-extension] :value #system/date-time "2020"})))) (testing "value" - (is (= #system/date-time"2020" (type/value extended-date-time)))) + (is (= #system/date-time "2020" (:value extended-date-time)))) (testing "to-xml" (is (= extended-date-time-element (type/to-xml extended-date-time)))) (testing "equals" - (is (= (type/dateTime {:extension [string-extension] :value #system/date-time"2020"}) extended-date-time))) + (is (= (type/dateTime {:extension [string-extension] :value #system/date-time "2020"}) extended-date-time))) (testing "hash-into" (are [x hex] (= hex (murmur3 x)) - extended-date-time "c8805e69")) + extended-date-time "f1c7cff4")) + + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + extended-date-time 32)) (testing "references" (is (empty? (type/references extended-date-time))))))) @@ -1919,82 +1845,93 @@ (deftest time-test (testing "time?" (are [x] (type/time? x) - #fhir/time "15:27:45" + #fhir/time #system/time "15:27:45" #fhir/time{:id "foo"})) - (testing "invalid" - (is (s2/invalid? (type/time "a")))) - (testing "type" - (are [x] (= :fhir/time (type/type x)) - #fhir/time "15:27:45" + (are [x] (= :fhir/time (:fhir/type x)) + #fhir/time #system/time "15:27:45" #fhir/time{:id "foo"})) (testing "time" - (is (= #fhir/time{:value "15:27:45"} #fhir/time "15:27:45"))) + (is (= #fhir/time{:value #system/time "15:27:45"} #fhir/time #system/time "15:27:45"))) - (testing "interned" - (is (not-interned? #fhir/time "13:53:21" #fhir/time "13:53:21"))) + (testing "interning" + (is (not-interned? #fhir/time #system/time "13:53:21" #fhir/time #system/time "13:53:21"))) (testing "assoc id" (testing "non-extended" - (is (= (type/assoc-id #fhir/time "13:53:21" "id-111030") - #fhir/time{:id "id-111030" :value #system/time"13:53:21"}))) + (is (= (assoc #fhir/time #system/time "13:53:21" :id "id-111030") + #fhir/time{:id "id-111030" :value #system/time "13:53:21"}))) (testing "already extended" - (is (= (type/assoc-id #fhir/time{:id "foo"} "bar") + (is (= (assoc #fhir/time{:id "foo"} :id "bar") #fhir/time{:id "bar"})) - (is (= (type/assoc-id #fhir/time{:extension [#fhir/Extension{:url "foo"}]} "id-111902") + (is (= (assoc #fhir/time{:extension [#fhir/Extension{:url "foo"}]} :id "id-111902") #fhir/time{:id "id-111902" :extension [#fhir/Extension{:url "foo"}]})))) (testing "assoc extension" (testing "non-extended" - (is (= (type/assoc-extension #fhir/time "13:53:21" [#fhir/Extension{:url "foo"}]) + (is (= (assoc #fhir/time #system/time "13:53:21" :extension [#fhir/Extension{:url "foo"}]) #fhir/time{:extension [#fhir/Extension{:url "foo"}] :value #system/time "13:53:21"}))) (testing "already extended" - (is (= (type/assoc-extension #fhir/time{:id "id-111953"} [#fhir/Extension{:url "foo"}]) + (is (= (assoc #fhir/time{:id "id-111953"} :extension [#fhir/Extension{:url "foo"}]) #fhir/time{:id "id-111953" :extension [#fhir/Extension{:url "foo"}]})) - (is (= (type/assoc-extension #fhir/time{:extension [#fhir/Extension{:url "foo"}]} [#fhir/Extension{:url "bar"}]) + (is (= (assoc #fhir/time{:extension [#fhir/Extension{:url "foo"}]} :extension [#fhir/Extension{:url "bar"}]) #fhir/time{:extension [#fhir/Extension{:url "bar"}]})))) (testing "value is a System.Time which is a LocalTime" - (are [x] (= #system/time"13:53:21" (type/value x)) - #fhir/time "13:53:21" - #fhir/time{:id "foo" :value "13:53:21"} - #fhir/time{:id "foo" :value #system/time"13:53:21"})) + (are [x] (= #system/time "13:53:21" (:value x)) + #fhir/time #system/time "13:53:21" + #fhir/time{:id "foo" :value #system/time "13:53:21"})) (testing "assoc value" + (testing "non-extended" - (is (= (type/assoc-value #fhir/time "13:53:21" #system/time"13:34:45") - #fhir/time "13:34:45"))) + + (is (= (assoc #fhir/time #system/time "13:53:21" :value #system/time "13:34:45") + + #fhir/time #system/time "13:34:45"))) (testing "already extended" - (is (= (type/assoc-value #fhir/time{:id "foo"} #system/time"13:34:45") - (type/time {:id "foo" :value #system/time"13:34:45"}))))) + + (is (= (assoc #fhir/time{:id "foo"} :value #system/time "13:34:45") + + (type/time {:id "foo" :value #system/time "13:34:45"}))))) (testing "to-json" - (is (= "\"13:53:21\"" (gen-json-string #fhir/time "13:53:21")))) + (is (= "13:53:21" (gen-json-value #fhir/time #system/time "13:53:21")))) (testing "to-xml" (is (= (sexp-value "13:53:21") - (type/to-xml #fhir/time "13:53:21")))) + (type/to-xml #fhir/time #system/time "13:53:21")))) (testing "equals" - (is (= #fhir/time "13:53:21" #fhir/time "13:53:21")) - (is (not= #fhir/time "13:53:21" #fhir/time "13:53:22")) - (is (not= #fhir/time "13:53:21" "13:53:21"))) + (is (= #fhir/time #system/time "13:53:21" #fhir/time #system/time "13:53:21")) + (is (not= #fhir/time #system/time "13:53:21" #fhir/time #system/time "13:53:22")) + (is (not= #fhir/time #system/time "13:53:21" "13:53:21"))) (testing "hash-into" (are [x hex] (= hex (murmur3 x)) - #fhir/time "13:53:21" "faa37be9" - #fhir/time{:value "13:53:21"} "faa37be9" + #fhir/time #system/time "13:53:21" "faa37be9" + #fhir/time{:value #system/time "13:53:21"} "faa37be9" #fhir/time{:id "foo"} "1547f086" - #fhir/time{:id "foo" :value "13:53:21"} "52a81d69" + #fhir/time{:id "foo" :value #system/time "13:53:21"} "52a81d69" #fhir/time{:extension [#fhir/Extension{:url "foo"}]} "9e94d20a")) + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/time #system/time "13:53:21" 32 + #fhir/time{:id "foo"} 80)) + (testing "references" - (is (empty? (type/references #fhir/time "13:53:21"))))) + (is (empty? (type/references #fhir/time #system/time "13:53:21")))) + + (testing "print" + (are [x s] (= (pr-str x) s) + #fhir/time #system/time "13:53:21" "#fhir/time #system/time \"13:53:21\"" + #fhir/time{:id "foo"} "#fhir/time{:id \"foo\"}"))) (def gender-extension #fhir/Extension @@ -2018,12 +1955,22 @@ #fhir/code{:id "foo"})) (testing "type" - (are [x] (= :fhir/code (type/type x)) + (are [x] (= :fhir/code (:fhir/type x)) #fhir/code "" #fhir/code{:id "foo"})) - (testing "interned" + (testing "interning" + (are [x y] (interned? x y) + (type/code {:extension [internable-extension]}) + (type/code {:extension [internable-extension]}) + + #fhir/code "1234" #fhir/code "1234" + + #fhir/code "1234" (assoc #fhir/code "5678" :value "1234")) + (is (interned? #fhir/code "code-123745" #fhir/code "code-123745")) + (is (interned? (type/code {:extension [] :value "code-123745"}) + (type/code "code-123745"))) (testing "instances with id's are not interned" (is (not-interned? #fhir/code{:id "id-171649" :value "code-123745"} @@ -2041,47 +1988,57 @@ (testing "assoc id" (testing "non-extended" - (is (= (type/assoc-id #fhir/code "165645" "id-111030") + (is (= (assoc #fhir/code "165645" :id "id-111030") #fhir/code{:id "id-111030" :value "165645"}))) (testing "already extended" - (is (= (type/assoc-id #fhir/code{:id "foo"} "bar") + (is (= (assoc #fhir/code{:id "foo"} :id "bar") #fhir/code{:id "bar"})) - (is (= (type/assoc-id #fhir/code{:extension [#fhir/Extension{:url "foo"}]} "id-111902") + (is (= (assoc #fhir/code{:extension [#fhir/Extension{:url "foo"}]} :id "id-111902") #fhir/code{:id "id-111902" :extension [#fhir/Extension{:url "foo"}]})))) (testing "assoc extension" (testing "non-extended" - (is (= (type/assoc-extension #fhir/code "165645" [#fhir/Extension{:url "foo"}]) + (is (= (assoc #fhir/code "165645" :extension [#fhir/Extension{:url "foo"}]) #fhir/code{:extension [#fhir/Extension{:url "foo"}] :value "165645"}))) (testing "already extended" - (is (= (type/assoc-extension #fhir/code{:id "id-111953"} [#fhir/Extension{:url "foo"}]) + (is (= (assoc #fhir/code{:id "id-111953"} :extension [#fhir/Extension{:url "foo"}]) #fhir/code{:id "id-111953" :extension [#fhir/Extension{:url "foo"}]})) - (is (= (type/assoc-extension #fhir/code{:extension [#fhir/Extension{:url "foo"}]} [#fhir/Extension{:url "bar"}]) + (is (= (assoc #fhir/code{:extension [#fhir/Extension{:url "foo"}]} :extension [#fhir/Extension{:url "bar"}]) #fhir/code{:extension [#fhir/Extension{:url "bar"}]})))) (testing "value" - (are [x] (= "code-123745" (type/value x) (:value x) (:value x ::foo)) + (are [x] (= "code-123745" (:value x) (:value x) (:value x ::foo)) #fhir/code "code-123745" #fhir/code{:id "foo" :value "code-123745"})) (testing "assoc value" + (are [code value] (identical? code (assoc code :value value)) + #fhir/code "code-165634" "code-165634") + (testing "non-extended" - (is (= (type/assoc-value #fhir/code "code-165634" "code-165643") + (is (= (assoc #fhir/code "code-165634" :value "code-165643") #fhir/code "code-165643"))) (testing "already extended" - (is (= (type/assoc-value #fhir/code{:id "foo"} "code-171046") + (is (= (assoc #fhir/code{:id "foo"} :value "code-171046") #fhir/code{:id "foo" :value "code-171046"})))) (testing "lookup" (testing "other keys are not found" (is (= ::not-found (::other-key #fhir/code "foo" ::not-found))))) + (testing "metadata" + (is (nil? (meta #fhir/code "code-123745"))) + (is (= {:foo "bar"} (meta (with-meta #fhir/code "code-123745" {:foo "bar"})))) + (is (= {:foo "bar"} (meta ^{:foo "bar"} #fhir/code "code-123745"))) + (is (= (with-meta #fhir/code "code-123745" {:x "a"}) + (with-meta #fhir/code "code-123745" {:x "b"})))) + (testing "to-json" - (are [code json] (= json (gen-json-string code)) - #fhir/code "code-123745" "\"code-123745\"")) + (are [code json] (= json (gen-json-value code)) + #fhir/code "code-123745" "code-123745")) (testing "to-xml" (is (= (sexp-value "code-123745") @@ -2102,10 +2059,15 @@ #fhir/code{:id "170837" :value "175726"} "fc8af973" #fhir/code{:extension [#fhir/Extension{:url "181911"}]} "838ce6ff")) + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/code "175726" 0 + #fhir/code{:id "foo"} 80)) + (testing "references" (are [x refs] (= refs (type/references x)) #fhir/code "code-150839" - nil + [] #fhir/code {:extension @@ -2114,38 +2076,13 @@ [["Patient" "1"]])) (testing "print" - (is (= "#fhir/code\"175718\"" (pr-str #fhir/code "175718")))) - - (testing "toString" - (satisfies-prop 10 - (prop/for-all [value fg/code-value] - (= value (str (type/code value)))))) - - (testing "SerializableString" - (testing "getValue" - (satisfies-prop 10 - (prop/for-all [value fg/code-value] - (= value (.getValue ^SerializableString (type/code value)))))) - - (testing "appendQuotedUTF8" - (satisfies-prop 100 - (prop/for-all [value fg/code-value] - (let [expected-buffer (.quoteAsUTF8 (JsonStringEncoder/getInstance) value) - buffer (byte-array (count expected-buffer))] - (.appendQuotedUTF8 ^SerializableString (type/code value) buffer 0) - (= (bb/wrap expected-buffer) (bb/wrap buffer)))))) - - (testing "asUnquotedUTF8" - (satisfies-prop 100 - (prop/for-all [value fg/code-value] - (= (bb/wrap (.encodeAsUTF8 (JsonStringEncoder/getInstance) ^String value)) - (bb/wrap (.asUnquotedUTF8 ^SerializableString (type/code value))))))) - - (testing "asQuotedUTF8" - (satisfies-prop 100 - (prop/for-all [value fg/code-value] - (= (bb/wrap (.quoteAsUTF8 (JsonStringEncoder/getInstance) value)) - (bb/wrap (.asQuotedUTF8 ^SerializableString (type/code value))))))))) + (is (= "#fhir/code \"175718\"" (pr-str #fhir/code "175718"))) + (is (= "#fhir/code{:id \"170837\"}" (pr-str #fhir/code{:id "170837"}))) + (is (= "#fhir/code{:id \"170837\" :value \"175718\"}" (pr-str #fhir/code{:id "170837" :value "175718"}))) + (is (= "#fhir/code{:extension [#fhir/Extension{:url \"181911\"}]}" + (pr-str #fhir/code{:extension [#fhir/Extension{:url "181911"}]}))) + (is (= "#fhir/code{:id \"170837\" :extension [#fhir/Extension{:url \"181911\"}]}" + (pr-str #fhir/code{:id "170837" :extension [#fhir/Extension{:url "181911"}]}))))) (deftest oid-test (testing "oid?" @@ -2154,50 +2091,50 @@ #fhir/oid{:id "foo"})) (testing "type" - (are [x] (= :fhir/oid (type/type x)) + (are [x] (= :fhir/oid (:fhir/type x)) #fhir/oid "" #fhir/oid{:id "foo"})) (testing "oid" (is (= #fhir/oid{:value "182040"} #fhir/oid "182040"))) - (testing "interned" + (testing "interning" (is (not-interned? #fhir/oid "oid-123745" #fhir/oid "oid-123745"))) (testing "assoc id" (testing "non-extended" - (is (= (type/assoc-id #fhir/oid "165645" "id-111030") + (is (= (assoc #fhir/oid "165645" :id "id-111030") #fhir/oid{:id "id-111030" :value "165645"}))) (testing "already extended" - (is (= (type/assoc-id #fhir/oid{:id "foo"} "bar") + (is (= (assoc #fhir/oid{:id "foo"} :id "bar") #fhir/oid{:id "bar"})) - (is (= (type/assoc-id #fhir/oid{:extension [#fhir/Extension{:url "foo"}]} "id-111902") + (is (= (assoc #fhir/oid{:extension [#fhir/Extension{:url "foo"}]} :id "id-111902") #fhir/oid{:id "id-111902" :extension [#fhir/Extension{:url "foo"}]})))) (testing "assoc extension" (testing "non-extended" - (is (= (type/assoc-extension #fhir/oid "165645" [#fhir/Extension{:url "foo"}]) + (is (= (assoc #fhir/oid "165645" :extension [#fhir/Extension{:url "foo"}]) #fhir/oid{:extension [#fhir/Extension{:url "foo"}] :value "165645"}))) (testing "already extended" - (is (= (type/assoc-extension #fhir/oid{:id "id-111953"} [#fhir/Extension{:url "foo"}]) + (is (= (assoc #fhir/oid{:id "id-111953"} :extension [#fhir/Extension{:url "foo"}]) #fhir/oid{:id "id-111953" :extension [#fhir/Extension{:url "foo"}]})) - (is (= (type/assoc-extension #fhir/oid{:extension [#fhir/Extension{:url "foo"}]} [#fhir/Extension{:url "bar"}]) + (is (= (assoc #fhir/oid{:extension [#fhir/Extension{:url "foo"}]} :extension [#fhir/Extension{:url "bar"}]) #fhir/oid{:extension [#fhir/Extension{:url "bar"}]})))) (testing "value" - (are [x] (= "oid-123745" (type/value x) (:value x) (:value x ::foo)) + (are [x] (= "oid-123745" (:value x) (:value x) (:value x ::foo)) #fhir/oid "oid-123745" #fhir/oid{:id "foo" :value "oid-123745"})) (testing "assoc value" (testing "non-extended" - (is (= (type/assoc-value #fhir/oid "oid-165634" "oid-165643") + (is (= (assoc #fhir/oid "oid-165634" :value "oid-165643") #fhir/oid "oid-165643"))) (testing "already extended" - (is (= (type/assoc-value #fhir/oid{:id "foo"} "oid-171046") + (is (= (assoc #fhir/oid{:id "foo"} :value "oid-171046") #fhir/oid{:id "foo" :value "oid-171046"})))) (testing "lookup" @@ -2205,7 +2142,7 @@ (is (= ::not-found (::other-key #fhir/oid "foo" ::not-found))))) (testing "to-json" - (is (= "\"oid-123745\"" (gen-json-string #fhir/oid "oid-123745")))) + (is (= "oid-123745" (gen-json-value #fhir/oid "oid-123745")))) (testing "to-xml" (is (= (sexp-value "oid-123745") @@ -2225,11 +2162,18 @@ #fhir/oid{:id "foo" :value "175726"} "5e076060" #fhir/oid{:extension [#fhir/Extension{:url "foo"}]} "c114dd42")) + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/oid "175726" 64 + #fhir/oid{:id "foo"} 80)) + (testing "references" (is (empty? (type/references #fhir/oid "151329")))) (testing "print" - (is (= "#fhir/oid\"175718\"" (pr-str #fhir/oid "175718"))))) + (are [x s] (= (pr-str x) s) + #fhir/oid "175726" "#fhir/oid \"175726\"" + #fhir/oid{:id "foo"} "#fhir/oid{:id \"foo\"}"))) (deftest id-test (testing "id?" @@ -2238,50 +2182,50 @@ #fhir/id{:id "foo"})) (testing "type" - (are [x] (= :fhir/id (type/type x)) + (are [x] (= :fhir/id (:fhir/type x)) #fhir/id "" #fhir/id{:id "foo"})) (testing "id" (is (= #fhir/id{:value "182040"} #fhir/id "182040"))) - (testing "interned" + (testing "interning" (is (not-interned? #fhir/id "id-123745" #fhir/id "id-123745"))) (testing "assoc id" (testing "non-extended" - (is (= (type/assoc-id #fhir/id "165645" "id-111030") + (is (= (assoc #fhir/id "165645" :id "id-111030") #fhir/id{:id "id-111030" :value "165645"}))) (testing "already extended" - (is (= (type/assoc-id #fhir/id{:id "foo"} "bar") + (is (= (assoc #fhir/id{:id "foo"} :id "bar") #fhir/id{:id "bar"})) - (is (= (type/assoc-id #fhir/id{:extension [#fhir/Extension{:url "foo"}]} "id-111902") + (is (= (assoc #fhir/id{:extension [#fhir/Extension{:url "foo"}]} :id "id-111902") #fhir/id{:id "id-111902" :extension [#fhir/Extension{:url "foo"}]})))) (testing "assoc extension" (testing "non-extended" - (is (= (type/assoc-extension #fhir/id "165645" [#fhir/Extension{:url "foo"}]) + (is (= (assoc #fhir/id "165645" :extension [#fhir/Extension{:url "foo"}]) #fhir/id{:extension [#fhir/Extension{:url "foo"}] :value "165645"}))) (testing "already extended" - (is (= (type/assoc-extension #fhir/id{:id "id-111953"} [#fhir/Extension{:url "foo"}]) + (is (= (assoc #fhir/id{:id "id-111953"} :extension [#fhir/Extension{:url "foo"}]) #fhir/id{:id "id-111953" :extension [#fhir/Extension{:url "foo"}]})) - (is (= (type/assoc-extension #fhir/id{:extension [#fhir/Extension{:url "foo"}]} [#fhir/Extension{:url "bar"}]) + (is (= (assoc #fhir/id{:extension [#fhir/Extension{:url "foo"}]} :extension [#fhir/Extension{:url "bar"}]) #fhir/id{:extension [#fhir/Extension{:url "bar"}]})))) (testing "value" - (are [x] (= "id-123745" (type/value x) (:value x) (:value x ::foo)) + (are [x] (= "id-123745" (:value x) (:value x) (:value x ::foo)) #fhir/id "id-123745" #fhir/id{:id "foo" :value "id-123745"})) (testing "assoc value" (testing "non-extended" - (is (= (type/assoc-value #fhir/id "id-165634" "id-165643") + (is (= (assoc #fhir/id "id-165634" :value "id-165643") #fhir/id "id-165643"))) (testing "already extended" - (is (= (type/assoc-value #fhir/id{:id "foo"} "id-171046") + (is (= (assoc #fhir/id{:id "foo"} :value "id-171046") #fhir/id{:id "foo" :value "id-171046"})))) (testing "lookup" @@ -2289,7 +2233,7 @@ (is (= ::not-found (::other-key #fhir/id "foo" ::not-found))))) (testing "to-json" - (is (= "\"id-123745\"" (gen-json-string #fhir/id "id-123745")))) + (is (= "id-123745" (gen-json-value #fhir/id "id-123745")))) (testing "to-xml" (is (= (sexp-value "id-123745") @@ -2309,11 +2253,18 @@ #fhir/id{:id "foo" :value "175726"} "3dbaa84e" #fhir/id{:extension [#fhir/Extension{:url "foo"}]} "1e8120f7")) + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/id "175726" 64 + #fhir/id{:id "foo"} 80)) + (testing "references" (is (empty? (type/references #fhir/id "151408")))) (testing "print" - (is (= "#fhir/id\"175718\"" (pr-str #fhir/id "175718"))))) + (are [x s] (= (pr-str x) s) + #fhir/id "175726" "#fhir/id \"175726\"" + #fhir/id{:id "foo"} "#fhir/id{:id \"foo\"}"))) (deftest markdown-test (testing "markdown?" @@ -2322,51 +2273,51 @@ #fhir/markdown{:id "foo"})) (testing "type" - (are [x] (= :fhir/markdown (type/type x)) + (are [x] (= :fhir/markdown (:fhir/type x)) #fhir/markdown "" #fhir/markdown{:id "foo"})) (testing "markdown" (is (= #fhir/markdown{:value "182040"} #fhir/markdown "182040"))) - (testing "interned" + (testing "interning" (is (not-interned? #fhir/markdown "markdown-123745" #fhir/markdown "markdown-123745"))) (testing "assoc id" (testing "non-extended" - (is (= (type/assoc-id #fhir/markdown "165645" "id-111030") + (is (= (assoc #fhir/markdown "165645" :id "id-111030") #fhir/markdown{:id "id-111030" :value "165645"}))) (testing "already extended" - (is (= (type/assoc-id #fhir/markdown{:id "foo"} "bar") + (is (= (assoc #fhir/markdown{:id "foo"} :id "bar") #fhir/markdown{:id "bar"})) - (is (= (type/assoc-id #fhir/markdown{:extension [#fhir/Extension{:url "foo"}]} "id-111902") + (is (= (assoc #fhir/markdown{:extension [#fhir/Extension{:url "foo"}]} :id "id-111902") #fhir/markdown{:id "id-111902" :extension [#fhir/Extension{:url "foo"}]})))) (testing "assoc extension" (testing "non-extended" - (is (= (type/assoc-extension #fhir/markdown "165645" [#fhir/Extension{:url "foo"}]) + (is (= (assoc #fhir/markdown "165645" :extension [#fhir/Extension{:url "foo"}]) #fhir/markdown{:extension [#fhir/Extension{:url "foo"}] :value "165645"}))) (testing "already extended" - (is (= (type/assoc-extension #fhir/markdown{:id "id-111953"} [#fhir/Extension{:url "foo"}]) + (is (= (assoc #fhir/markdown{:id "id-111953"} :extension [#fhir/Extension{:url "foo"}]) #fhir/markdown{:id "id-111953" :extension [#fhir/Extension{:url "foo"}]})) - (is (= (type/assoc-extension #fhir/markdown{:extension [#fhir/Extension{:url "foo"}]} [#fhir/Extension{:url "bar"}]) + (is (= (assoc #fhir/markdown{:extension [#fhir/Extension{:url "foo"}]} :extension [#fhir/Extension{:url "bar"}]) #fhir/markdown{:extension [#fhir/Extension{:url "bar"}]})))) (testing "value" - (are [x] (= "markdown-123745" (type/value x) (:value x) (:value x ::foo)) + (are [x] (= "markdown-123745" (:value x) (:value x) (:value x ::foo)) #fhir/markdown "markdown-123745" #fhir/markdown{:id "foo" :value "markdown-123745"})) (testing "assoc value" (testing "non-extended" - (is (= (type/assoc-value #fhir/markdown "markdown-165634" "markdown-165643") + (is (= (assoc #fhir/markdown "markdown-165634" :value "markdown-165643") #fhir/markdown "markdown-165643"))) (testing "already extended" - (is (= (type/assoc-value #fhir/markdown{:id "foo"} "markdown-171046") + (is (= (assoc #fhir/markdown{:id "foo"} :value "markdown-171046") #fhir/markdown{:id "foo" :value "markdown-171046"})))) (testing "lookup" @@ -2374,8 +2325,7 @@ (is (= ::not-found (::other-key #fhir/markdown "foo" ::not-found))))) (testing "to-json" - (is (= "\"markdown-123745\"" - (gen-json-string #fhir/markdown "markdown-123745")))) + (is (= "markdown-123745" (gen-json-value #fhir/markdown "markdown-123745")))) (testing "to-xml" (is (= (sexp-value "markdown-123745") @@ -2395,64 +2345,83 @@ #fhir/markdown{:id "foo" :value "175726"} "c9b526e9" #fhir/markdown{:extension [#fhir/Extension{:url "foo"}]} "8d0712c5")) + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/markdown "175726" 64 + #fhir/markdown{:id "foo"} 80)) + (testing "references" (is (empty? (type/references #fhir/markdown "151424")))) (testing "print" - (is (= "#fhir/markdown\"175718\"" (pr-str #fhir/markdown "175718"))))) + (are [x s] (= (pr-str x) s) + #fhir/markdown "175726" "#fhir/markdown \"175726\"" + #fhir/markdown{:id "foo"} "#fhir/markdown{:id \"foo\"}"))) (deftest unsignedInt-test (testing "unsignedInt?" (are [x] (type/unsignedInt? x) #fhir/unsignedInt 0 + (type/unsignedInt (dec (bit-shift-left 1 31))) #fhir/unsignedInt{:id "foo"})) (testing "type" - (are [x] (= :fhir/unsignedInt (type/type x)) + (are [x] (= :fhir/unsignedInt (:fhir/type x)) #fhir/unsignedInt 0 + (type/unsignedInt (dec (bit-shift-left 1 31))) #fhir/unsignedInt{:id "foo"})) + (testing "invalid value" + (doseq [x [-1 {:value -1}]] + (given (type/unsignedInt x) + ::anom/category := ::anom/incorrect + ::anom/message := "Invalid unsignedInt value `-1`.")) + + (doseq [x [(inc Integer/MAX_VALUE) {:value (inc Integer/MAX_VALUE)}]] + (given (type/unsignedInt x) + ::anom/category := ::anom/incorrect + ::anom/message := "Invalid unsignedInt value `2147483648`."))) + (testing "unsignedInt" (is (= #fhir/unsignedInt{:value 160845} #fhir/unsignedInt 160845))) - (testing "interned" + (testing "interning" (is (not-interned? #fhir/unsignedInt 160845 #fhir/unsignedInt 160845))) (testing "assoc id" (testing "non-extended" - (is (= (type/assoc-id #fhir/unsignedInt 165645 "id-111030") + (is (= (assoc #fhir/unsignedInt 165645 :id "id-111030") #fhir/unsignedInt{:id "id-111030" :value 165645}))) (testing "already extended" - (is (= (type/assoc-id #fhir/unsignedInt{:id "foo"} "bar") + (is (= (assoc #fhir/unsignedInt{:id "foo"} :id "bar") #fhir/unsignedInt{:id "bar"})) - (is (= (type/assoc-id #fhir/unsignedInt{:extension [#fhir/Extension{:url "foo"}]} "id-111902") + (is (= (assoc #fhir/unsignedInt{:extension [#fhir/Extension{:url "foo"}]} :id "id-111902") #fhir/unsignedInt{:id "id-111902" :extension [#fhir/Extension{:url "foo"}]})))) (testing "assoc extension" (testing "non-extended" - (is (= (type/assoc-extension #fhir/unsignedInt 165645 [#fhir/Extension{:url "foo"}]) + (is (= (assoc #fhir/unsignedInt 165645 :extension [#fhir/Extension{:url "foo"}]) #fhir/unsignedInt{:extension [#fhir/Extension{:url "foo"}] :value 165645}))) (testing "already extended" - (is (= (type/assoc-extension #fhir/unsignedInt{:id "id-111953"} [#fhir/Extension{:url "foo"}]) + (is (= (assoc #fhir/unsignedInt{:id "id-111953"} :extension [#fhir/Extension{:url "foo"}]) #fhir/unsignedInt{:id "id-111953" :extension [#fhir/Extension{:url "foo"}]})) - (is (= (type/assoc-extension #fhir/unsignedInt{:extension [#fhir/Extension{:url "foo"}]} [#fhir/Extension{:url "bar"}]) + (is (= (assoc #fhir/unsignedInt{:extension [#fhir/Extension{:url "foo"}]} :extension [#fhir/Extension{:url "bar"}]) #fhir/unsignedInt{:extension [#fhir/Extension{:url "bar"}]})))) (testing "value" - (are [x] (= 160845 (type/value x) (:value x) (:value x ::foo)) + (are [x] (= 160845 (:value x) (:value x) (:value x ::foo)) #fhir/unsignedInt 160845 #fhir/unsignedInt{:id "foo" :value 160845})) (testing "assoc value" (testing "non-extended" - (is (= (type/assoc-value #fhir/unsignedInt 1 2) - #fhir/unsignedInt 2))) + (is (= #fhir/unsignedInt 2 (assoc #fhir/unsignedInt 1 :value 2)))) (testing "already extended" - (is (= (type/assoc-value #fhir/unsignedInt{:id "foo"} 1) + (is (= (assoc #fhir/unsignedInt{:id "foo"} :value 1) #fhir/unsignedInt{:id "foo" :value 1})))) (testing "lookup" @@ -2460,7 +2429,7 @@ (is (= ::not-found (::other-key #fhir/unsignedInt 1 ::not-found))))) (testing "to-json" - (is (= "160845" (gen-json-string #fhir/unsignedInt 160845)))) + (is (= 160845 (gen-json-value #fhir/unsignedInt 160845)))) (testing "to-xml" (is (= (sexp-value "160845") @@ -2476,9 +2445,14 @@ #fhir/unsignedInt 160845 "10a52aa2" #fhir/unsignedInt{:value 160845} "10a52aa2" #fhir/unsignedInt{:id "foo"} "7a1f86be" - #fhir/unsignedInt{:id "foo" :value 160845} "b38b1609" + #fhir/unsignedInt{:id "foo" :value 160845} "aa5dbbe7" #fhir/unsignedInt{:extension [#fhir/Extension{:url "foo"}]} "8117a763")) + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/unsignedInt 160845 16 + #fhir/unsignedInt{:id "foo"} 80)) + (testing "references" (is (empty? (type/references #fhir/unsignedInt 151440)))) @@ -2491,7 +2465,7 @@ "#fhir/unsignedInt{:id \"id-192647\"}" #fhir/unsignedInt{:id "id-192703" :value 192711} - "#fhir/unsignedInt{:id \"id-192703\", :value 192711}" + "#fhir/unsignedInt{:id \"id-192703\" :value 192711}" #fhir/unsignedInt{:extension [#fhir/Extension{:url "url-192724"}]} "#fhir/unsignedInt{:extension [#fhir/Extension{:url \"url-192724\"}]}"))) @@ -2499,55 +2473,72 @@ (deftest positiveInt-test (testing "positiveInt?" (are [x] (type/positiveInt? x) - #fhir/positiveInt 0 + #fhir/positiveInt 1 + (type/positiveInt (dec (bit-shift-left 1 31))) #fhir/positiveInt{:id "foo"})) (testing "type" - (are [x] (= :fhir/positiveInt (type/type x)) - #fhir/positiveInt 0 + (are [x] (= :fhir/positiveInt (:fhir/type x)) + #fhir/positiveInt 1 + (type/positiveInt (dec (bit-shift-left 1 31))) #fhir/positiveInt{:id "foo"})) + (testing "invalid value" + (doseq [x [0 {:value 0}]] + (given (type/positiveInt x) + ::anom/category := ::anom/incorrect + ::anom/message := "Invalid positiveInt value `0`.")) + + (doseq [x [-1 {:value -1}]] + (given (type/positiveInt x) + ::anom/category := ::anom/incorrect + ::anom/message := "Invalid positiveInt value `-1`.")) + + (doseq [x [(inc Integer/MAX_VALUE) {:value (inc Integer/MAX_VALUE)}]] + (given (type/positiveInt x) + ::anom/category := ::anom/incorrect + ::anom/message := "Invalid positiveInt value `2147483648`."))) + (testing "positiveInt" (is (= #fhir/positiveInt{:value 160845} #fhir/positiveInt 160845))) - (testing "interned" + (testing "interning" (is (not-interned? #fhir/positiveInt 160845 #fhir/positiveInt 160845))) (testing "assoc id" (testing "non-extended" - (is (= (type/assoc-id #fhir/positiveInt 165645 "id-111030") + (is (= (assoc #fhir/positiveInt 165645 :id "id-111030") #fhir/positiveInt{:id "id-111030" :value 165645}))) (testing "already extended" - (is (= (type/assoc-id #fhir/positiveInt{:id "foo"} "bar") + (is (= (assoc #fhir/positiveInt{:id "foo"} :id "bar") #fhir/positiveInt{:id "bar"})) - (is (= (type/assoc-id #fhir/positiveInt{:extension [#fhir/Extension{:url "foo"}]} "id-111902") + (is (= (assoc #fhir/positiveInt{:extension [#fhir/Extension{:url "foo"}]} :id "id-111902") #fhir/positiveInt{:id "id-111902" :extension [#fhir/Extension{:url "foo"}]})))) (testing "assoc extension" (testing "non-extended" - (is (= (type/assoc-extension #fhir/positiveInt 165645 [#fhir/Extension{:url "foo"}]) + (is (= (assoc #fhir/positiveInt 165645 :extension [#fhir/Extension{:url "foo"}]) #fhir/positiveInt{:extension [#fhir/Extension{:url "foo"}] :value 165645}))) (testing "already extended" - (is (= (type/assoc-extension #fhir/positiveInt{:id "id-111953"} [#fhir/Extension{:url "foo"}]) + (is (= (assoc #fhir/positiveInt{:id "id-111953"} :extension [#fhir/Extension{:url "foo"}]) #fhir/positiveInt{:id "id-111953" :extension [#fhir/Extension{:url "foo"}]})) - (is (= (type/assoc-extension #fhir/positiveInt{:extension [#fhir/Extension{:url "foo"}]} [#fhir/Extension{:url "bar"}]) + (is (= (assoc #fhir/positiveInt{:extension [#fhir/Extension{:url "foo"}]} :extension [#fhir/Extension{:url "bar"}]) #fhir/positiveInt{:extension [#fhir/Extension{:url "bar"}]})))) (testing "value" - (are [x] (= 160845 (type/value x) (:value x) (:value x ::foo)) + (are [x] (= 160845 (:value x) (:value x) (:value x ::foo)) #fhir/positiveInt 160845 #fhir/positiveInt{:id "foo" :value 160845})) (testing "assoc value" (testing "non-extended" - (is (= (type/assoc-value #fhir/positiveInt 1 2) - #fhir/positiveInt 2))) + (is (= #fhir/positiveInt 2 (assoc #fhir/positiveInt 1 :value 2)))) (testing "already extended" - (is (= (type/assoc-value #fhir/positiveInt{:id "foo"} 1) + (is (= (assoc #fhir/positiveInt{:id "foo"} :value 1) #fhir/positiveInt{:id "foo" :value 1})))) (testing "lookup" @@ -2555,7 +2546,7 @@ (is (= ::not-found (::other-key #fhir/positiveInt 1 ::not-found))))) (testing "to-json" - (is (= "160845" (gen-json-string #fhir/positiveInt 160845)))) + (is (= 160845 (gen-json-value #fhir/positiveInt 160845)))) (testing "to-xml" (is (= (sexp-value "160845") @@ -2571,14 +2562,21 @@ #fhir/positiveInt 160845 "8c218d7d" #fhir/positiveInt{:value 160845} "8c218d7d" #fhir/positiveInt{:id "foo"} "3f7dbd4e" - #fhir/positiveInt{:id "foo" :value 160845} "8f325fc8" + #fhir/positiveInt{:id "foo" :value 160845} "2f1e63f" #fhir/positiveInt{:extension [#fhir/Extension{:url "foo"}]} "7c036682")) + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/positiveInt 160845 16 + #fhir/positiveInt{:id "foo"} 80)) + (testing "references" (is (empty? (type/references #fhir/positiveInt 151500)))) (testing "print" - (is (= "#fhir/positiveInt 160845" (pr-str #fhir/positiveInt 160845))))) + (are [x s] (= (pr-str x) s) + #fhir/positiveInt 160845 "#fhir/positiveInt 160845" + #fhir/positiveInt{:id "foo"} "#fhir/positiveInt{:id \"foo\"}"))) (deftest uuid-test (testing "uuid?" @@ -2587,14 +2585,14 @@ #fhir/uuid{:id "foo"})) (testing "type" - (are [x] (= :fhir/uuid (type/type x)) + (are [x] (= :fhir/uuid (:fhir/type x)) #fhir/uuid "urn:uuid:6d270b7d-bf7d-4c95-8e30-4d87360d47a3" #fhir/uuid{:id "foo"})) (testing "uuid" (is (= #fhir/uuid{:value "urn:uuid:6d270b7d-bf7d-4c95-8e30-4d87360d47a3"} #fhir/uuid "urn:uuid:6d270b7d-bf7d-4c95-8e30-4d87360d47a3"))) - (testing "interned" + (testing "interning" (is (not-interned? #fhir/uuid "urn:uuid:6d270b7d-bf7d-4c95-8e30-4d87360d47a3" #fhir/uuid "urn:uuid:6d270b7d-bf7d-4c95-8e30-4d87360d47a3")) @@ -2615,43 +2613,43 @@ (testing "assoc id" (testing "non-extended" - (is (= (type/assoc-id #fhir/uuid "urn:uuid:6d270b7d-bf7d-4c95-8e30-4d87360d47a3" "id-111030") + (is (= (assoc #fhir/uuid "urn:uuid:6d270b7d-bf7d-4c95-8e30-4d87360d47a3" :id "id-111030") #fhir/uuid{:id "id-111030" :value "urn:uuid:6d270b7d-bf7d-4c95-8e30-4d87360d47a3"}))) (testing "already extended" - (is (= (type/assoc-id #fhir/uuid{:id "foo"} "bar") + (is (= (assoc #fhir/uuid{:id "foo"} :id "bar") #fhir/uuid{:id "bar"})) - (is (= (type/assoc-id #fhir/uuid{:extension [#fhir/Extension{:url "foo"}]} "id-111902") + (is (= (assoc #fhir/uuid{:extension [#fhir/Extension{:url "foo"}]} :id "id-111902") #fhir/uuid{:id "id-111902" :extension [#fhir/Extension{:url "foo"}]})))) (testing "assoc extension" (testing "non-extended" - (is (= (type/assoc-extension #fhir/uuid "urn:uuid:6d270b7d-bf7d-4c95-8e30-4d87360d47a3" [#fhir/Extension{:url "foo"}]) + (is (= (assoc #fhir/uuid "urn:uuid:6d270b7d-bf7d-4c95-8e30-4d87360d47a3" :extension [#fhir/Extension{:url "foo"}]) #fhir/uuid{:extension [#fhir/Extension{:url "foo"}] :value "urn:uuid:6d270b7d-bf7d-4c95-8e30-4d87360d47a3"}))) (testing "already extended" - (is (= (type/assoc-extension #fhir/uuid{:id "id-111953"} [#fhir/Extension{:url "foo"}]) + (is (= (assoc #fhir/uuid{:id "id-111953"} :extension [#fhir/Extension{:url "foo"}]) #fhir/uuid{:id "id-111953" :extension [#fhir/Extension{:url "foo"}]})) - (is (= (type/assoc-extension #fhir/uuid{:extension [#fhir/Extension{:url "foo"}]} [#fhir/Extension{:url "bar"}]) + (is (= (assoc #fhir/uuid{:extension [#fhir/Extension{:url "foo"}]} :extension [#fhir/Extension{:url "bar"}]) #fhir/uuid{:extension [#fhir/Extension{:url "bar"}]})))) (testing "value" - (are [x] (= "urn:uuid:6d270b7d-bf7d-4c95-8e30-4d87360d47a3" (type/value x)) + (are [x] (= "urn:uuid:6d270b7d-bf7d-4c95-8e30-4d87360d47a3" (:value x)) #fhir/uuid "urn:uuid:6d270b7d-bf7d-4c95-8e30-4d87360d47a3" #fhir/uuid{:id "foo" :value "urn:uuid:6d270b7d-bf7d-4c95-8e30-4d87360d47a3"})) (testing "assoc value" (testing "non-extended" - (is (= (type/assoc-value #fhir/uuid "urn:uuid:6d270b7d-bf7d-4c95-8e30-4d87360d47a3" "urn:uuid:224c0729-05a7-4703-8ffd-acaa98d2d217") + (is (= (assoc #fhir/uuid "urn:uuid:6d270b7d-bf7d-4c95-8e30-4d87360d47a3" :value "urn:uuid:224c0729-05a7-4703-8ffd-acaa98d2d217") #fhir/uuid "urn:uuid:224c0729-05a7-4703-8ffd-acaa98d2d217"))) (testing "already extended" - (is (= (type/assoc-value #fhir/uuid{:id "foo"} "urn:uuid:224c0729-05a7-4703-8ffd-acaa98d2d217") + (is (= (assoc #fhir/uuid{:id "foo"} :value "urn:uuid:224c0729-05a7-4703-8ffd-acaa98d2d217") #fhir/uuid{:id "foo" :value "urn:uuid:224c0729-05a7-4703-8ffd-acaa98d2d217"})))) (testing "to-json" - (is (= "\"urn:uuid:6d270b7d-bf7d-4c95-8e30-4d87360d47a3\"" - (gen-json-string #fhir/uuid "urn:uuid:6d270b7d-bf7d-4c95-8e30-4d87360d47a3")))) + (is (= "urn:uuid:6d270b7d-bf7d-4c95-8e30-4d87360d47a3" + (gen-json-value #fhir/uuid "urn:uuid:6d270b7d-bf7d-4c95-8e30-4d87360d47a3")))) (testing "to-xml" (is (= (sexp-value "urn:uuid:6d270b7d-bf7d-4c95-8e30-4d87360d47a3") @@ -2670,11 +2668,21 @@ #fhir/uuid "urn:uuid:6d270b7d-bf7d-4c95-8e30-4d87360d47a3" "f894ff2b" #fhir/uuid{:value "urn:uuid:6d270b7d-bf7d-4c95-8e30-4d87360d47a3"} "f894ff2b" #fhir/uuid{:id "foo"} "3b18b5b7" - #fhir/uuid{:id "foo" :value "urn:uuid:6d270b7d-bf7d-4c95-8e30-4d87360d47a3"} "c23eebae" + #fhir/uuid{:id "foo" :value "urn:uuid:6d270b7d-bf7d-4c95-8e30-4d87360d47a3"} "64cb0e66" #fhir/uuid{:extension [#fhir/Extension{:url "foo"}]} "9160d648")) + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/uuid "urn:uuid:6d270b7d-bf7d-4c95-8e30-4d87360d47a3" 40 + #fhir/uuid{:id "foo"} 80)) + (testing "references" - (is (empty? (type/references #fhir/uuid "urn:uuid:89ddf6ab-8813-4c75-9500-dd07560fe817"))))) + (is (empty? (type/references #fhir/uuid "urn:uuid:89ddf6ab-8813-4c75-9500-dd07560fe817")))) + + (testing "print" + (are [x s] (= (pr-str x) s) + #fhir/uuid "urn:uuid:6d270b7d-bf7d-4c95-8e30-4d87360d47a3" "#fhir/uuid \"urn:uuid:6d270b7d-bf7d-4c95-8e30-4d87360d47a3\"" + #fhir/uuid{:id "foo"} "#fhir/uuid{:id \"foo\"}"))) (def xhtml-element (sexp @@ -2690,44 +2698,44 @@ (type/xml->Xhtml xhtml-element)))) (testing "type" - (is (= :fhir/xhtml (type/type #fhir/xhtml "")))) + (is (= :fhir/xhtml (:fhir/type #fhir/xhtml "")))) - (testing "interned" + (testing "interning" (is (not-interned? #fhir/xhtml "xhtml-123745" #fhir/xhtml "xhtml-123745"))) (testing "value" - (is (= "xhtml-123745" (type/value #fhir/xhtml "xhtml-123745")))) + (is (= "xhtml-123745" (:value #fhir/xhtml "xhtml-123745")))) (testing "assoc value" - (is (= (type/assoc-value #fhir/xhtml "xhtml-165634" "xhtml-165643") + (is (= (assoc #fhir/xhtml "xhtml-165634" :value "xhtml-165643") #fhir/xhtml "xhtml-165643"))) (testing "to-json" - (is (= "\"xhtml-123745\"" (gen-json-string #fhir/xhtml "xhtml-123745")))) + (is (= "xhtml-123745" (gen-json-value #fhir/xhtml "xhtml-123745")))) (testing "to-xml" (testing "plain text" - (is (= (xml/emit-str (type/to-xml #fhir/xhtml "xhtml-123745")) + (is (= (xml/emit-str (type/xhtml-to-xml #fhir/xhtml "xhtml-123745")) "
xhtml-123745
"))) (testing "not closed tag" - (is (= (xml/emit-str (type/to-xml #fhir/xhtml "")) + (is (= (xml/emit-str (type/xhtml-to-xml #fhir/xhtml "")) "
<foo>
"))) (testing "invalid tag" - (is (= (xml/emit-str (type/to-xml #fhir/xhtml "
<foo
"))) (testing "CDATA" - (is (= (xml/emit-str (type/to-xml #fhir/xhtml "")) + (is (= (xml/emit-str (type/xhtml-to-xml #fhir/xhtml "")) "
<![CDATA[foo]]>
"))) (testing "CDATA end" - (is (= (xml/emit-str (type/to-xml #fhir/xhtml "]]>")) + (is (= (xml/emit-str (type/xhtml-to-xml #fhir/xhtml "]]>")) "
]]>
"))) - (is (= xhtml-element (type/to-xml #fhir/xhtml "

FHIR is cool.

")))) + (is (= xhtml-element (type/xhtml-to-xml #fhir/xhtml "

FHIR is cool.

")))) (testing "equals" (is (= #fhir/xhtml "175726" #fhir/xhtml "175726")) @@ -2738,218 +2746,246 @@ (are [x hex] (= hex (murmur3 x)) #fhir/xhtml "175726" "e90ddf05")) + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/xhtml "175726" 64 + #fhir/xhtml{:id "foo"} 80)) + (testing "references" (is (empty? (type/references #fhir/xhtml "151551")))) (testing "print" - (is (= "#fhir/xhtml\"175718\"" (pr-str #fhir/xhtml "175718")))) + (are [x s] (= (pr-str x) s) + #fhir/xhtml "175726" "#fhir/xhtml \"175726\"" + #fhir/xhtml{:id "foo"} "#fhir/xhtml{:id \"foo\"}")) (testing "toString" - (is (= "175718" (str #fhir/xhtml "175718"))))) + (is (= "Xhtml{id=null, extension=[], value='175718'}" (str #fhir/xhtml "175718"))))) -(deftest attachment-test +(defn- recreate + "Takes `x`, a complex type and recreates it from its components using + `constructor`." + [constructor x] + (constructor (into {} x))) + +(def ^:private markdown-extension-gen + (fg/extension :value (fg/markdown :value fg/markdown-value))) + +(deftest address-test (testing "type" - (is (= :fhir/Attachment (type/type #fhir/Attachment{})))) + (is (= :fhir/Address (:fhir/type #fhir/Address{})))) - (testing "interned" + (testing "interning" (are [x y] (not-interned? x y) - #fhir/Attachment{:id "foo"} - #fhir/Attachment{:id "foo"}) + #fhir/Address{:id "foo"} + #fhir/Address{:id "foo"} - (are [x y] (interned? x y) - #fhir/Attachment{:extension [#fhir/Extension{:url "foo" :value #fhir/code "bar"}]} - #fhir/Attachment{:extension [#fhir/Extension{:url "foo" :value #fhir/code "bar"}]})) + #fhir/Address{:extension [#fhir/Extension{:url "foo" :value #fhir/string "barbar"}]} + #fhir/Address{:extension [#fhir/Extension{:url "foo" :value #fhir/string "barbar"}]} - (testing "primary/secondary content" - (is (true? (p/-has-primary-content #fhir/Attachment{}))) - (is (false? (p/-has-secondary-content #fhir/Attachment{})))) + #fhir/Address{:text #fhir/string "foofoo"} + #fhir/Address{:text #fhir/string "foofoo"})) (testing "hash-into" (are [x hex] (= hex (murmur3 x)) - #fhir/Attachment{} - "af56fc23" + #fhir/Address{} + "4a6b5e4f" - #fhir/Attachment{:id "id-204201"} - "fb9177d8" + #fhir/Address{:id "id-130739"} + "bd6a5731" - #fhir/Attachment{:extension [#fhir/Extension{}]} - "4947bc16" + #fhir/Address{:extension [#fhir/Extension{}]} + "2a3786e7" - #fhir/Attachment{:contentType #fhir/code "text/plain"} - "21d5985e" + #fhir/Address{:use #fhir/code "use-155144"} + "b6cf1d48" - #fhir/Attachment{:language #fhir/code "de"} - "223e2e7f" + #fhir/Address{:type #fhir/code "type-084442"} + "54c286c3" - #fhir/Attachment{:data #fhir/base64Binary "MTA1NjE0Cg=="} - "d2a23543" + #fhir/Address{:text #fhir/string "text-212402"} + "15baed84" - #fhir/Attachment{:url #fhir/url "url-210424"} - "67f9de2f" + #fhir/Address{:line [#fhir/string "line-212441"]} + "eafac0f1" - #fhir/Attachment{:size #fhir/unsignedInt 1} - "180724c5" + #fhir/Address{:line [#fhir/string "line-212448" #fhir/string "line-212454"]} + "62f4cf8f" - #fhir/Attachment{:hash #fhir/base64Binary "MTA1NjE0Cg=="} - "26e1ef66" + #fhir/Address{:city #fhir/string "city-084705"} + "9765a1e9" - #fhir/Attachment{:title #fhir/string "title-210622"} - "fce4d064" + #fhir/Address{:district #fhir/string "district-084717"} + "9e6dc6b8" - #fhir/Attachment{:creation #fhir/dateTime "2021"} - "1f9bf068")) + #fhir/Address{:state #fhir/string "state-084729"} + "17a7640f" + + #fhir/Address{:postalCode #fhir/string "postalCode-084832"} + "8880561c" + + #fhir/Address{:country #fhir/string "country-084845"} + "57c51a7d" + + #fhir/Address{:period #fhir/Period{}} + "fb17905a")) + + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/Address{} 56 + #fhir/Address{:id "id-130739"} 128 + #fhir/Address{:extension [#fhir/Extension{}]} 56 + #fhir/Address{:use #fhir/code "use-155144"} 56 + #fhir/Address{:type #fhir/code "type-084442"} 56 + #fhir/Address{:text #fhir/string "text-212402"} 120 + #fhir/Address{:line [#fhir/string "line-212441"]} 176 + #fhir/Address{:line [#fhir/string "line-212448" #fhir/string "line-212454"]} 248 + #fhir/Address{:city #fhir/string "city-084705"} 120 + #fhir/Address{:district #fhir/string "district-084717"} 128 + #fhir/Address{:state #fhir/string "state-084729"} 120 + #fhir/Address{:postalCode #fhir/string "postalCode-084832"} 128 + #fhir/Address{:country #fhir/string "country-084845"} 128 + #fhir/Address{:period #fhir/Period{}} 56)) (testing "references" - (is (empty? (type/references #fhir/Attachment{})))) + (is (empty? (type/references #fhir/Address{})))) (testing "print" (are [v s] (= s (pr-str v)) - #fhir/Attachment{} "#fhir/Attachment{}" - #fhir/Attachment{:id "212329"} "#fhir/Attachment{:id \"212329\"}"))) + #fhir/Address{} "#fhir/Address{}" + #fhir/Address{:id "084856"} "#fhir/Address{:id \"084856\"}"))) -(deftest extension-test +(deftest age-test (testing "type" - (is (= :fhir/Extension (type/type #fhir/Extension{})))) - - (testing "interned" - (testing "instances with code values are interned" - (are [x y] (interned? x y) - #fhir/Extension{:url "foo" :value #fhir/code "bar"} - #fhir/Extension{:url "foo" :value #fhir/code "bar"})) - - (testing "instances with code values and interned extensions are interned" - (are [x y] (interned? x y) - #fhir/Extension - {:extension [#fhir/Extension{:url "foo" :value #fhir/code "bar"}] - :url "foo" - :value #fhir/code "bar"} - #fhir/Extension - {:extension [#fhir/Extension{:url "foo" :value #fhir/code "bar"}] - :url "foo" - :value #fhir/code "bar"})) + (is (= :fhir/Age (:fhir/type #fhir/Age{})))) - (testing "instances with code values but id's are not interned" - (are [x y] (not-interned? x y) - #fhir/Extension{:id "foo" :url "bar" :value #fhir/code "baz"} - #fhir/Extension{:id "foo" :url "bar" :value #fhir/code "baz"})) + (testing "print" + (are [v s] (= s (pr-str v)) + #fhir/Age{} "#fhir/Age{}" + #fhir/Age{:id "212329"} "#fhir/Age{:id \"212329\"}"))) - (testing "instances with string values are not interned" - (are [x y] (not-interned? x y) - #fhir/Extension{:url "foo" :value #fhir/string "bar"} - #fhir/Extension{:url "foo" :value #fhir/string "bar"}))) +(deftest annotation-test + (testing "type" + (is (= :fhir/Annotation (:fhir/type #fhir/Annotation{})))) - (testing "primary/secondary content" - (is (true? (p/-has-primary-content #fhir/Extension{}))) - (is (false? (p/-has-secondary-content #fhir/Extension{})))) + (testing "print" + (are [v s] (= s (pr-str v)) + #fhir/Annotation{} "#fhir/Annotation{}" + #fhir/Annotation{:id "212329"} "#fhir/Annotation{:id \"212329\"}"))) - (testing "to-json" - (are [code json] (= json (gen-json-string code)) - #fhir/Extension{} "{}" - #fhir/Extension{:id "id-162531"} "{\"id\":\"id-162531\"}")) +(deftest attachment-test + (testing "type" + (is (= :fhir/Attachment (:fhir/type #fhir/Attachment{})))) - (testing "equals" - (is (= #fhir/Extension{:url ""} #fhir/Extension{:url ""}))) + (testing "interning" + (are [x y] (not-interned? x y) + #fhir/Attachment{:id "foo"} + #fhir/Attachment{:id "foo"})) (testing "hash-into" (are [x hex] (= hex (murmur3 x)) - #fhir/Extension{} - "9b6ea882" + #fhir/Attachment{} + "af56fc23" - #fhir/Extension{:id "id-204201"} - "495cd278" + #fhir/Attachment{:id "id-204201"} + "fb9177d8" - #fhir/Extension{:extension [#fhir/Extension{}]} - "af661e95" + #fhir/Attachment{:extension [#fhir/Extension{}]} + "4947bc16" - #fhir/Extension{:extension [#fhir/Extension{} #fhir/Extension{}]} - "96fd01bd" + #fhir/Attachment{:contentType #fhir/code "text/plain"} + "21d5985e" - #fhir/Extension{:url "url-130945"} - "8204427a" + #fhir/Attachment{:language #fhir/code "de"} + "223e2e7f" - #fhir/Extension{:value #fhir/code "value-130953"} - "befce87a")) + #fhir/Attachment{:data #fhir/base64Binary "MTA1NjE0Cg=="} + "d2a23543" + + #fhir/Attachment{:url #fhir/url "url-210424"} + "67f9de2f" + + #fhir/Attachment{:size #fhir/unsignedInt 1} + "180724c5" + + #fhir/Attachment{:hash #fhir/base64Binary "MTA1NjE0Cg=="} + "26e1ef66" + + #fhir/Attachment{:title #fhir/string "title-210622"} + "fce4d064" + + #fhir/Attachment{:creation #fhir/dateTime #system/date-time "2021"} + "1f9bf068")) + + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/Attachment{:id "id-204201"} 120 + #fhir/Attachment{:extension [#fhir/Extension{}]} 48 + #fhir/Attachment{:contentType #fhir/code "text/plain"} 48 + #fhir/Attachment{:language #fhir/code "de"} 48 + #fhir/Attachment{:data #fhir/base64Binary "MTA1NjE0Cg=="} 112)) (testing "references" - (is (empty? (type/references #fhir/Extension{})))) + (is (empty? (type/references #fhir/Attachment{})))) (testing "print" (are [v s] (= s (pr-str v)) - #fhir/Extension{} "#fhir/Extension{}" - #fhir/Extension{:id "212329"} "#fhir/Extension{:id \"212329\"}"))) - -(defn- recreate - "Takes `x`, a complex type and recreates it from its components using - `constructor`." - [constructor x] - (constructor (into {} (remove (comp nil? val)) x))) - -(def ^:private string-extension-gen - (fg/extension :value (fg/string :value fg/string-value))) + #fhir/Attachment{} "#fhir/Attachment{}" + #fhir/Attachment{:id "212329"} "#fhir/Attachment{:id \"212329\"}"))) -(deftest coding-test +(deftest bundle-entry-search-test (testing "type" - (is (= :fhir/Coding (type/type #fhir/Coding{})))) + (is (= :fhir.Bundle.entry/search (:fhir/type #fhir.Bundle.entry/search{})))) - (testing "interned" - (satisfies-prop 100 - (prop/for-all [x (fg/coding :id (gen/return nil) :extension (fg/extensions :id (gen/return nil) :extension (gen/return nil) :value (fg/code :id (gen/return nil) :extension (gen/return nil))))] - (interned? x (recreate type/coding x)))) - - (testing "instances with id's are not interned" - (satisfies-prop 100 - (prop/for-all [x (fg/coding :id fg/id-value)] - (not-interned? x (recreate type/coding x))))) + (testing "interning" + (are [x y] (not-interned? x y) + #fhir.Bundle.entry/search{:id "foo"} + #fhir.Bundle.entry/search{:id "foo"} - (testing "instances with not interned extensions are not interned" - (satisfies-prop 100 - (prop/for-all [x (fg/coding :extension (gen/vector string-extension-gen 1))] - (not-interned? x (recreate type/coding x)))))) + #fhir.Bundle.entry/search{:extension [#fhir/Extension{:url "foo" :value #fhir/string "barbar"}]} + #fhir.Bundle.entry/search{:extension [#fhir/Extension{:url "foo" :value #fhir/string "barbar"}]} - (testing "primary/secondary content" - (is (true? (p/-has-primary-content #fhir/Coding{}))) - (is (false? (p/-has-secondary-content #fhir/Coding{})))) + #fhir.Bundle.entry/search{:score #fhir/decimal 1M} + #fhir.Bundle.entry/search{:score #fhir/decimal 1M})) (testing "hash-into" (are [x hex] (= hex (murmur3 x)) - #fhir/Coding{} - "24e7e891" - - #fhir/Coding{:id "id-204201"} - "c1c82c65" + #fhir.Bundle.entry/search{} + "f945531f" - #fhir/Coding{:extension [#fhir/Extension{}]} - "e1d440bb" + #fhir.Bundle.entry/search{:id "id-130825"} + "6b1b9201" - #fhir/Coding{:system #fhir/uri "system-202808"} - "da808d2d" + #fhir.Bundle.entry/search{:extension [#fhir/Extension{}]} + "f24daf4f" - #fhir/Coding{:version #fhir/uri "version-154317"} - "93fc58d9" + #fhir.Bundle.entry/search{:mode #fhir/code "match"} + "5912b48c" - #fhir/Coding{:code #fhir/code "code-202828"} - "74e3328d" + #fhir.Bundle.entry/search{:score #fhir/decimal 1M} + "2b2509dc")) - #fhir/Coding{:display #fhir/string "display-154256"} - "baac923d")) + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir.Bundle.entry/search{} 24 + #fhir.Bundle.entry/search{:id "id-130825"} 96 + #fhir.Bundle.entry/search{:extension [#fhir/Extension{}]} 24 + #fhir.Bundle.entry/search{:mode #fhir/code "match"} 24 + #fhir.Bundle.entry/search{:score #fhir/decimal 1M} 72)) (testing "references" - (is (empty? (type/references #fhir/Coding{})))) + (is (empty? (type/references #fhir.Bundle.entry/search{})))) (testing "print" (are [v s] (= s (pr-str v)) - #fhir/Coding{} "#fhir/Coding{}" - #fhir/Coding{:id "212329"} "#fhir/Coding{:id \"212329\"}"))) + #fhir.Bundle.entry/search{} "#fhir.Bundle.entry/search{}" + #fhir.Bundle.entry/search{:id "212329"} "#fhir.Bundle.entry/search{:id \"212329\"}"))) (deftest codeable-concept-test (testing "type" - (is (= :fhir/CodeableConcept (type/type #fhir/CodeableConcept{})))) - - (testing "interned" - (satisfies-prop 100 - (prop/for-all [x (fg/codeable-concept :id (gen/return nil) :extension (fg/extensions :id (gen/return nil) :extension (gen/return nil) :value (fg/code :id (gen/return nil) :extension (gen/return nil))))] - (interned? x (recreate type/codeable-concept x)))) + (is (= :fhir/CodeableConcept (:fhir/type #fhir/CodeableConcept{})))) + (testing "interning" (testing "instances with id's are not interned" (satisfies-prop 100 (prop/for-all [x (fg/codeable-concept :id fg/id-value)] @@ -2957,13 +2993,9 @@ (testing "instances with not interned extensions are not interned" (satisfies-prop 100 - (prop/for-all [x (fg/codeable-concept :extension (gen/vector string-extension-gen 1))] + (prop/for-all [x (fg/codeable-concept :extension (gen/vector markdown-extension-gen 1))] (not-interned? x (recreate type/codeable-concept x)))))) - (testing "primary/secondary content" - (is (true? (p/-has-primary-content #fhir/CodeableConcept{}))) - (is (false? (p/-has-secondary-content #fhir/CodeableConcept{})))) - (testing "hash-into" (are [x hex] (= hex (murmur3 x)) #fhir/CodeableConcept{} @@ -2981,6 +3013,16 @@ #fhir/CodeableConcept{:text #fhir/string "text-153829"} "fe2e61f1")) + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/CodeableConcept{} 0 + #fhir/CodeableConcept{:id "id-141755"} 96 + #fhir/CodeableConcept{:extension [#fhir/Extension{}]} 0 + #fhir/CodeableConcept{:coding [#fhir/Coding{}]} 0 + #fhir/CodeableConcept{:coding [#fhir/Coding{:id "foo"}]} 184 + #fhir/CodeableConcept{:text #fhir/string-interned "text-153829"} 0 + #fhir/CodeableConcept{:text #fhir/string "text-153829"} 88)) + (testing "references" (is (empty? (type/references #fhir/CodeableConcept{})))) @@ -2989,205 +3031,349 @@ #fhir/CodeableConcept{} "#fhir/CodeableConcept{}" #fhir/CodeableConcept{:id "212329"} "#fhir/CodeableConcept{:id \"212329\"}"))) -(deftest quantity-test +(deftest coding-test (testing "type" - (is (= :fhir/Quantity (type/type #fhir/Quantity{})))) - - (testing "interned" - (are [x y] (not-interned? x y) - #fhir/Quantity{:id "foo"} - #fhir/Quantity{:id "foo"} + (is (= :fhir/Coding (:fhir/type #fhir/Coding{})))) - #fhir/Quantity{:extension [#fhir/Extension{:url "foo" :value #fhir/string "bar"}]} - #fhir/Quantity{:extension [#fhir/Extension{:url "foo" :value #fhir/string "bar"}]} + (testing "interning" + (testing "instances with id's are not interned" + (satisfies-prop 100 + (prop/for-all [x (fg/coding :id fg/id-value)] + (not-interned? x (recreate type/coding x))))) - #fhir/Quantity{:value #fhir/decimal 1M} - #fhir/Quantity{:value #fhir/decimal 1M}) + (testing "instances with not interned extensions are not interned" + (satisfies-prop 100 + (prop/for-all [x (fg/coding :extension (gen/vector markdown-extension-gen 1))] + (not-interned? x (recreate type/coding x)))))) - (are [x y] (interned? x y) - #fhir/Quantity{:extension [#fhir/Extension{:url "foo" :value #fhir/code "bar"}]} - #fhir/Quantity{:extension [#fhir/Extension{:url "foo" :value #fhir/code "bar"}]} + (testing "hash-into" + (are [x hex] (= hex (murmur3 x)) + #fhir/Coding{} + "24e7e891" - #fhir/Quantity{:comparator #fhir/code "foo"} - #fhir/Quantity{:comparator #fhir/code "foo"} + #fhir/Coding{:id "id-204201"} + "c1c82c65" - #fhir/Quantity{:unit #fhir/string "foo"} - #fhir/Quantity{:unit #fhir/string "foo"} + #fhir/Coding{:extension [#fhir/Extension{}]} + "e1d440bb" - #fhir/Quantity{:system #fhir/uri "foo"} - #fhir/Quantity{:system #fhir/uri "foo"} + #fhir/Coding{:system #fhir/uri "system-202808"} + "da808d2d" - #fhir/Quantity{:code #fhir/code "foo"} - #fhir/Quantity{:code #fhir/code "foo"})) + #fhir/Coding{:version #fhir/string "version-154317"} + "9df26acc" - (testing "primary/secondary content" - (is (true? (p/-has-primary-content #fhir/Quantity{}))) - (is (false? (p/-has-secondary-content #fhir/Quantity{})))) + #fhir/Coding{:code #fhir/code "code-202828"} + "74e3328d" - (testing "hash-into" - (are [x hex] (= hex (murmur3 x)) - #fhir/Quantity{} - "1ddef3ed" + #fhir/Coding{:display #fhir/string "display-154256"} + "baac923d")) - #fhir/Quantity{:id "id-141848"} - "abb59da1" + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/Coding{} 0 + #fhir/Coding{:id "id-204201"} 112 + #fhir/Coding{:extension [#fhir/Extension{}]} 0 + #fhir/Coding{:system #fhir/uri-interned "system-202808"} 0 + #fhir/Coding{:version #fhir/string-interned "version-154317"} 0 + #fhir/Coding{:code #fhir/code "code-202828"} 0 + #fhir/Coding{:display #fhir/string-interned "display-154256"} 0 + #fhir/Coding{:display #fhir/string "display-154256"} 112)) - #fhir/Quantity{:extension [#fhir/Extension{}]} - "4f5028ac" + (testing "references" + (is (empty? (type/references #fhir/Coding{})))) - #fhir/Quantity{:value #fhir/decimal 1M} - "4adf97ab" + (testing "print" + (are [v s] (= s (pr-str v)) + #fhir/Coding{} "#fhir/Coding{}" + #fhir/Coding{:id "212329"} "#fhir/Coding{:id \"212329\"}"))) - #fhir/Quantity{:comparator #fhir/code "comparator-153342"} - "6339e3e8" +(deftest contact-detail-test + (testing "type" + (is (= :fhir/ContactDetail (:fhir/type #fhir/ContactDetail{})))) - #fhir/Quantity{:unit #fhir/string "unit-153351"} - "d8f92891" + (testing "print" + (are [v s] (= s (pr-str v)) + #fhir/ContactDetail{} "#fhir/ContactDetail{}" + #fhir/ContactDetail{:id "212329"} "#fhir/ContactDetail{:id \"212329\"}"))) - #fhir/Quantity{:system #fhir/uri "system-153337"} - "98f918ba" +(deftest contact-point-test + (testing "type" + (is (= :fhir/ContactPoint (:fhir/type #fhir/ContactPoint{})))) - #fhir/Quantity{:code #fhir/code "code-153427"} - "7ff49528")) + (testing "print" + (are [v s] (= s (pr-str v)) + #fhir/ContactPoint{} "#fhir/ContactPoint{}" + #fhir/ContactPoint{:id "212329"} "#fhir/ContactPoint{:id \"212329\"}"))) - (testing "references" - (is (empty? (type/references #fhir/Quantity{})))) +(deftest contributor-test + (testing "type" + (is (= :fhir/Contributor (:fhir/type #fhir/Contributor{})))) (testing "print" (are [v s] (= s (pr-str v)) - #fhir/Quantity{} "#fhir/Quantity{}" - #fhir/Quantity{:id "212329"} "#fhir/Quantity{:id \"212329\"}"))) + #fhir/Contributor{} "#fhir/Contributor{}" + #fhir/Contributor{:id "212329"} "#fhir/Contributor{:id \"212329\"}"))) -(deftest ratio-test +(deftest count-test (testing "type" - (is (= :fhir/Ratio (type/type #fhir/Ratio{})))) + (is (= :fhir/Count (:fhir/type #fhir/Count{})))) - (testing "interned" - (are [x y] (not-interned? x y) - #fhir/Ratio{:id "foo"} - #fhir/Ratio{:id "foo"} + (testing "print" + (are [v s] (= s (pr-str v)) + #fhir/Count{} "#fhir/Count{}" + #fhir/Count{:id "212329"} "#fhir/Count{:id \"212329\"}"))) - #fhir/Ratio{:extension [#fhir/Extension{:url "foo" :value #fhir/string "bar"}]} - #fhir/Ratio{:extension [#fhir/Extension{:url "foo" :value #fhir/string "bar"}]} +(deftest data-requirement-test + (testing "type" + (is (= :fhir/DataRequirement (:fhir/type #fhir/DataRequirement{})))) - #fhir/Ratio{:numerator #fhir/Quantity{:value #fhir/decimal 1M}} - #fhir/Ratio{:numerator #fhir/Quantity{:value #fhir/decimal 1M}} + (testing "print" + (are [v s] (= s (pr-str v)) + #fhir/DataRequirement{} "#fhir/DataRequirement{}" + #fhir/DataRequirement{:id "212329"} "#fhir/DataRequirement{:id \"212329\"}"))) - #fhir/Ratio{:denominator #fhir/Quantity{:value #fhir/decimal 1M}} - #fhir/Ratio{:denominator #fhir/Quantity{:value #fhir/decimal 1M}}) +(deftest data-requirement-code-filter-test + (testing "type" + (is (= :fhir.DataRequirement/codeFilter (:fhir/type #fhir.DataRequirement/codeFilter{})))) - (are [x y] (interned? x y) - #fhir/Ratio{:extension [#fhir/Extension{:url "foo" :value #fhir/code "bar"}]} - #fhir/Ratio{:extension [#fhir/Extension{:url "foo" :value #fhir/code "bar"}]} + (testing "print" + (are [v s] (= s (pr-str v)) + #fhir.DataRequirement/codeFilter{} "#fhir.DataRequirement/codeFilter{}" + #fhir.DataRequirement/codeFilter{:id "212329"} "#fhir.DataRequirement/codeFilter{:id \"212329\"}"))) - #fhir/Ratio{:numerator #fhir/Quantity{:code #fhir/code "foo"}} - #fhir/Ratio{:numerator #fhir/Quantity{:code #fhir/code "foo"}} +(deftest data-requirement-date-filter-test + (testing "type" + (is (= :fhir.DataRequirement/dateFilter (:fhir/type #fhir.DataRequirement/dateFilter{})))) - #fhir/Ratio{:denominator #fhir/Quantity{:code #fhir/code "foo"}} - #fhir/Ratio{:denominator #fhir/Quantity{:code #fhir/code "foo"}})) + (testing "print" + (are [v s] (= s (pr-str v)) + #fhir.DataRequirement/dateFilter{} "#fhir.DataRequirement/dateFilter{}" + #fhir.DataRequirement/dateFilter{:id "212329"} "#fhir.DataRequirement/dateFilter{:id \"212329\"}"))) - (testing "primary/secondary content" - (is (true? (p/-has-primary-content #fhir/Ratio{}))) - (is (false? (p/-has-secondary-content #fhir/Ratio{})))) +(deftest data-requirement-sort-test + (testing "type" + (is (= :fhir.DataRequirement/sort (:fhir/type #fhir.DataRequirement/sort{})))) - (testing "hash-into" - (are [x hex] (= hex (murmur3 x)) - #fhir/Ratio{} - "d271c07f" + (testing "print" + (are [v s] (= s (pr-str v)) + #fhir.DataRequirement/sort{} "#fhir.DataRequirement/sort{}" + #fhir.DataRequirement/sort{:id "212329"} "#fhir.DataRequirement/sort{:id \"212329\"}"))) - #fhir/Ratio{:id "id-130710"} - "e3c0ee3c" +(deftest distance-test + (testing "type" + (is (= :fhir/Distance (:fhir/type #fhir/Distance{})))) - #fhir/Ratio{:extension [#fhir/Extension{}]} - "23473d24" + (testing "print" + (are [v s] (= s (pr-str v)) + #fhir/Distance{} "#fhir/Distance{}" + #fhir/Distance{:id "212329"} "#fhir/Distance{:id \"212329\"}"))) - #fhir/Ratio{:numerator #fhir/Quantity{:value #fhir/decimal 1M}} - "fbf83a67" +(deftest duration-test + (testing "type" + (is (= :fhir/Duration (:fhir/type #fhir/Duration{})))) - #fhir/Ratio{:denominator #fhir/Quantity{:value #fhir/decimal 1M}} - "7f2075fb")) + (testing "print" + (are [v s] (= s (pr-str v)) + #fhir/Duration{} "#fhir/Duration{}" + #fhir/Duration{:id "212329"} "#fhir/Duration{:id \"212329\"}"))) - (testing "references" - (is (empty? (type/references #fhir/Ratio{})))) +(deftest expression-test + (testing "type" + (is (= :fhir/Expression (:fhir/type #fhir/Expression{})))) (testing "print" (are [v s] (= s (pr-str v)) - #fhir/Ratio{} "#fhir/Ratio{}" - #fhir/Ratio{:id "212329"} "#fhir/Ratio{:id \"212329\"}"))) + #fhir/Expression{} "#fhir/Expression{}" + #fhir/Expression{:id "212329"} "#fhir/Expression{:id \"212329\"}"))) -(deftest period-test +(deftest dosage-test (testing "type" - (is (= :fhir/Period (type/type #fhir/Period{})))) + (is (= :fhir/Dosage (:fhir/type #fhir/Dosage{})))) - (testing "interned" - (are [x y] (not-interned? x y) - #fhir/Period{:id "foo"} - #fhir/Period{:id "foo"} + (testing "print" + (are [v s] (= s (pr-str v)) + #fhir/Dosage{} "#fhir/Dosage{}" + #fhir/Dosage{:id "212329"} "#fhir/Dosage{:id \"212329\"}"))) - #fhir/Period{:extension [#fhir/Extension{:url "foo" :value #fhir/string "bar"}]} - #fhir/Period{:extension [#fhir/Extension{:url "foo" :value #fhir/string "bar"}]} +(deftest dosage-dose-and-rate-test + (testing "type" + (is (= :fhir.Dosage/doseAndRate (:fhir/type #fhir.Dosage/doseAndRate{})))) - #fhir/Period{:start #fhir/dateTime "2020"} - #fhir/Period{:start #fhir/dateTime "2020"}) + (testing "print" + (are [v s] (= s (pr-str v)) + #fhir.Dosage/doseAndRate{} "#fhir.Dosage/doseAndRate{}" + #fhir.Dosage/doseAndRate{:id "212329"} "#fhir.Dosage/doseAndRate{:id \"212329\"}"))) - (are [x y] (interned? x y) - #fhir/Period{:extension [#fhir/Extension{:url "foo" :value #fhir/code "bar"}]} - #fhir/Period{:extension [#fhir/Extension{:url "foo" :value #fhir/code "bar"}]})) +(deftest extension-test + (testing "type" + (is (= :fhir/Extension (:fhir/type #fhir/Extension{})))) + + (testing "interning" + (testing "instances with code values are interned" + (are [x y] (interned? x y) + #fhir/Extension{:url "foo" :value #fhir/code "bar"} + #fhir/Extension{:url "foo" :value #fhir/code "bar"})) + + (testing "instances with code values and interned extensions are interned" + (are [x y] (interned? x y) + #fhir/Extension + {:extension [#fhir/Extension{:url "foo" :value #fhir/code "bar"}] + :url "foo" + :value #fhir/code "bar"} + #fhir/Extension + {:extension [#fhir/Extension{:url "foo" :value #fhir/code "bar"}] + :url "foo" + :value #fhir/code "bar"})) - (testing "primary/secondary content" - (is (true? (p/-has-primary-content #fhir/Period{}))) - (is (false? (p/-has-secondary-content #fhir/Period{})))) + (testing "instances with code values but id's are not interned" + (are [x y] (not-interned? x y) + #fhir/Extension{:id "foo" :url "bar" :value #fhir/code "baz"} + #fhir/Extension{:id "foo" :url "bar" :value #fhir/code "baz"})) + + (testing "instances with string values are not interned" + (are [x y] (not-interned? x y) + #fhir/Extension{:url "foo" :value #fhir/string "barbar"} + #fhir/Extension{:url "foo" :value #fhir/string "barbar"}))) + + (testing "equals" + (is (= #fhir/Extension{:url ""} #fhir/Extension{:url ""}))) (testing "hash-into" (are [x hex] (= hex (murmur3 x)) - #fhir/Period{} - "e5f76205" + #fhir/Extension{} + "9b6ea882" - #fhir/Period{:id "id-130710"} - "29c53420" + #fhir/Extension{:id "id-204201"} + "495cd278" - #fhir/Period{:extension [#fhir/Extension{}]} - "92e4ba37" + #fhir/Extension{:extension [#fhir/Extension{}]} + "af661e95" - #fhir/Period{:start #fhir/dateTime "2020"} - "f1b7c952" + #fhir/Extension{:extension [#fhir/Extension{} #fhir/Extension{}]} + "96fd01bd" - #fhir/Period{:end #fhir/dateTime "2020"} - "434787dd")) + #fhir/Extension{:url "url-130945"} + "8204427a" + + #fhir/Extension{:value #fhir/code "value-130953"} + "befce87a")) + + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/Extension{} 0 + #fhir/Extension{:id "id-204201"} 96 + #fhir/Extension{:extension [#fhir/Extension{}]} 0 + #fhir/Extension{:extension [#fhir/Extension{} #fhir/Extension{}]} 0 + #fhir/Extension{:url "url-130945"} 0 + #fhir/Extension{:value #fhir/code "value-130953"} 0 + #fhir/Extension{:url "url-130945" :value #fhir/string "12345"} 88)) (testing "references" - (is (empty? (type/references #fhir/Period{})))) + (is (empty? (type/references #fhir/Extension{})))) (testing "print" (are [v s] (= s (pr-str v)) - #fhir/Period{} "#fhir/Period{}" - #fhir/Period{:id "212329"} "#fhir/Period{:id \"212329\"}"))) + #fhir/Extension{} "#fhir/Extension{}" + #fhir/Extension{:id "212329"} "#fhir/Extension{:id \"212329\"}"))) -(deftest identifier-test +(deftest human-name-test (testing "type" - (is (= :fhir/Identifier (type/type #fhir/Identifier{})))) + (is (= :fhir/HumanName (:fhir/type #fhir/HumanName{})))) - (testing "interned" + (testing "interning" (are [x y] (not-interned? x y) - #fhir/Identifier{:id "foo"} - #fhir/Identifier{:id "foo"} + #fhir/HumanName{:id "foo"} + #fhir/HumanName{:id "foo"} - #fhir/Identifier{:extension [#fhir/Extension{:url "foo" :value #fhir/string "bar"}]} - #fhir/Identifier{:extension [#fhir/Extension{:url "foo" :value #fhir/string "bar"}]} + #fhir/HumanName{:extension [#fhir/Extension{:url "foo" :value #fhir/string "barbar"}]} + #fhir/HumanName{:extension [#fhir/Extension{:url "foo" :value #fhir/string "barbar"}]} - #fhir/Identifier{:value #fhir/string "foo"} - #fhir/Identifier{:value #fhir/string "foo"}) + #fhir/HumanName{:text #fhir/string "foofoo"} + #fhir/HumanName{:text #fhir/string "foofoo"} - (are [x y] (interned? x y) - #fhir/Identifier{:extension [#fhir/Extension{:url "foo" :value #fhir/code "bar"}]} - #fhir/Identifier{:extension [#fhir/Extension{:url "foo" :value #fhir/code "bar"}]} + #fhir/HumanName{:family #fhir/string "foofoo"} + #fhir/HumanName{:family #fhir/string "foofoo"})) - #fhir/Identifier{:use #fhir/code "foo"} - #fhir/Identifier{:use #fhir/code "foo"})) + (testing "hash-into" + (are [x hex] (= hex (murmur3 x)) + #fhir/HumanName{} + "af56fc23" + + #fhir/HumanName{:id "id-130739"} + "ebba60f8" + + #fhir/HumanName{:extension [#fhir/Extension{}]} + "4947bc16" + + #fhir/HumanName{:use #fhir/code "use-155144"} + "60b2b58c" + + #fhir/HumanName{:text #fhir/string "text-212402"} + "b9ab5f61" + + #fhir/HumanName{:family #fhir/string "family-212422"} + "915831d8" + + #fhir/HumanName{:given [#fhir/string "given-212441"]} + "e26a58ee" - (testing "primary/secondary content" - (is (true? (p/-has-primary-content #fhir/Identifier{}))) - (is (false? (p/-has-secondary-content #fhir/Identifier{})))) + #fhir/HumanName{:given [#fhir/string "given-212448" #fhir/string "given-212454"]} + "b46d5198" + + #fhir/HumanName{:prefix [#fhir/string "prefix-212514"]} + "1a411067" + + #fhir/HumanName{:prefix [#fhir/string "prefix-212523" #fhir/string "prefix-212525"]} + "32529f07" + + #fhir/HumanName{:suffix [#fhir/string "suffix-212542"]} + "3181f719" + + #fhir/HumanName{:suffix [#fhir/string "suffix-212547" #fhir/string "suffix-212554"]} + "69ca06e0" + + #fhir/HumanName{:period #fhir/Period{}} + "18b2a823")) + + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/HumanName{} 40 + #fhir/HumanName{:id "id-130739"} 112 + #fhir/HumanName{:extension [#fhir/Extension{}]} 40 + #fhir/HumanName{:use #fhir/code "use-155144"} 40 + #fhir/HumanName{:text #fhir/string "text-212402"} 104 + #fhir/HumanName{:family #fhir/string "family-212422"} 112 + #fhir/HumanName{:given [#fhir/string "given-212441"]} 160 + #fhir/HumanName{:given [#fhir/string "given-212448" #fhir/string "given-212454"]} 232 + #fhir/HumanName{:prefix [#fhir/string "prefix-212514"]} 168 + #fhir/HumanName{:prefix [#fhir/string "prefix-212523" #fhir/string "prefix-212525"]} 248 + #fhir/HumanName{:suffix [#fhir/string "suffix-212542"]} 168 + #fhir/HumanName{:suffix [#fhir/string "suffix-212547" #fhir/string "suffix-212554"]} 248 + #fhir/HumanName{:period #fhir/Period{}} 40)) + + (testing "references" + (is (empty? (type/references #fhir/HumanName{})))) + + (testing "print" + (are [v s] (= s (pr-str v)) + #fhir/HumanName{} "#fhir/HumanName{}" + #fhir/HumanName{:id "212625"} "#fhir/HumanName{:id \"212625\"}"))) + +(deftest identifier-test + (testing "type" + (is (= :fhir/Identifier (:fhir/type #fhir/Identifier{})))) + + (testing "interning" + (are [x y] (not-interned? x y) + #fhir/Identifier{:id "foo"} + #fhir/Identifier{:id "foo"} + + #fhir/Identifier{:extension [#fhir/Extension{:url "foo" :value #fhir/string "barbar"}]} + #fhir/Identifier{:extension [#fhir/Extension{:url "foo" :value #fhir/string "barbar"}]} + + #fhir/Identifier{:value #fhir/string "foofoo"} + #fhir/Identifier{:value #fhir/string "foofoo"})) (testing "hash-into" (are [x hex] (= hex (murmur3 x)) @@ -3215,207 +3401,471 @@ #fhir/Identifier{:period #fhir/Period{}} "8a73bfa3" - #fhir/Identifier{:assigner #fhir/Reference{}} - "aa994e1e")) + #fhir/Identifier{:assigner #fhir/Reference{}} + "aa994e1e")) + + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/Identifier{} 40 + #fhir/Identifier{:id "id-130739"} 112 + #fhir/Identifier{:extension [#fhir/Extension{}]} 40 + #fhir/Identifier{:use #fhir/code "use-155144"} 40 + #fhir/Identifier{:type #fhir/CodeableConcept{}} 40 + #fhir/Identifier{:system #fhir/uri-interned "system-145514"} 40 + #fhir/Identifier{:value #fhir/string "value-145509"} 104 + #fhir/Identifier{:period #fhir/Period{}} 40 + #fhir/Identifier{:assigner #fhir/Reference{}} 40)) + + (testing "references" + (is (empty? (type/references #fhir/Identifier{})))) + + (testing "print" + (are [v s] (= s (pr-str v)) + #fhir/Identifier{} "#fhir/Identifier{}" + #fhir/Identifier{:id "212329"} "#fhir/Identifier{:id \"212329\"}"))) + +(deftest meta-test + (testing "type" + (is (= :fhir/Meta (:fhir/type #fhir/Meta{})))) + + (testing "interning" + (are [x y] (not-interned? x y) + #fhir/Meta{:id "foo"} + #fhir/Meta{:id "foo"} + + #fhir/Meta{:extension [#fhir/Extension{:url "foo" :value #fhir/string "barbar"}]} + #fhir/Meta{:extension [#fhir/Extension{:url "foo" :value #fhir/string "barbar"}]} + + #fhir/Meta{:versionId #fhir/id "foo"} + #fhir/Meta{:versionId #fhir/id "foo"} + + #fhir/Meta{:lastUpdated #fhir/instant #system/date-time "2020-01-01T00:00:00Z"} + #fhir/Meta{:lastUpdated #fhir/instant #system/date-time "2020-01-01T00:00:00Z"}) + + (are [x y] (interned? x y) + #fhir/Meta{:extension [#fhir/Extension{:url "foo" :value #fhir/code "bar"}]} + #fhir/Meta{:extension [#fhir/Extension{:url "foo" :value #fhir/code "bar"}]} + + #fhir/Meta{:source #fhir/uri "foo"} + #fhir/Meta{:source #fhir/uri "foo"} + + #fhir/Meta{:profile [#fhir/canonical "foo"]} + #fhir/Meta{:profile [#fhir/canonical "foo"]} + + #fhir/Meta{:security [#fhir/Coding{:system #fhir/uri "foo" :code #fhir/code "bar"}]} + #fhir/Meta{:security [#fhir/Coding{:system #fhir/uri "foo" :code #fhir/code "bar"}]} + + #fhir/Meta{:tag [#fhir/Coding{:system #fhir/uri "foo" :code #fhir/code "bar"}]} + #fhir/Meta{:tag [#fhir/Coding{:system #fhir/uri "foo" :code #fhir/code "bar"}]})) + + (testing "hash-into" + (are [x hex] (= hex (murmur3 x)) + #fhir/Meta{} + "cbae28fd" + + #fhir/Meta{:id "id-130825"} + "c2c18a00" + + #fhir/Meta{:extension [#fhir/Extension{}]} + "aaf41f94" + + #fhir/Meta{:versionId #fhir/id "versionId-161415"} + "9edaa9b" + + #fhir/Meta{:lastUpdated #fhir/instant #system/date-time "2020-01-01T00:00:00Z"} + "df91eaa0" + + #fhir/Meta{:source #fhir/uri "source-161629"} + "bc99bc82" + + #fhir/Meta{:profile [#fhir/canonical "profile-uri-145024"]} + "b13c3d52" + + #fhir/Meta{:security [#fhir/Coding{}]} + "9b7633bc" + + #fhir/Meta{:tag [#fhir/Coding{}]} + "96e4e336")) + + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/Meta{} 0 + #fhir/Meta{:id "id-130825"} 112 + #fhir/Meta{:extension [#fhir/Extension{}]} 0 + #fhir/Meta{:versionId #fhir/id "versionId-161415"} 112 + #fhir/Meta{:lastUpdated #fhir/instant #system/date-time "2020-01-01T00:00:00Z"} 120 + #fhir/Meta{:source #fhir/uri "source-161629"} 112 + #fhir/Meta{:profile [#fhir/canonical "profile-uri-145024"]} 0 + #fhir/Meta{:security [#fhir/Coding{}]} 0 + #fhir/Meta{:tag [#fhir/Coding{}]} 0)) + + (testing "references" + (are [x refs] (= refs (type/references x)) + #fhir/Meta{} + [] + + #fhir/Meta{:extension [#fhir/Extension{}]} + [] + + #fhir/Meta + {:extension + [#fhir/Extension{:value #fhir/Reference{:reference #fhir/string "Patient/2"}}]} + [["Patient" "2"]])) + + (testing "print" + (are [v s] (= s (pr-str v)) + #fhir/Meta{} "#fhir/Meta{}" + #fhir/Meta{:id "212329"} "#fhir/Meta{:id \"212329\"}"))) + +(deftest money-test + (testing "type" + (is (= :fhir/Money (:fhir/type #fhir/Money{})))) + + (testing "interning" + (are [x y] (not-interned? x y) + #fhir/Money{:id "foo"} + #fhir/Money{:id "foo"} + + #fhir/Money{:extension [#fhir/Extension{:url "foo" :value #fhir/string "barbar"}]} + #fhir/Money{:extension [#fhir/Extension{:url "foo" :value #fhir/string "barbar"}]} + + #fhir/Money{:value #fhir/decimal 1M} + #fhir/Money{:value #fhir/decimal 1M})) + + (testing "hash-into" + (are [x hex] (= hex (murmur3 x)) + #fhir/Money{} + "f64d19e" + + #fhir/Money{:id "id-130825"} + "506137b" + + #fhir/Money{:extension [#fhir/Extension{}]} + "cf162845" + + #fhir/Money{:value #fhir/decimal 1M} + "1c2ff9f3")) + + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/Money{} 24 + #fhir/Money{:id "id-130825"} 96 + #fhir/Money{:extension [#fhir/Extension{}]} 24 + #fhir/Money{:value #fhir/decimal 1M} 72)) + + (testing "references" + (is (empty? (type/references #fhir/Money{})))) + + (testing "print" + (are [v s] (= s (pr-str v)) + #fhir/Money{} "#fhir/Money{}" + #fhir/Money{:id "212329"} "#fhir/Money{:id \"212329\"}"))) + +(deftest parameter-definition-test + (testing "type" + (is (= :fhir/ParameterDefinition (:fhir/type #fhir/ParameterDefinition{})))) + + (testing "interning" + (are [x y] (not-interned? x y) + #fhir/ParameterDefinition{:id "foo"} + #fhir/ParameterDefinition{:id "foo"} + + #fhir/ParameterDefinition{:extension [#fhir/Extension{:url "foo" :value #fhir/string "barbar"}]} + #fhir/ParameterDefinition{:extension [#fhir/Extension{:url "foo" :value #fhir/string "barbar"}]} + + #fhir/ParameterDefinition{:type #fhir/code "Patient"} + #fhir/ParameterDefinition{:type #fhir/code "Patient"})) + + (testing "hash-into" + (are [x hex] (= hex (murmur3 x)) + #fhir/ParameterDefinition{} + "cca66a8a" + + #fhir/ParameterDefinition{:id "id-130825"} + "71cc8c06" + + #fhir/ParameterDefinition{:extension [#fhir/Extension{}]} + "b664c391" + + #fhir/ParameterDefinition{:type #fhir/code "Patient"} + "be85c928")) + + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/ParameterDefinition{} 40 + #fhir/ParameterDefinition{:id "id-130825"} 112 + #fhir/ParameterDefinition{:extension [#fhir/Extension{}]} 40 + #fhir/ParameterDefinition{:type #fhir/code "Patient"} 40)) + + (testing "references" + (is (empty? (type/references #fhir/ParameterDefinition{})))) + + (testing "print" + (are [v s] (= s (pr-str v)) + #fhir/ParameterDefinition{} "#fhir/ParameterDefinition{}" + #fhir/ParameterDefinition{:id "212329"} "#fhir/ParameterDefinition{:id \"212329\"}"))) + +(deftest period-test + (testing "type" + (is (= :fhir/Period (:fhir/type #fhir/Period{})))) + + (testing "interning" + (are [x y] (not-interned? x y) + #fhir/Period{:id "foo"} + #fhir/Period{:id "foo"} + + #fhir/Period{:extension [#fhir/Extension{:url "foo" :value #fhir/string "barbar"}]} + #fhir/Period{:extension [#fhir/Extension{:url "foo" :value #fhir/string "barbar"}]} + + #fhir/Period{:start #fhir/dateTime #system/date-time "2020"} + #fhir/Period{:start #fhir/dateTime #system/date-time "2020"}) + + (are [x y] (interned? x y) + #fhir/Period{:extension [#fhir/Extension{:url "foo" :value #fhir/code "bar"}]} + #fhir/Period{:extension [#fhir/Extension{:url "foo" :value #fhir/code "bar"}]})) + + (testing "hash-into" + (are [x hex] (= hex (murmur3 x)) + #fhir/Period{} + "e5f76205" + + #fhir/Period{:id "id-130710"} + "29c53420" + + #fhir/Period{:extension [#fhir/Extension{}]} + "92e4ba37" + + #fhir/Period{:start #fhir/dateTime #system/date-time "2020"} + "f1b7c952" + + #fhir/Period{:end #fhir/dateTime #system/date-time "2020"} + "434787dd")) + + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/Period{} 0 + #fhir/Period{:id "id-130710"} 96 + #fhir/Period{:extension [#fhir/Extension{}]} 0 + #fhir/Period{:start #fhir/dateTime #system/date-time "2020"} 56 + #fhir/Period{:end #fhir/dateTime #system/date-time "2020"} 56 + #fhir/Period{:start #fhir/dateTime #system/date-time "2020" :end #fhir/dateTime #system/date-time "2021"} 88)) (testing "references" - (is (empty? (type/references #fhir/Identifier{})))) + (is (empty? (type/references #fhir/Period{})))) (testing "print" (are [v s] (= s (pr-str v)) - #fhir/Identifier{} "#fhir/Identifier{}" - #fhir/Identifier{:id "212329"} "#fhir/Identifier{:id \"212329\"}"))) + #fhir/Period{} "#fhir/Period{}" + #fhir/Period{:id "212329"} "#fhir/Period{:id \"212329\"}"))) -(deftest human-name-test +(deftest quantity-test (testing "type" - (is (= :fhir/HumanName (type/type #fhir/HumanName{})))) + (is (= :fhir/Quantity (:fhir/type #fhir/Quantity{})))) - (testing "interned" + (testing "interning" (are [x y] (not-interned? x y) - #fhir/HumanName{:id "foo"} - #fhir/HumanName{:id "foo"} - - #fhir/HumanName{:extension [#fhir/Extension{:url "foo" :value #fhir/string "bar"}]} - #fhir/HumanName{:extension [#fhir/Extension{:url "foo" :value #fhir/string "bar"}]} + #fhir/Quantity{:id "foo"} + #fhir/Quantity{:id "foo"} - #fhir/HumanName{:text #fhir/string "foo"} - #fhir/HumanName{:text #fhir/string "foo"} + #fhir/Quantity{:extension [#fhir/Extension{:url "foo" :value #fhir/string "barbar"}]} + #fhir/Quantity{:extension [#fhir/Extension{:url "foo" :value #fhir/string "barbar"}]} - #fhir/HumanName{:family #fhir/string "foo"} - #fhir/HumanName{:family #fhir/string "foo"}) + #fhir/Quantity{:value #fhir/decimal 1M} + #fhir/Quantity{:value #fhir/decimal 1M}) (are [x y] (interned? x y) - #fhir/HumanName{:extension [#fhir/Extension{:url "foo" :value #fhir/code "bar"}]} - #fhir/HumanName{:extension [#fhir/Extension{:url "foo" :value #fhir/code "bar"}]} - - #fhir/HumanName{:use #fhir/code "foo"} - #fhir/HumanName{:use #fhir/code "foo"})) - - (testing "primary/secondary content" - (is (true? (p/-has-primary-content #fhir/HumanName{}))) - (is (false? (p/-has-secondary-content #fhir/HumanName{})))) + #fhir/Quantity{:extension [#fhir/Extension{:url "foo" :value #fhir/code "bar"}]} + #fhir/Quantity{:extension [#fhir/Extension{:url "foo" :value #fhir/code "bar"}]} - (testing "hash-into" - (are [x hex] (= hex (murmur3 x)) - #fhir/HumanName{} - "af56fc23" + #fhir/Quantity{:comparator #fhir/code "foo"} + #fhir/Quantity{:comparator #fhir/code "foo"} - #fhir/HumanName{:id "id-130739"} - "ebba60f8" + #fhir/Quantity{:unit #fhir/string "foo"} + #fhir/Quantity{:unit #fhir/string "foo"} - #fhir/HumanName{:extension [#fhir/Extension{}]} - "4947bc16" + #fhir/Quantity{:system #fhir/uri "foo"} + #fhir/Quantity{:system #fhir/uri "foo"} - #fhir/HumanName{:use #fhir/code "use-155144"} - "60b2b58c" + #fhir/Quantity{:code #fhir/code "foo"} + #fhir/Quantity{:code #fhir/code "foo"})) - #fhir/HumanName{:text #fhir/string "text-212402"} - "b9ab5f61" + (testing "hash-into" + (are [x hex] (= hex (murmur3 x)) + #fhir/Quantity{} + "1ddef3ed" - #fhir/HumanName{:family #fhir/string "family-212422"} - "915831d8" + #fhir/Quantity{:id "id-141848"} + "abb59da1" - #fhir/HumanName{:given [#fhir/string "given-212441"]} - "e26a58ee" + #fhir/Quantity{:extension [#fhir/Extension{}]} + "4f5028ac" - #fhir/HumanName{:given [#fhir/string "given-212448" #fhir/string "given-212454"]} - "b46d5198" + #fhir/Quantity{:value #fhir/decimal 1M} + "4adf97ab" - #fhir/HumanName{:prefix [#fhir/string "prefix-212514"]} - "1a411067" + #fhir/Quantity{:comparator #fhir/code "comparator-153342"} + "6339e3e8" - #fhir/HumanName{:prefix [#fhir/string "prefix-212523" #fhir/string "prefix-212525"]} - "32529f07" + #fhir/Quantity{:unit #fhir/string "unit-153351"} + "d8f92891" - #fhir/HumanName{:suffix [#fhir/string "suffix-212542"]} - "3181f719" + #fhir/Quantity{:system #fhir/uri "system-153337"} + "98f918ba" - #fhir/HumanName{:suffix [#fhir/string "suffix-212547" #fhir/string "suffix-212554"]} - "69ca06e0" + #fhir/Quantity{:code #fhir/code "code-153427"} + "7ff49528")) - #fhir/HumanName{:period #fhir/Period{}} - "18b2a823")) + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/Quantity{} 0 + #fhir/Quantity{:id "id-141848"} 104 + #fhir/Quantity{:extension [#fhir/Extension{}]} 0 + #fhir/Quantity{:value #fhir/decimal 1M} 80 + #fhir/Quantity{:comparator #fhir/code "comparator-153342"} 0 + #fhir/Quantity{:unit #fhir/string "unit-153351"} 96 + #fhir/Quantity{:unit #fhir/string-interned "unit-153351"} 0 + #fhir/Quantity{:system #fhir/uri-interned "system-153337"} 0 + #fhir/Quantity{:code #fhir/code "code-153427"} 0)) (testing "references" - (is (empty? (type/references #fhir/HumanName{})))) + (is (empty? (type/references #fhir/Quantity{})))) (testing "print" (are [v s] (= s (pr-str v)) - #fhir/HumanName{} "#fhir/HumanName{}" - #fhir/HumanName{:id "212625"} "#fhir/HumanName{:id \"212625\"}"))) + #fhir/Quantity{} "#fhir/Quantity{}" + #fhir/Quantity{:id "212329"} "#fhir/Quantity{:id \"212329\"}"))) -(deftest address-test +(deftest range-test (testing "type" - (is (= :fhir/Address (type/type #fhir/Address{})))) + (is (= :fhir/Range (:fhir/type #fhir/Range{})))) - (testing "interned" + (testing "interning" (are [x y] (not-interned? x y) - #fhir/Address{:id "foo"} - #fhir/Address{:id "foo"} + #fhir/Range{:id "foo"} + #fhir/Range{:id "foo"} - #fhir/Address{:extension [#fhir/Extension{:url "foo" :value #fhir/string "bar"}]} - #fhir/Address{:extension [#fhir/Extension{:url "foo" :value #fhir/string "bar"}]} + #fhir/Range{:extension [#fhir/Extension{:url "foo" :value #fhir/string "barbar"}]} + #fhir/Range{:extension [#fhir/Extension{:url "foo" :value #fhir/string "barbar"}]} - #fhir/Address{:text #fhir/string "foo"} - #fhir/Address{:text #fhir/string "foo"}) + #fhir/Range{:low #fhir/Quantity{:value #fhir/decimal 1M}} + #fhir/Range{:low #fhir/Quantity{:value #fhir/decimal 1M}} + + #fhir/Range{:high #fhir/Quantity{:value #fhir/decimal 1M}} + #fhir/Range{:high #fhir/Quantity{:value #fhir/decimal 1M}}) (are [x y] (interned? x y) - #fhir/Address{:extension [#fhir/Extension{:url "foo" :value #fhir/code "bar"}]} - #fhir/Address{:extension [#fhir/Extension{:url "foo" :value #fhir/code "bar"}]} + #fhir/Range{:extension [#fhir/Extension{:url "foo" :value #fhir/code "bar"}]} + #fhir/Range{:extension [#fhir/Extension{:url "foo" :value #fhir/code "bar"}]})) - #fhir/Address{:use #fhir/code "foo"} - #fhir/Address{:use #fhir/code "foo"} + (testing "hash-into" + (are [x hex] (= hex (murmur3 x)) + #fhir/Range{} + "129e217" - #fhir/Address{:type #fhir/code "foo"} - #fhir/Address{:type #fhir/code "foo"})) + #fhir/Range{:id "id-130710"} + "3bf1204b" - (testing "primary/secondary content" - (is (true? (p/-has-primary-content #fhir/Address{}))) - (is (false? (p/-has-secondary-content #fhir/Address{})))) + #fhir/Range{:extension [#fhir/Extension{}]} + "458d6390" - (testing "hash-into" - (are [x hex] (= hex (murmur3 x)) - #fhir/Address{} - "4a6b5e4f" + #fhir/Range{:low #fhir/Quantity{:value #fhir/decimal 1M}} + "2e95d572" - #fhir/Address{:id "id-130739"} - "bd6a5731" + #fhir/Range{:high #fhir/Quantity{:value #fhir/decimal 1M}} + "56047f86")) - #fhir/Address{:extension [#fhir/Extension{}]} - "2a3786e7" + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/Range{} 0 + #fhir/Range{:id "id-130710"} 96 + #fhir/Range{:extension [#fhir/Extension{}]} 0 + #fhir/Range{:low #fhir/Quantity{:value #fhir/decimal 1M}} 104 + #fhir/Range{:high #fhir/Quantity{:value #fhir/decimal 1M}} 104 + #fhir/Range{:low #fhir/Quantity{:value #fhir/decimal 1M} :high #fhir/Quantity{:value #fhir/decimal 2M}} 184)) - #fhir/Address{:use #fhir/code "use-155144"} - "b6cf1d48" + (testing "references" + (is (empty? (type/references #fhir/Range{})))) - #fhir/Address{:type #fhir/code "type-084442"} - "54c286c3" + (testing "print" + (are [v s] (= s (pr-str v)) + #fhir/Range{} "#fhir/Range{}" + #fhir/Range{:id "212329"} "#fhir/Range{:id \"212329\"}"))) - #fhir/Address{:text #fhir/string "text-212402"} - "15baed84" +(deftest ratio-test + (testing "type" + (is (= :fhir/Ratio (:fhir/type #fhir/Ratio{})))) - #fhir/Address{:line [#fhir/string "line-212441"]} - "eafac0f1" + (testing "interning" + (are [x y] (not-interned? x y) + #fhir/Ratio{:id "foo"} + #fhir/Ratio{:id "foo"} - #fhir/Address{:line [#fhir/string "line-212448" #fhir/string "line-212454"]} - "62f4cf8f" + #fhir/Ratio{:extension [#fhir/Extension{:url "foo" :value #fhir/string "barbar"}]} + #fhir/Ratio{:extension [#fhir/Extension{:url "foo" :value #fhir/string "barbar"}]} - #fhir/Address{:city #fhir/string "city-084705"} - "9765a1e9" + #fhir/Ratio{:numerator #fhir/Quantity{:value #fhir/decimal 1M}} + #fhir/Ratio{:numerator #fhir/Quantity{:value #fhir/decimal 1M}} - #fhir/Address{:district #fhir/string "district-084717"} - "9e6dc6b8" + #fhir/Ratio{:denominator #fhir/Quantity{:value #fhir/decimal 1M}} + #fhir/Ratio{:denominator #fhir/Quantity{:value #fhir/decimal 1M}}) - #fhir/Address{:state #fhir/string "state-084729"} - "17a7640f" + (are [x y] (interned? x y) + #fhir/Ratio{:extension [#fhir/Extension{:url "foo" :value #fhir/code "bar"}]} + #fhir/Ratio{:extension [#fhir/Extension{:url "foo" :value #fhir/code "bar"}]})) - #fhir/Address{:postalCode #fhir/string "postalCode-084832"} - "8880561c" + (testing "hash-into" + (are [x hex] (= hex (murmur3 x)) + #fhir/Ratio{} + "d271c07f" - #fhir/Address{:country #fhir/string "country-084845"} - "57c51a7d" + #fhir/Ratio{:id "id-130710"} + "e3c0ee3c" - #fhir/Address{:period #fhir/Period{}} - "fb17905a")) + #fhir/Ratio{:extension [#fhir/Extension{}]} + "23473d24" + + #fhir/Ratio{:numerator #fhir/Quantity{:value #fhir/decimal 1M}} + "fbf83a67" + + #fhir/Ratio{:denominator #fhir/Quantity{:value #fhir/decimal 1M}} + "7f2075fb")) + + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/Ratio{} 0 + #fhir/Ratio{:id "id-130710"} 96 + #fhir/Ratio{:extension [#fhir/Extension{}]} 0 + #fhir/Ratio{:numerator #fhir/Quantity{:value #fhir/decimal 1M}} 104 + #fhir/Ratio{:denominator #fhir/Quantity{:value #fhir/decimal 1M}} 104)) (testing "references" - (is (empty? (type/references #fhir/Address{})))) + (is (empty? (type/references #fhir/Ratio{})))) (testing "print" (are [v s] (= s (pr-str v)) - #fhir/Address{} "#fhir/Address{}" - #fhir/Address{:id "084856"} "#fhir/Address{:id \"084856\"}"))) + #fhir/Ratio{} "#fhir/Ratio{}" + #fhir/Ratio{:id "212329"} "#fhir/Ratio{:id \"212329\"}"))) (deftest reference-test (testing "type" - (is (= :fhir/Reference (type/type #fhir/Reference{})))) + (is (= :fhir/Reference (:fhir/type #fhir/Reference{})))) - (testing "interned" + (testing "interning" (are [x y] (not-interned? x y) #fhir/Reference{:id "foo"} #fhir/Reference{:id "foo"} - #fhir/Reference{:extension [#fhir/Extension{:url "foo" :value #fhir/string "bar"}]} - #fhir/Reference{:extension [#fhir/Extension{:url "foo" :value #fhir/string "bar"}]} - - #fhir/Reference{:reference #fhir/string "foo"} - #fhir/Reference{:reference #fhir/string "foo"} - - #fhir/Reference{:identifier #fhir/Identifier{:value #fhir/string "foo"}} - #fhir/Reference{:identifier #fhir/Identifier{:value #fhir/string "foo"}} + #fhir/Reference{:extension [#fhir/Extension{:url "foo" :value #fhir/string "barbar"}]} + #fhir/Reference{:extension [#fhir/Extension{:url "foo" :value #fhir/string "barbar"}]} - #fhir/Reference{:display #fhir/string "foo"} - #fhir/Reference{:display #fhir/string "foo"}) + #fhir/Reference{:reference #fhir/string "foofoo"} + #fhir/Reference{:reference #fhir/string "foofoo"} - (are [x y] (interned? x y) - #fhir/Reference{:extension [#fhir/Extension{:url "foo" :value #fhir/code "bar"}]} - #fhir/Reference{:extension [#fhir/Extension{:url "foo" :value #fhir/code "bar"}]} - - #fhir/Reference{:type #fhir/uri "foo"} - #fhir/Reference{:type #fhir/uri "foo"})) + #fhir/Reference{:identifier #fhir/Identifier{:value #fhir/string "foofoo"}} + #fhir/Reference{:identifier #fhir/Identifier{:value #fhir/string "foofoo"}} - (testing "primary/secondary content" - (is (true? (p/-has-primary-content #fhir/Reference{}))) - (is (false? (p/-has-secondary-content #fhir/Reference{})))) + #fhir/Reference{:display #fhir/string "foofoo"} + #fhir/Reference{:display #fhir/string "foofoo"})) (testing "hash-into" (are [x hex] (= hex (murmur3 x)) @@ -3440,6 +3890,16 @@ #fhir/Reference{:display #fhir/string "display-161314"} "543cf75f")) + (testing "mem-size" + (are [s mem-size] (= mem-size (Base/memSize s)) + #fhir/Reference{} 32 + #fhir/Reference{:id "id-130802"} 104 + #fhir/Reference{:extension [#fhir/Extension{}]} 32 + #fhir/Reference{:reference #fhir/string "Patient/0"} 96 + #fhir/Reference{:type #fhir/uri-interned "type-161222"} 32 + #fhir/Reference{:identifier #fhir/Identifier{}} 72 + #fhir/Reference{:display #fhir/string "display-161314"} 104)) + (testing "references" (are [x refs] (= refs (type/references x)) #fhir/Reference{} @@ -3468,8 +3928,8 @@ {:extension [#fhir/Extension {:value #fhir/Reference - {:reference #fhir/string "Patient/1"}}] - :reference #fhir/string "Patient/0"} + {:reference #fhir/string "Patient/0"}}] + :reference #fhir/string "Patient/1"} [["Patient" "0"] ["Patient" "1"]] #fhir/Reference @@ -3486,138 +3946,65 @@ #fhir/Reference{} "#fhir/Reference{}" #fhir/Reference{:id "212329"} "#fhir/Reference{:id \"212329\"}"))) -(deftest meta-test +(deftest related-artifact-test (testing "type" - (is (= :fhir/Meta (type/type #fhir/Meta{})))) - - (testing "interned" - (are [x y] (not-interned? x y) - #fhir/Meta{:id "foo"} - #fhir/Meta{:id "foo"} - - #fhir/Meta{:extension [#fhir/Extension{:url "foo" :value #fhir/string "bar"}]} - #fhir/Meta{:extension [#fhir/Extension{:url "foo" :value #fhir/string "bar"}]} - - #fhir/Meta{:versionId #fhir/id "foo"} - #fhir/Meta{:versionId #fhir/id "foo"} - - #fhir/Meta{:lastUpdated #fhir/instant "2020-01-01T00:00:00Z"} - #fhir/Meta{:lastUpdated #fhir/instant "2020-01-01T00:00:00Z"}) - - (are [x y] (interned? x y) - #fhir/Meta{:extension [#fhir/Extension{:url "foo" :value #fhir/code "bar"}]} - #fhir/Meta{:extension [#fhir/Extension{:url "foo" :value #fhir/code "bar"}]} - - #fhir/Meta{:source #fhir/uri "foo"} - #fhir/Meta{:source #fhir/uri "foo"} - - #fhir/Meta{:profile [#fhir/canonical "foo"]} - #fhir/Meta{:profile [#fhir/canonical "foo"]} - - #fhir/Meta{:security [#fhir/Coding{:system #fhir/uri "foo" :code #fhir/code "bar"}]} - #fhir/Meta{:security [#fhir/Coding{:system #fhir/uri "foo" :code #fhir/code "bar"}]} - - #fhir/Meta{:tag [#fhir/Coding{:system #fhir/uri "foo" :code #fhir/code "bar"}]} - #fhir/Meta{:tag [#fhir/Coding{:system #fhir/uri "foo" :code #fhir/code "bar"}]})) - - (testing "primary/secondary content" - (is (true? (p/-has-primary-content #fhir/Meta{}))) - (is (false? (p/-has-secondary-content #fhir/Meta{})))) - - (testing "hash-into" - (are [x hex] (= hex (murmur3 x)) - #fhir/Meta{} - "cbae28fd" - - #fhir/Meta{:id "id-130825"} - "c2c18a00" - - #fhir/Meta{:extension [#fhir/Extension{}]} - "aaf41f94" - - #fhir/Meta{:versionId #fhir/id "versionId-161415"} - "9edaa9b" - - (type/meta {:lastUpdated Instant/EPOCH}) - "38b8dfe3" - - #fhir/Meta{:source #fhir/uri "source-161629"} - "bc99bc82" - - #fhir/Meta{:profile [#fhir/canonical "profile-uri-145024"]} - "b13c3d52" - - #fhir/Meta{:security [#fhir/Coding{}]} - "9b7633bc" - - #fhir/Meta{:tag [#fhir/Coding{}]} - "96e4e336")) - - (testing "references" - (are [x refs] (= refs (type/references x)) - #fhir/Meta{} - [] - - #fhir/Meta{:extension [#fhir/Extension{}]} - [] - - #fhir/Meta - {:extension - [#fhir/Extension{:value #fhir/Reference{:reference #fhir/string "Patient/2"}}]} - [["Patient" "2"]])) + (is (= :fhir/RelatedArtifact (:fhir/type #fhir/RelatedArtifact{})))) (testing "print" (are [v s] (= s (pr-str v)) - #fhir/Meta{} "#fhir/Meta{}" - #fhir/Meta{:id "212329"} "#fhir/Meta{:id \"212329\"}"))) + #fhir/RelatedArtifact{} "#fhir/RelatedArtifact{}" + #fhir/RelatedArtifact{:id "212329"} "#fhir/RelatedArtifact{:id \"212329\"}"))) -(deftest bundle-entry-search-test +(deftest sampled-data-test (testing "type" - (is (= :fhir.Bundle.entry/search (type/type #fhir/BundleEntrySearch{})))) - - (testing "interned" - (are [x y] (not-interned? x y) - #fhir/BundleEntrySearch{:id "foo"} - #fhir/BundleEntrySearch{:id "foo"} - - #fhir/BundleEntrySearch{:extension [#fhir/Extension{:url "foo" :value #fhir/string "bar"}]} - #fhir/BundleEntrySearch{:extension [#fhir/Extension{:url "foo" :value #fhir/string "bar"}]} + (is (= :fhir/SampledData (:fhir/type #fhir/SampledData{})))) - #fhir/BundleEntrySearch{:score #fhir/decimal 1M} - #fhir/BundleEntrySearch{:score #fhir/decimal 1M}) + (testing "print" + (are [v s] (= s (pr-str v)) + #fhir/SampledData{} "#fhir/SampledData{}" + #fhir/SampledData{:id "212329"} "#fhir/SampledData{:id \"212329\"}"))) - (are [x y] (interned? x y) - #fhir/BundleEntrySearch{:extension [#fhir/Extension{:url "foo" :value #fhir/code "bar"}]} - #fhir/BundleEntrySearch{:extension [#fhir/Extension{:url "foo" :value #fhir/code "bar"}]} +(deftest signature-test + (testing "type" + (is (= :fhir/Signature (:fhir/type #fhir/Signature{})))) - #fhir/BundleEntrySearch{:mode #fhir/code "match"} - #fhir/BundleEntrySearch{:mode #fhir/code "match"})) + (testing "print" + (are [v s] (= s (pr-str v)) + #fhir/Signature{} "#fhir/Signature{}" + #fhir/Signature{:id "212329"} "#fhir/Signature{:id \"212329\"}"))) - (testing "primary/secondary content" - (is (true? (p/-has-primary-content #fhir/BundleEntrySearch{}))) - (is (false? (p/-has-secondary-content #fhir/BundleEntrySearch{})))) +(deftest timing-test + (testing "type" + (is (= :fhir/Timing (:fhir/type #fhir/Timing{})))) - (testing "hash-into" - (are [x hex] (= hex (murmur3 x)) - #fhir/BundleEntrySearch{} - "f945531f" + (testing "print" + (are [v s] (= s (pr-str v)) + #fhir/Timing{} "#fhir/Timing{}" + #fhir/Timing{:id "212329"} "#fhir/Timing{:id \"212329\"}"))) - #fhir/BundleEntrySearch{:id "id-130825"} - "6b1b9201" +(deftest timing-repeat-test + (testing "type" + (is (= :fhir.Timing/repeat (:fhir/type #fhir.Timing/repeat{})))) - #fhir/BundleEntrySearch{:extension [#fhir/Extension{}]} - "f24daf4f" + (testing "print" + (are [v s] (= s (pr-str v)) + #fhir.Timing/repeat{} "#fhir.Timing/repeat{}" + #fhir.Timing/repeat{:id "212329"} "#fhir.Timing/repeat{:id \"212329\"}"))) - #fhir/BundleEntrySearch{:mode #fhir/code "match"} - "5912b48c" +(deftest trigger-definition-test + (testing "type" + (is (= :fhir/TriggerDefinition (:fhir/type #fhir/TriggerDefinition{})))) - #fhir/BundleEntrySearch{:score #fhir/decimal 1M} - "2b2509dc")) + (testing "print" + (are [v s] (= s (pr-str v)) + #fhir/TriggerDefinition{} "#fhir/TriggerDefinition{}" + #fhir/TriggerDefinition{:id "212329"} "#fhir/TriggerDefinition{:id \"212329\"}"))) - (testing "references" - (is (empty? (type/references #fhir/BundleEntrySearch{})))) +(deftest usage-context-test + (testing "type" + (is (= :fhir/UsageContext (:fhir/type #fhir/UsageContext{})))) (testing "print" (are [v s] (= s (pr-str v)) - #fhir/BundleEntrySearch{} "#fhir/BundleEntrySearch{}" - #fhir/BundleEntrySearch{:id "212329"} "#fhir/BundleEntrySearch{:id \"212329\"}"))) + #fhir/UsageContext{} "#fhir/UsageContext{}" + #fhir/UsageContext{:id "212329"} "#fhir/UsageContext{:id \"212329\"}"))) diff --git a/modules/fhir-structure/test/blaze/fhir/spec_test.clj b/modules/fhir-structure/test/blaze/fhir/spec_test.clj index 2d56aef61..aa20363c9 100644 --- a/modules/fhir-structure/test/blaze/fhir/spec_test.clj +++ b/modules/fhir-structure/test/blaze/fhir/spec_test.clj @@ -9,6 +9,7 @@ [blaze.fhir.spec-spec] [blaze.fhir.spec.generators :as fg] [blaze.fhir.spec.impl.xml-spec] + [blaze.fhir.spec.memory :as mem] [blaze.fhir.spec.spec] [blaze.fhir.spec.type :as type] [blaze.fhir.test-util :refer [structure-definition-repo]] @@ -29,8 +30,10 @@ [jsonista.core :as j] [juxt.iota :refer [given]]) (:import + [blaze.fhir.spec.type Base] + [blaze.fhir.spec.type.system DateTimes Times] [com.fasterxml.jackson.dataformat.cbor CBORFactory] - [java.time Instant])) + [com.google.common.hash Hashing])) (xml-name/alias-uri 'f "http://hl7.org/fhir") (xml-name/alias-uri 'xhtml "http://www.w3.org/1999/xhtml") @@ -98,11 +101,8 @@ ([type data] (fhir-spec/parse-json parsing-context type (j/write-value-as-string data)))) -(defn- write-parse-cbor - ([{:fhir/keys [type] :as resource}] - (parse-cbor (name type) (write-cbor resource))) - ([type data] - (fhir-spec/parse-cbor rs-context type (j/write-value-as-bytes data cbor-object-mapper)))) +(defn- write-parse-cbor [type data] + (parse-cbor type (j/write-value-as-bytes data cbor-object-mapper))) (deftest resource-test (testing "valid" @@ -119,27 +119,11 @@ {:system #fhir/uri "system-204435" :code #fhir/code "code-204441"}]} :subject #fhir/Reference{:reference #fhir/string "Patient/id-145552"} - :onset #fhir/dateTime "2020-01-30"} + :onset #fhir/dateTime #system/date-time "2020-01-30"} {:fhir/type :fhir/Patient :id "0" :birthDate #fhir/date{:extension [#fhir/Extension{:url "foo" :value #fhir/code "bar"}]}}))) -(deftest fhir-type-test - (testing "Patient" - (is (= :fhir/Patient - (fhir-spec/fhir-type - (write-parse-json {:resourceType "Patient"})))))) - -(deftest primitive-test - (are [spec] (fhir-spec/primitive? spec) - :fhir/id) - - (are [spec] (not (fhir-spec/primitive? spec)) - :fhir/Patient - nil - "foo" - :Patient)) - (deftest primitive-val-test (are [x] (fhir-spec/primitive-val? x) #fhir/string "foo" @@ -148,7 +132,7 @@ (are [x] (not (fhir-spec/primitive-val? x)) #fhir/Coding{} - {})) + {:fhir/type :fhir.CodeSystem/concept})) (deftest resource-id-test (are [s] (s/valid? :blaze.resource/id s) @@ -170,7 +154,8 @@ (testing "true" (are [type] (true? (fhir-spec/type-exists? type)) "Patient" - "Observation")) + "Observation" + "Coding")) (testing "false" (are [type] (false? (fhir-spec/type-exists? type)) @@ -193,6 +178,24 @@ (is (= result "name[0].text"))))) (deftest parse-json-test + (testing "fails on missing type" + (given (parse-json "") + ::anom/category := ::anom/incorrect + ::anom/message := "Invalid JSON representation of a resource. Error on token null. Expected type is `Resource`.") + + (given (parse-json "{}") + ::anom/category := ::anom/incorrect + ::anom/message := "Invalid JSON representation of a resource. Missing property `resourceType`.")) + + (testing "fails on unsupported type" + (given (parse-json "Foo" "") + ::anom/category := ::anom/unsupported + ::anom/message := "Unsupported type `Foo`.") + + (given (write-parse-json {:resourceType "Foo"}) + ::anom/category := ::anom/unsupported + ::anom/message := "Invalid JSON representation of a resource. Unsupported type `Foo`.")) + (testing "fails on unexpected end-of-input" (given (parse-json "{") ::anom/category := ::anom/incorrect @@ -429,9 +432,9 @@ [{:extension [{:url "foo"} {:url 0}]} 1]]] (given (write-parse-json {:resourceType "Patient" :_gender value}) ::anom/category := ::anom/incorrect - ::anom/message := "Invalid JSON representation of a resource. Error on integer value 0. Expected type is `uri`." + ::anom/message := "Invalid JSON representation of a resource. Error on integer value 0. Expected type is `string`." [:fhir/issues 0 :fhir.issues/code] := "invariant" - [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on integer value 0. Expected type is `uri`." + [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on integer value 0. Expected type is `string`." [:fhir/issues 0 :fhir.issues/expression] := (format "Patient.gender.extension[%d].url" idx)))) (testing "unknown property Patient.gender.extension.name-163857" @@ -457,9 +460,9 @@ (testing "invalid string" (given (write-parse-json {:resourceType "Patient" :birthDate "a"}) ::anom/category := ::anom/incorrect - ::anom/message := "Invalid JSON representation of a resource. Error on value `a`. Expected type is `date`." + ::anom/message := "Invalid JSON representation of a resource. Error on value `a`. Expected type is `date`. Can't parse `a` as System.Date because it doesn't has the right length." [:fhir/issues 0 :fhir.issues/code] := "invariant" - [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on value `a`. Expected type is `date`." + [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on value `a`. Expected type is `date`. Can't parse `a` as System.Date because it doesn't has the right length." [:fhir/issues 0 :fhir.issues/expression] := "Patient.birthDate"))) (testing "invalid Patient.deceasedBoolean" @@ -548,8 +551,8 @@ (testing "empty patient resource" (testing "gets type annotated" - (is (= :fhir/Patient - (fhir-spec/fhir-type (write-parse-json {:resourceType "Patient"}))))) + (given (write-parse-json {:resourceType "Patient"}) + :fhir/type := :fhir/Patient)) (testing "stays the same" (is (= {:fhir/type :fhir/Patient} @@ -560,7 +563,7 @@ (write-parse-json {:resourceType "Patient" :deceasedBoolean true})))) (testing "deceasedDateTime on Patient will be remapped" - (is (= {:fhir/type :fhir/Patient :deceased #fhir/dateTime "2020"} + (is (= {:fhir/type :fhir/Patient :deceased #fhir/dateTime #system/date-time "2020"} (write-parse-json {:resourceType "Patient" :deceasedDateTime "2020"})))) (testing "multipleBirthInteger on Patient will be remapped" @@ -594,7 +597,7 @@ (testing "Observation with valueTime" (is (= {:fhir/type :fhir/Observation - :value #fhir/time "16:26:42"} + :value #fhir/time #system/time "16:26:42"} (write-parse-json {:resourceType "Observation" :valueTime "16:26:42"})))) @@ -648,14 +651,16 @@ (deftest write-json-test (testing "without fhir type" (testing "at the root" - (is (= 0 (alength ^bytes (write-json {}))))) + (given (st/with-instrument-disabled (write-json {})) + ::anom/category := ::anom/incorrect + ::anom/message := "Missing type.")) (testing "backbone elements don't need types" (testing "cardinality single" (testing "no keys" (are [resource json] (= json (write-read-json resource)) - {:fhir/type :fhir.Bundle/entry :search {}} - {:search {}})) + {:fhir/type :fhir.Bundle/entry :response {}} + {:response {}})) (testing "one unknown key" (are [resource json] (= json (write-read-json resource)) @@ -679,10 +684,17 @@ (testing "elements don't need types" (testing "DataRequirement.codeFilter" (are [resource json] (= json (write-read-json resource)) - {:fhir/type :fhir/DataRequirement - :codeFilter [{:searchParam #fhir/string "bar" :path #fhir/string "foo"}]} + #fhir/DataRequirement + {:codeFilter + [#fhir.DataRequirement/codeFilter + {:searchParam #fhir/string "bar" :path #fhir/string "foo"}]} {:codeFilter [{:path "foo" :searchParam "bar"}]})))) + (testing "with unsupported fhir type" + (given (write-json {:fhir/type :fhir/Foo}) + ::anom/category := ::anom/unsupported + ::anom/message := "Unsupported type `Foo`.")) + (testing "Patient with deceasedBoolean" (are [resource json] (= json (write-read-json resource)) {:fhir/type :fhir/Patient :deceased #fhir/boolean true} @@ -690,7 +702,7 @@ (testing "Patient with deceasedDateTime" (are [resource json] (= json (write-read-json resource)) - {:fhir/type :fhir/Patient :deceased #fhir/dateTime "2020"} + {:fhir/type :fhir/Patient :deceased #fhir/dateTime #system/date-time "2020"} {:resourceType "Patient" :deceasedDateTime "2020"})) (testing "Patient with multipleBirthBoolean" @@ -745,6 +757,11 @@ :code "kg/m2"}}))) (deftest parse-cbor-test + (testing "fails on unsupported type" + (given (parse-cbor "Foo" (byte-array 0)) + ::anom/category := ::anom/unsupported + ::anom/message := "Unsupported type `Foo`.")) + (testing "fails on empty input" (given (parse-cbor "Patient" (byte-array 0)) ::anom/category := ::anom/incorrect @@ -783,58 +800,72 @@ {:fhir/type :fhir/Patient}))))))) (deftest write-cbor-test - (testing "Patient with deceasedBoolean" - (are [resource] (= resource (write-parse-cbor resource)) - {:fhir/type :fhir/Patient :deceased #fhir/boolean true})) - - (testing "Patient with deceasedDateTime" - (are [resource] (= resource (write-parse-cbor resource)) - {:fhir/type :fhir/Patient :deceased #fhir/dateTime "2020"})) - - (testing "Patient with multipleBirthBoolean" - (are [resource] (= resource (write-parse-cbor resource)) - {:fhir/type :fhir/Patient :multipleBirth #fhir/boolean false})) - - (testing "Patient with multipleBirthInteger" - (are [resource] (= resource (write-parse-cbor resource)) - {:fhir/type :fhir/Patient :multipleBirth #fhir/integer 2})) - - (testing "Bundle with Patient" - (are [resource] (= resource (write-parse-cbor resource)) - {:fhir/type :fhir/Bundle - :entry - [{:fhir/type :fhir.Bundle/entry - :resource {:fhir/type :fhir/Patient :id "0"}}]})) - - (testing "Observation with code" - (are [resource] (= resource (write-parse-cbor resource)) - {:fhir/type :fhir/Observation - :code - #fhir/CodeableConcept - {:coding - [#fhir/Coding - {:system #fhir/uri "http://loinc.org" - :code #fhir/code "39156-5"}]}})) - - (testing "Observation with valueQuantity" - (are [resource] (= resource (write-parse-cbor resource)) - {:fhir/type :fhir/Observation - :value - #fhir/Quantity - {:value #fhir/decimal 36.6M - :unit #fhir/string "kg/m^2" - :system #fhir/uri "http://unitsofmeasure.org" - :code #fhir/code "kg/m2"}})) - - (testing "Observation with valueTime" - (are [resource] (= resource (write-parse-cbor resource)) - {:fhir/type :fhir/Observation - :value #fhir/time "00:00"})) - - (testing "Observation with valueTime with id" - (are [resource] (= resource (write-parse-cbor resource)) - {:fhir/type :fhir/Observation - :value #fhir/time{:id "foo" :value #system/time"00:00"}}))) + (testing "without resource type" + (given (st/with-instrument-disabled (write-cbor {})) + ::anom/category := ::anom/incorrect + ::anom/message := "Missing type.")) + + (testing "with unsupported type" + (given (write-cbor {:fhir/type :fhir/Foo}) + ::anom/category := ::anom/unsupported + ::anom/message := "Unsupported type `Foo`.")) + + (let [write-parse-cbor + (fn [{:fhir/keys [type] :as resource}] + (parse-cbor (name type) (write-cbor resource)))] + + (testing "Patient with deceasedBoolean" + (are [resource] (= resource (write-parse-cbor resource)) + {:fhir/type :fhir/Patient :deceased #fhir/boolean true})) + + (testing "Patient with deceasedDateTime" + (are [resource] (= resource (write-parse-cbor resource)) + {:fhir/type :fhir/Patient :deceased #fhir/dateTime #system/date-time "2020"})) + + (testing "Patient with multipleBirthBoolean" + (are [resource] (= resource (write-parse-cbor resource)) + {:fhir/type :fhir/Patient :multipleBirth #fhir/boolean false})) + + (testing "Patient with multipleBirthInteger" + (are [resource] (= resource (write-parse-cbor resource)) + {:fhir/type :fhir/Patient :multipleBirth #fhir/integer 2})) + + (testing "Bundle with Patient" + (are [resource] (= resource (write-parse-cbor resource)) + {:fhir/type :fhir/Bundle + :entry + [{:fhir/type :fhir.Bundle/entry + :resource {:fhir/type :fhir/Patient :id "0"}}]})) + + (testing "Observation with code" + (are [resource] (= resource (write-parse-cbor resource)) + {:fhir/type :fhir/Observation + :code + #fhir/CodeableConcept + {:coding + [#fhir/Coding + {:system #fhir/uri "http://loinc.org" + :code #fhir/code "39156-5"}]}})) + + (testing "Observation with valueQuantity" + (are [resource] (= resource (write-parse-cbor resource)) + {:fhir/type :fhir/Observation + :value + #fhir/Quantity + {:value #fhir/decimal 36.6M + :unit #fhir/string "kg/m^2" + :system #fhir/uri "http://unitsofmeasure.org" + :code #fhir/code "kg/m2"}})) + + (testing "Observation with valueTime" + (are [resource] (= resource (write-parse-cbor resource)) + {:fhir/type :fhir/Observation + :value #fhir/time #system/time "00:00"})) + + (testing "Observation with valueTime with id" + (are [resource] (= resource (write-parse-cbor resource)) + {:fhir/type :fhir/Observation + :value #fhir/time{:id "foo" :value #system/time"00:00"}})))) (defn- conform-xml [sexp] (fhir-spec/conform-xml (prxml/sexp-as-element sexp))) @@ -881,8 +912,8 @@ (testing "empty patient resource" (testing "gets type annotated" - (is (= :fhir/Patient - (fhir-spec/fhir-type (conform-xml [::f/Patient]))))) + (given (conform-xml [::f/Patient]) + :fhir/type := :fhir/Patient)) (testing "stays the same" (is (= {:fhir/type :fhir/Patient} @@ -897,7 +928,7 @@ (conform-xml [::f/Patient [::f/deceasedBoolean {:value "true"}]])))) (testing "deceasedDateTime on Patient will be remapped" - (is (= {:fhir/type :fhir/Patient :deceased #fhir/dateTime "2020"} + (is (= {:fhir/type :fhir/Patient :deceased #fhir/dateTime #system/date-time "2020"} (conform-xml [::f/Patient [::f/deceasedDateTime {:value "2020"}]])))) (testing "multipleBirthInteger on Patient will be remapped" @@ -1124,6 +1155,10 @@ [(type/extension {:url extension-url})] :value value}))))))))) +(deftest ^:mem-size fhir-boolean-mem-size-test + (are [value size] (= size (Base/memSize value) (mem/total-size value)) + #fhir/boolean{:id "id-205204"} 88)) + (deftest fhir-integer-test (testing "parsing" (testing "XML" @@ -1247,6 +1282,20 @@ [(type/extension {:url extension-url})] :value value}))))))))) +(deftest ^:mem-size fhir-string-mem-size-test + (satisfies-prop 20 + (prop/for-all [string (fg/string :value fg/large-string-value)] + (pos? (Base/memSize string)))) + + (testing "interning" + (are [x y] (zero? (mem/total-size* x y)) + #fhir/string "1234" + #fhir/string "1234")) + + (testing "examples" + (are [x size] (= (mem/total-size x) (Base/memSize x) size) + #fhir/string "string-131125" 72))) + (deftest fhir-decimal-test (testing "parsing" (testing "XML" @@ -1456,6 +1505,12 @@ [(type/extension {:url extension-url})] :value value}))))))))) +(deftest ^:mem-size fhir-canonical-mem-size-test + (satisfies-prop 10 + (prop/for-all [value (fg/canonical :id (gen/return nil) :extension (gen/return nil))] + (and (zero? (Base/memSize value)) + (= (mem/total-size value) (mem/total-size value value)))))) + (deftest fhir-base64Binary-test (testing "parsing" (testing "XML" @@ -1514,7 +1569,8 @@ (testing "valid" (satisfies-prop 500 (prop/for-all [value fg/instant-value] - (= (type/instant value) (s2/conform :fhir.xml/instant (sexp-value value))))) + (= (type/instant value) + (s2/conform :fhir.xml/instant (sexp-value (DateTimes/toString value)))))) (testing "with extension" (satisfies-prop 100 @@ -1527,7 +1583,7 @@ :value value}) (s2/conform :fhir.xml/instant (sexp - [nil (cond-> {} id (assoc :id id) value (assoc :value value)) + [nil (cond-> {} id (assoc :id id) value (assoc :value (DateTimes/toString value))) [::f/extension {:url extension-url}]])))))) (testing "invalid" @@ -1540,7 +1596,8 @@ (testing "value only" (satisfies-prop 500 (prop/for-all [value fg/instant-value] - (= (sexp-value value) (s2/unform :fhir.xml/instant (type/instant value))))) + (= (sexp-value (DateTimes/toString value)) + (s2/unform :fhir.xml/instant (type/instant value))))) (testing "emit" (satisfies-prop 500 @@ -1553,7 +1610,7 @@ extension-url fg/uri-value value (gen/one-of [fg/instant-value (gen/return nil)])] (= (sexp - [nil (cond-> {} id (assoc :id id) value (assoc :value value)) + [nil (cond-> {} id (assoc :id id) value (assoc :value (DateTimes/toString value))) [::f/extension {:url extension-url}]]) (s2/unform :fhir.xml/instant (type/instant {:id id @@ -1561,6 +1618,11 @@ [(type/extension {:url extension-url})] :value value}))))))))) +(deftest ^:mem-size fhir-instant-mem-size-test + (satisfies-prop 100 + (prop/for-all [value (fg/instant :value fg/instant-value)] + (pos? (Base/memSize value))))) + (deftest fhir-date-test (testing "valid" (are [x] (s2/valid? :fhir/date x) @@ -1571,7 +1633,7 @@ (testing "valid" (satisfies-prop 500 (prop/for-all [value fg/date-value] - (= (type/date value) (s2/conform :fhir.xml/date (sexp-value value))))) + (= (type/date value) (s2/conform :fhir.xml/date (sexp-value (str value)))))) (testing "with extension" (satisfies-prop 100 @@ -1585,20 +1647,15 @@ :value value}) (s2/conform :fhir.xml/date (sexp - [nil (cond-> {} id (assoc :id id) value (assoc :value value)) - character-content [::f/extension {:url extension-url}]]))))))) - - (testing "invalid" - (are [v] (s2/invalid? (s2/conform :fhir.xml/date (sexp-value v))) - "2019-13" - "2019-02-29")))) + [nil (cond-> {} id (assoc :id id) value (assoc :value (str value))) + character-content [::f/extension {:url extension-url}]]))))))))) (testing "writing" (testing "XML" (testing "value only" (satisfies-prop 500 (prop/for-all [value fg/date-value] - (= (sexp-value value) (s2/unform :fhir.xml/date (type/date value))))) + (= (sexp-value (str value)) (s2/unform :fhir.xml/date (type/date value))))) (testing "emit" (satisfies-prop 500 @@ -1611,7 +1668,7 @@ extension-url fg/uri-value value (gen/one-of [fg/date-value (gen/return nil)])] (= (sexp - [nil (cond-> {} id (assoc :id id) value (assoc :value value)) + [nil (cond-> {} id (assoc :id id) value (assoc :value (str value))) [::f/extension {:url extension-url}]]) (s2/unform :fhir.xml/date (type/date {:id id @@ -1619,13 +1676,18 @@ [(type/extension {:url extension-url})] :value value}))))))))) +(deftest ^:mem-size fhir-date-mem-size-test + (satisfies-prop 100 + (prop/for-all [value (fg/date :value fg/date-value)] + (pos? (Base/memSize value))))) + (deftest fhir-dateTime-test (testing "parsing" (testing "XML" (testing "valid" (satisfies-prop 500 (prop/for-all [value (fg/dateTime-value)] - (= (type/dateTime value) (s2/conform :fhir.xml/dateTime (sexp-value value))))) + (= (type/dateTime value) (s2/conform :fhir.xml/dateTime (sexp-value (DateTimes/toString value)))))) (testing "with extension" (satisfies-prop 100 @@ -1638,20 +1700,15 @@ :value value}) (s2/conform :fhir.xml/dateTime (sexp - [nil (cond-> {} id (assoc :id id) value (assoc :value value)) - [::f/extension {:url extension-url}]])))))) - - (testing "invalid" - (are [v] (s2/invalid? (s2/conform :fhir.xml/dateTime (sexp-value v))) - "2019-13" - "2019-02-29"))))) + [nil (cond-> {} id (assoc :id id) value (assoc :value (DateTimes/toString value))) + [::f/extension {:url extension-url}]]))))))))) (testing "writing" (testing "XML" (testing "value only" (satisfies-prop 500 (prop/for-all [value (fg/dateTime-value)] - (= (sexp-value value) (s2/unform :fhir.xml/dateTime (type/dateTime value))))) + (= (sexp-value (DateTimes/toString value)) (s2/unform :fhir.xml/dateTime (type/dateTime value))))) (testing "emit" (satisfies-prop 500 @@ -1664,7 +1721,7 @@ extension-url fg/uri-value value (gen/one-of [(fg/dateTime-value) (gen/return nil)])] (= (sexp - [nil (cond-> {} id (assoc :id id) value (assoc :value value)) + [nil (cond-> {} id (assoc :id id) value (assoc :value (DateTimes/toString value))) [::f/extension {:url extension-url}]]) (s2/unform :fhir.xml/dateTime (type/dateTime {:id id @@ -1678,7 +1735,7 @@ (testing "valid" (satisfies-prop 500 (prop/for-all [value fg/time-value] - (= (type/time value) (s2/conform :fhir.xml/time (sexp-value value))))) + (= (type/time value) (s2/conform :fhir.xml/time (sexp-value (Times/toString value)))))) (testing "with extension" (satisfies-prop 100 @@ -1691,7 +1748,7 @@ :value value}) (s2/conform :fhir.xml/time (sexp - [nil (cond-> {} id (assoc :id id) value (assoc :value value)) + [nil (cond-> {} id (assoc :id id) value (assoc :value (Times/toString value))) [::f/extension {:url extension-url}]])))))) (testing "invalid" @@ -1704,7 +1761,7 @@ (testing "value only" (satisfies-prop 500 (prop/for-all [value fg/time-value] - (= (sexp-value value) (s2/unform :fhir.xml/time (type/time value))))) + (= (sexp-value (Times/toString value)) (s2/unform :fhir.xml/time (type/time value))))) (testing "emit" (satisfies-prop 100 @@ -1717,7 +1774,7 @@ extension-url fg/uri-value value (gen/one-of [fg/time-value (gen/return nil)])] (= (sexp - [nil (cond-> {} id (assoc :id id) value (assoc :value value)) + [nil (cond-> {} id (assoc :id id) value (assoc :value (Times/toString value))) [::f/extension {:url extension-url}]]) (s2/unform :fhir.xml/time (type/time {:id id @@ -2113,863 +2170,2760 @@ ;; ---- Complex Types --------------------------------------------------------- -(deftest attachment-test +(defmacro mem-size-test [type & body] + `(are [x# size#] (= (Base/memSize (write-parse-json ~type x#)) + (mem/total-size* (write-parse-json ~type x#) + (write-parse-json ~type x#)) + size#) + ~@body)) + +(deftest address-test (testing "FHIR spec" (testing "valid" - (satisfies-prop 100 - (prop/for-all [x (fg/attachment)] - (s2/valid? :fhir/Attachment x)))) - - (testing "invalid" - (are [x] (not (s2/valid? :fhir/Attachment x)) - #fhir/Attachment{:contentType "foo"}))) + (satisfies-prop 20 + (prop/for-all [x (fg/address)] + (s2/valid? :fhir/Address x))))) (testing "round-trip" (testing "JSON" - (satisfies-prop 100 - (prop/for-all [x (fg/attachment)] + (satisfies-prop 20 + (prop/for-all [x (fg/address)] (= (->> (write-json x) - (parse-json "Attachment")) + (parse-json "Address")) + (->> (write-json x) + (parse-json "Address") + (write-json) + (parse-json "Address")) x)))) (testing "XML" - (satisfies-prop 100 - (prop/for-all [x (fg/attachment)] + (satisfies-prop 20 + (prop/for-all [x (fg/address)] (= (->> x fhir-spec/unform-xml - (s2/conform :fhir.xml/Attachment)) + (s2/conform :fhir.xml/Address)) x)))) (testing "CBOR" - (satisfies-prop 100 - (prop/for-all [x (fg/attachment)] + (satisfies-prop 20 + (prop/for-all [x (fg/address)] (= (->> (write-cbor x) - (parse-cbor "Attachment")) + (parse-cbor "Address")) + (->> (write-cbor x) + (parse-cbor "Address") + (write-cbor) + (parse-cbor "Address")) x))))) (testing "parsing" (testing "JSON" - (are [json fhir] (= fhir (write-parse-json "Attachment" json)) + (are [json fhir] (= fhir (write-parse-json "Address" json)) {} - #fhir/Attachment{} - - {:contentType "code-115735"} - #fhir/Attachment{:contentType #fhir/code "code-115735"} - - {:contentType "code-115735" - :_contentType {:extension [{:url "url-101510"}]}} - #fhir/Attachment - {:contentType - #fhir/code{:value "code-115735" - :extension [#fhir/Extension{:url "url-101510"}]}} - - {:_contentType {:id "id-205332"}} - #fhir/Attachment{:contentType #fhir/code{:id "id-205332"}} - - {:_contentType {:extension [{:url "url-101510"}]}} - #fhir/Attachment - {:contentType - #fhir/code{:extension [#fhir/Extension{:url "url-101510"}]}} - - {:data "MTA1NjE0Cg=="} - #fhir/Attachment{:data #fhir/base64Binary "MTA1NjE0Cg=="} - - {:_data {:extension [{:url "url-115417"}]}} - #fhir/Attachment{:data #fhir/base64Binary{:extension [#fhir/Extension{:url "url-115417"}]}} - - {:_url {:extension [{:url "url-130143"}]}} - #fhir/Attachment{:url #fhir/url{:extension [#fhir/Extension{:url "url-130143"}]}} - - {:_size {:extension [{:url "url-130946"}]}} - #fhir/Attachment{:size #fhir/unsignedInt{:extension [#fhir/Extension{:url "url-130946"}]}} - - {:size 204737 - :_size {:extension [{:url "url-131115"}]}} - #fhir/Attachment{:size #fhir/unsignedInt{:extension [#fhir/Extension{:url "url-131115"}] :value 204737}} - - {:_creation {:extension [{:url "url-132312"}]}} - #fhir/Attachment{:creation #fhir/dateTime{:extension [#fhir/Extension{:url "url-132312"}]}}) - - (testing "unknown keys are ignored" - (given (write-parse-json "Attachment" {::unknown "unknown"}) - ::anom/category := ::anom/incorrect - ::anom/message := "Invalid JSON representation of a resource. Unknown property `blaze.fhir.spec-test/unknown`." - [:fhir/issues 0 :fhir.issues/code] := "invariant" - [:fhir/issues 0 :fhir.issues/diagnostics] := "Unknown property `blaze.fhir.spec-test/unknown`." - [:fhir/issues 0 :fhir.issues/expression] := "Attachment")) + #fhir/Address{} - (testing "invalid underscore properties are ignored" - (given (write-parse-json "Attachment" {:_contentType "foo"}) - ::anom/category := ::anom/incorrect - ::anom/message := "Invalid JSON representation of a resource. Error on value `foo`. Expected type is `primitive extension map`." - [:fhir/issues 0 :fhir.issues/code] := "invariant" - [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on value `foo`. Expected type is `primitive extension map`." - [:fhir/issues 0 :fhir.issues/expression] := "Attachment.contentType")) + {:use "usual"} + #fhir/Address{:use #fhir/code "usual"}) (testing "invalid" - (given (write-parse-json "Attachment" {:contentType 1}) + (given (write-parse-json "Address" {:use 1}) ::anom/category := ::anom/incorrect ::anom/message := "Invalid JSON representation of a resource. Error on integer value 1. Expected type is `code`." [:fhir/issues 0 :fhir.issues/code] := "invariant" [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on integer value 1. Expected type is `code`." - [:fhir/issues 0 :fhir.issues/expression] := "Attachment.contentType"))) - - (testing "XML" - (are [xml fhir] (= fhir (s2/conform :fhir.xml/Attachment xml)) - (sexp [::f/Attachment]) - #fhir/Attachment{} - - (sexp [::f/Attachment {} [::f/contentType {:value "code-115735"}]]) - #fhir/Attachment{:contentType #fhir/code "code-115735"} - - (sexp [::f/Attachment {} [::f/contentType {:value "code-115735"} [::f/extension {:url "url-101510"}]]]) - #fhir/Attachment{:contentType #fhir/code{:value "code-115735" :extension [#fhir/Extension{:url "url-101510"}]}} - - (sexp [::f/Attachment {} [::f/contentType {:id "id-205332"}]]) - #fhir/Attachment{:contentType #fhir/code{:id "id-205332"}} - - (sexp [::f/Attachment {} [::f/contentType {} [::f/extension {:url "url-101510"}]]]) - #fhir/Attachment{:contentType #fhir/code{:extension [#fhir/Extension{:url "url-101510"}]}} - - (sexp [::f/Attachment {} [::f/data {:value "MTA1NjE0Cg=="}]]) - #fhir/Attachment{:data #fhir/base64Binary "MTA1NjE0Cg=="} - - (sexp [::f/Attachment {} [::f/data {} [::f/extension {:url "url-115417"}]]]) - #fhir/Attachment{:data #fhir/base64Binary{:extension [#fhir/Extension{:url "url-115417"}]}} - - (sexp [::f/Attachment {} [::f/url {} [::f/extension {:url "url-130143"}]]]) - #fhir/Attachment{:url #fhir/url{:extension [#fhir/Extension{:url "url-130143"}]}} + [:fhir/issues 0 :fhir.issues/expression] := "Address.use")) - (sexp [::f/Attachment {} [::f/size {:value "204742"}]]) - #fhir/Attachment{:size #fhir/unsignedInt 204742} + (testing "interned data elements" + (are [json key value] (identical? value (key (write-parse-json "Address" json))) + {:city "141600"} :city #fhir/string-interned "141600" + {:district "141612"} :district #fhir/string-interned "141612" + {:state "141621"} :state #fhir/string-interned "141621" + {:postalCode "141631"} :postalCode #fhir/string-interned "141631" + {:country "141643"} :country #fhir/string-interned "141643"))) - (sexp [::f/Attachment {} [::f/size {} [::f/extension {:url "url-130946"}]]]) - #fhir/Attachment{:size #fhir/unsignedInt{:extension [#fhir/Extension{:url "url-130946"}]}}))) + (testing "XML" + (testing "interned data elements" + (are [xml key value] (identical? value (key (s2/conform :fhir.xml/Address xml))) + (sexp [nil {} [:city {:value "145938"}]]) :city #fhir/string-interned "145938" + (sexp [nil {} [:district {:value "145938"}]]) :district #fhir/string-interned "145938" + (sexp [nil {} [:state {:value "145938"}]]) :state #fhir/string-interned "145938" + (sexp [nil {} [:postalCode {:value "145938"}]]) :postalCode #fhir/string-interned "145938" + (sexp [nil {} [:country {:value "150034"}]]) :country #fhir/string-interned "150034")))) (testing "writing" (testing "JSON" (are [fhir json] (= json (write-read-json fhir)) - #fhir/Attachment{} + #fhir/Address{} {} - #fhir/Attachment{:id "id-155426"} + #fhir/Address{:id "id-155426"} {:id "id-155426"} - #fhir/Attachment{:extension [#fhir/Extension{}]} + #fhir/Address{:extension [#fhir/Extension{}]} {:extension [{}]} - #fhir/Attachment{:extension [#fhir/Extension{} #fhir/Extension{}]} + #fhir/Address{:extension [#fhir/Extension{} #fhir/Extension{}]} {:extension [{} {}]} - #fhir/Attachment{:contentType #fhir/code "code-150209"} - {:contentType "code-150209"} - - #fhir/Attachment{:contentType #fhir/code{:extension [#fhir/Extension{:url "url-101529"}]}} - {:_contentType {:extension [{:url "url-101529"}]}} + #fhir/Address{:use #fhir/code "use-155449"} + {:use "use-155449"} - #fhir/Attachment{:contentType #fhir/code{:id "id-205332"}} - {:_contentType {:id "id-205332"}} + #fhir/Address{:text #fhir/string "text-170345"} + {:text "text-170345"} - #fhir/Attachment{:contentType #fhir/code{:extension [#fhir/Extension{}] :value "code-150225"}} - {:contentType "code-150225" - :_contentType {:extension [{}]}} + #fhir/Address{:line [#fhir/string "line-171433"]} + {:line ["line-171433"]} - #fhir/Attachment{:contentType #fhir/code{:id "id-205544" :extension [#fhir/Extension{}] :value "code-150225"}} - {:contentType "code-150225" - :_contentType {:id "id-205544" :extension [{}]}} + #fhir/Address{:line [#fhir/string "line-171433" #fhir/string "line-171857"]} + {:line ["line-171433" "line-171857"]} - #fhir/Attachment{:language #fhir/code "de"} - {:language "de"} + #fhir/Address{:city #fhir/string "city-171937"} + {:city "city-171937"} - #fhir/Attachment{:data #fhir/base64Binary "MTA1NjE0Cg=="} - {:data "MTA1NjE0Cg=="} + #fhir/Address{:district #fhir/string "district-171937"} + {:district "district-171937"} - #fhir/Attachment{:data #fhir/base64Binary{:extension [#fhir/Extension{:url "url-115417"}]}} - {:_data {:extension [{:url "url-115417"}]}} + #fhir/Address{:state #fhir/string "state-171937"} + {:state "state-171937"} - #fhir/Attachment{:data #fhir/base64Binary{:extension [#fhir/Extension{}] :value "MTA1NjE0Cg=="}} - {:data "MTA1NjE0Cg==" - :_data {:extension [{}]}} + #fhir/Address{:postalCode #fhir/string "postalCode-171937"} + {:postalCode "postalCode-171937"} - #fhir/Attachment{:url #fhir/url "url-210424"} - {:url "url-210424"} + #fhir/Address{:country #fhir/string "country-171937"} + {:country "country-171937"} - #fhir/Attachment{:url #fhir/url{:extension [#fhir/Extension{:url "url-130143"}]}} - {:_url {:extension [{:url "url-130143"}]}} - - #fhir/Attachment{:size #fhir/unsignedInt 204742} - {:size 204742} - - #fhir/Attachment{:size #fhir/unsignedInt{:extension [#fhir/Extension{:url "url-130946"}]}} - {:_size {:extension [{:url "url-130946"}]}} - - #fhir/Attachment{:size #fhir/unsignedInt{:extension [#fhir/Extension{:url "url-131115"}] :value 204737}} - {:size 204737 - :_size {:extension [{:url "url-131115"}]}} - - #fhir/Attachment{:hash #fhir/base64Binary "MTA1NjE0Cg=="} - {:hash "MTA1NjE0Cg=="} - - #fhir/Attachment{:title #fhir/string "title-210622"} - {:title "title-210622"} - - #fhir/Attachment{:creation #fhir/dateTime{:extension [#fhir/Extension{:url "url-132312"}]}} - {:_creation {:extension [{:url "url-132312"}]}} - - #fhir/Attachment{:creation #fhir/dateTime{:extension [#fhir/Extension{:url "url-132333"}] :value "2022"}} - {:creation "2022" - :_creation {:extension [{:url "url-132333"}]}})) + #fhir/Address{:period #fhir/Period{}} + {:period {}})) (testing "XML" (are [fhir xml] (= xml (fhir-spec/unform-xml fhir)) - #fhir/Attachment{} + #fhir/Address{} (sexp []) - #fhir/Attachment{:id "id-155426"} + #fhir/Address{:id "id-155426"} (sexp [nil {:id "id-155426"}]) - #fhir/Attachment{:extension [#fhir/Extension{}]} + #fhir/Address{:extension [#fhir/Extension{}]} (sexp [nil {} [::f/extension]]) - #fhir/Attachment{:extension [#fhir/Extension{} #fhir/Extension{}]} + #fhir/Address{:extension [#fhir/Extension{} #fhir/Extension{}]} (sexp [nil {} [::f/extension] [::f/extension]]) - #fhir/Attachment{:contentType #fhir/code "code-150209"} - (sexp [nil {} [::f/contentType {:value "code-150209"}]]) - - #fhir/Attachment{:contentType #fhir/code{:extension [#fhir/Extension{}]}} - (sexp [nil {} [::f/contentType {} [::f/extension]]]) - - #fhir/Attachment{:contentType #fhir/code{:id "id-205332"}} - (sexp [nil {} [::f/contentType {:id "id-205332"}]]) - - #fhir/Attachment{:contentType #fhir/code{:extension [#fhir/Extension{}] :value "code-150225"}} - (sexp [nil {} [::f/contentType {:value "code-150225"} [::f/extension]]]) - - #fhir/Attachment{:contentType #fhir/code{:id "id-205544" :extension [#fhir/Extension{}] :value "code-150225"}} - (sexp [nil {} [::f/contentType {:id "id-205544" :value "code-150225"} [::f/extension]]]) + #fhir/Address{:use #fhir/code "use-155449"} + (sexp [nil {} [::f/use {:value "use-155449"}]]) - #fhir/Attachment{:language #fhir/code "de"} - (sexp [nil {} [::f/language {:value "de"}]]) + #fhir/Address{:text #fhir/string "text-170345"} + (sexp [nil {} [::f/text {:value "text-170345"}]]) - #fhir/Attachment{:data #fhir/base64Binary "MTA1NjE0Cg=="} - (sexp [nil {} [::f/data {:value "MTA1NjE0Cg=="}]]) + #fhir/Address{:line [#fhir/string "line-171433"]} + (sexp [nil {} [::f/line {:value "line-171433"}]]) - #fhir/Attachment{:data #fhir/base64Binary{:extension [#fhir/Extension{:url "url-115417"}]}} - (sexp [nil {} [::f/data {} [::f/extension {:url "url-115417"}]]]) + #fhir/Address{:line [#fhir/string "line-171433" #fhir/string "line-171857"]} + (sexp [nil {} + [::f/line {:value "line-171433"}] + [::f/line {:value "line-171857"}]]) - #fhir/Attachment{:url #fhir/url "url-210424"} - (sexp [nil {} [::f/url {:value "url-210424"}]]) + #fhir/Address{:city #fhir/string "city-171937"} + (sexp [nil {} [::f/city {:value "city-171937"}]]) - #fhir/Attachment{:url #fhir/url{:extension [#fhir/Extension{:url "url-130143"}]}} - (sexp [nil {} [::f/url {} [::f/extension {:url "url-130143"}]]]) + #fhir/Address{:district #fhir/string "district-171937"} + (sexp [nil {} [::f/district {:value "district-171937"}]]) - #fhir/Attachment{:size #fhir/unsignedInt 204742} - (sexp [nil {} [::f/size {:value "204742"}]]) + #fhir/Address{:state #fhir/string "state-171937"} + (sexp [nil {} [::f/state {:value "state-171937"}]]) - #fhir/Attachment{:size #fhir/unsignedInt{:extension [#fhir/Extension{:url "url-130946"}]}} - (sexp [nil {} [::f/size {} [::f/extension {:url "url-130946"}]]]) + #fhir/Address{:postalCode #fhir/string "postalCode-171937"} + (sexp [nil {} [::f/postalCode {:value "postalCode-171937"}]]) - #fhir/Attachment{:hash #fhir/base64Binary "MTA1NjE0Cg=="} - (sexp [nil {} [::f/hash {:value "MTA1NjE0Cg=="}]]) + #fhir/Address{:country #fhir/string "country-171937"} + (sexp [nil {} [::f/country {:value "country-171937"}]]) - #fhir/Attachment{:title #fhir/string "title-210622"} - (sexp [nil {} [::f/title {:value "title-210622"}]]) + #fhir/Address{:period #fhir/Period{}} + (sexp [nil {} [::f/period]]))))) - #fhir/Attachment{:creation #fhir/dateTime "2021"} - (sexp [nil {} [::f/creation {:value "2021"}]])))) +(deftest ^:mem-size address-mem-size-test + (satisfies-prop 50 + (prop/for-all [source (gen/fmap write-cbor (fg/address))] + (>= (Base/memSize (parse-cbor "Address" source)) + (mem/total-size* (parse-cbor "Address" source) + (parse-cbor "Address" source))))) - (testing "summary parsing" - (satisfies-prop 20 - (prop/for-all [attachment (fg/attachment)] - (let [source (write-cbor attachment) - attachment (parse-cbor "Attachment" source :summary)] - (nil? (:data attachment))))))) + (testing "examples" + (mem-size-test "Address" + {:text "text-171612"} 120))) -(deftest extension-test +(deftest age-test (testing "FHIR spec" (testing "valid" - (satisfies-prop 20 - (prop/for-all [x (fg/extension :value (fg/codeable-concept))] - (s2/valid? :fhir/Extension x)))) - - (testing "invalid" - (are [x] (not (s2/valid? :fhir/Extension x)) - #fhir/Extension{:url 1}))) + (satisfies-prop 100 + (prop/for-all [x (fg/age)] + (s2/valid? :fhir/Age x))))) (testing "round-trip" - (doseq [value-gen - [(fg/base64Binary) - (fg/boolean) - (fg/canonical) - (fg/code) - (fg/date) - (fg/dateTime) - (fg/decimal) - (fg/id) - (fg/instant) - (fg/integer) - (fg/markdown) - (fg/oid) - (fg/positiveInt) - (fg/string) - (fg/string :value (gen/return "𝗔𝗗𝗗𝗜𝗧𝗜𝗢𝗡𝗔𝗟 𝗨𝗦𝗖𝗗𝗜")) - (fg/time) - (fg/unsignedInt) - (fg/uri) - (fg/url) - (fg/uuid) - (fg/address) - (fg/attachment) - (fg/codeable-concept) - (fg/coding) - (fg/human-name) - (fg/identifier) - (fg/period) - (fg/quantity) - (fg/ratio) - (fg/meta)]] - (testing "JSON" - (satisfies-prop 20 - (prop/for-all [x (fg/extension :value value-gen)] - (= (->> (write-json x) - (parse-json "Extension")) - (->> (write-json x) - (parse-json "Extension") - (write-json) - (parse-json "Extension")) - x)))) - - (testing "XML" - (satisfies-prop 20 - (prop/for-all [x (fg/extension :value value-gen)] - (= (->> x - fhir-spec/unform-xml - (s2/conform :fhir.xml/Extension)) - x)))) - - (testing "CBOR" - (satisfies-prop 20 - (prop/for-all [x (fg/extension :value value-gen)] - (= (->> (write-cbor x) - (parse-cbor "Extension")) - (->> (write-cbor x) - (parse-cbor "Extension") - (write-cbor) - (parse-cbor "Extension")) - x)))))) - - (testing "parsing" (testing "JSON" - (testing "urls are interned" - (let [e1 (write-parse-json "Extension" {:url (String. "foo") :valueString "bar"}) - e2 (write-parse-json "Extension" {:url (String. "foo") :valueString "bar"})] - (is (identical? (:url e1) (:url e2))))) - - (are [json fhir] (= fhir (write-parse-json "Extension" json)) - {:url "foo" :valueString "bar"} - #fhir/Extension{:url "foo" :value #fhir/string "bar"} - - {:url "foo" :valueCode "bar"} - #fhir/Extension{:url "foo" :value #fhir/code "bar"} - - {:url "foo" :valueReference {:reference "bar"}} - #fhir/Extension{:url "foo" :value #fhir/Reference{:reference #fhir/string "bar"}} - - {:url "foo" :valueCodeableConcept {:text "bar"}} - #fhir/Extension{:url "foo" :value #fhir/CodeableConcept{:text #fhir/string "bar"}} - - {:url "foo" :extension [{:url "bar" :_valueDateTime {:extension [{:url "baz" :valueCode "qux"}]}}]} - #fhir/Extension{:url "foo" :extension [#fhir/Extension{:url "bar" :value #fhir/dateTime{:extension [#fhir/Extension{:url "baz" :value #fhir/code "qux"}]}}]})) + (satisfies-prop 100 + (prop/for-all [x (fg/age)] + (= (->> (write-json x) + (parse-json "Age")) + (->> (write-json x) + (parse-json "Age") + (write-json) + (parse-json "Age")) + x)))) (testing "XML" - (testing "urls are interned" - (let [e1 (s2/conform :fhir.xml/Extension (sexp [nil {:url (String. "foo")} [::f/valueString {:value "bar"}]])) - e2 (s2/conform :fhir.xml/Extension (sexp [nil {:url (String. "foo")} [::f/valueString {:value "bar"}]]))] - (is (identical? (:url e1) (:url e2))))) + (satisfies-prop 100 + (prop/for-all [x (fg/age)] + (= (->> x + fhir-spec/unform-xml + (s2/conform :fhir.xml/Age)) + x)))) - (are [xml fhir] (= fhir (s2/conform :fhir.xml/Extension xml)) - (sexp [nil {:url "foo"} [::f/valueString {:value "bar"}]]) - #fhir/Extension{:url "foo" :value #fhir/string "bar"} + (testing "CBOR" + (satisfies-prop 100 + (prop/for-all [x (fg/age)] + (= (->> (write-cbor x) + (parse-cbor "Age")) + (->> (write-cbor x) + (parse-cbor "Age") + (write-cbor) + (parse-cbor "Age")) + x))))) - (sexp [nil {:url "foo"} [::f/valueCode {:value "bar"}]]) - #fhir/Extension{:url "foo" :value #fhir/code "bar"} + (testing "parsing" + (testing "JSON" + (are [json fhir] (= fhir (write-parse-json "Age" json)) + {} + #fhir/Age{} - (sexp [nil {:url "foo"} [::f/valueReference {} [::f/reference {:value "bar"}]]]) - #fhir/Extension{:url "foo" :value #fhir/Reference{:reference #fhir/string "bar"}} + {:value 1M} + #fhir/Age{:value #fhir/decimal 1M}) - (sexp [nil {:url "foo"} [::f/valueCodeableConcept {} [::f/text {:value "bar"}]]]) - #fhir/Extension{:url "foo" :value #fhir/CodeableConcept{:text #fhir/string "bar"}} + (testing "invalid" + (given (write-parse-json "Age" {:value "1"}) + ::anom/category := ::anom/incorrect + ::anom/message := "Invalid JSON representation of a resource. Error on value `1`. Expected type is `decimal`." + [:fhir/issues 0 :fhir.issues/code] := "invariant" + [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on value `1`. Expected type is `decimal`." + [:fhir/issues 0 :fhir.issues/expression] := "Age.value")) - (sexp [nil {:url "foo"} [::f/extension {:url "bar"} [::f/valueDateTime {} [::f/extension {:url "baz"} [::f/valueCode {:value "qux"}]]]]]) - #fhir/Extension{:url "foo" :extension [#fhir/Extension{:url "bar" :value #fhir/dateTime{:extension [#fhir/Extension{:url "baz" :value #fhir/code "qux"}]}}]})) + (testing "interned data elements" + (are [json key value] (identical? value (key (write-parse-json "Age" json))) + {:unit "151658"} :unit #fhir/string-interned "151658" + {:system "151641"} :system #fhir/uri-interned "151641"))) + + (testing "XML" + (testing "interned data elements" + (are [xml key value] (identical? value (key (s2/conform :fhir.xml/Age xml))) + (sexp [nil {} [:unit {:value "151938"}]]) :unit #fhir/string-interned "151938" + (sexp [nil {} [:system {:value "151921"}]]) :system #fhir/uri-interned "151921"))) (testing "CBOR" - (testing "urls are interned" - (let [e1 (write-parse-cbor "Extension" {:url (String. "foo") :valueString "bar"}) - e2 (write-parse-cbor "Extension" {:url (String. "foo") :valueString "bar"})] - (is (identical? (:url e1) (:url e2))))) + (are [json fhir] (= fhir (write-parse-cbor "Age" json)) + {} + #fhir/Age{} - (are [cbor fhir] (= fhir (write-parse-cbor "Extension" cbor)) - {:url "foo" :valueString "bar"} - #fhir/Extension{:url "foo" :value #fhir/string "bar"} + {:value 1M} + #fhir/Age{:value #fhir/decimal 1M}) - {:url "foo" :valueCode "bar"} - #fhir/Extension{:url "foo" :value #fhir/code "bar"} + (testing "interned data elements" + (are [cbor key value] (identical? value (key (write-parse-cbor "Age" cbor))) + {:unit "151658"} :unit #fhir/string-interned "151658" + {:system "151641"} :system #fhir/uri-interned "151641")) - {:url "foo" :valueReference {:reference "bar"}} - #fhir/Extension{:url "foo" :value #fhir/Reference{:reference #fhir/string "bar"}} + (testing "interning works" + (are [x y] (identical? x y) + (write-parse-cbor "Age" {:unit "unit-183017"}) + (write-parse-cbor "Age" {:unit "unit-183017"}) - {:url "foo" :valueCodeableConcept {:text "bar"}} - #fhir/Extension{:url "foo" :value #fhir/CodeableConcept{:text #fhir/string "bar"}} + (write-parse-cbor "Age" {:system "system-151829"}) + (write-parse-cbor "Age" {:system "system-151829"}))) - {:url "foo" :extension [{:url "bar" :_valueDateTime {:extension [{:url "baz" :valueCode "qux"}]}}]} - #fhir/Extension{:url "foo" :extension [#fhir/Extension{:url "bar" :value #fhir/dateTime{:extension [#fhir/Extension{:url "baz" :value #fhir/code "qux"}]}}]}))) + (testing "invalid" + (given (write-parse-cbor "Age" {:value "1"}) + ::anom/category := ::anom/incorrect + ::anom/message := "Invalid JSON representation of a resource. Error on value `1`. Expected type is `decimal`." + [:fhir/issues 0 :fhir.issues/code] := "invariant" + [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on value `1`. Expected type is `decimal`." + [:fhir/issues 0 :fhir.issues/expression] := "Age.value")))) (testing "writing" (testing "JSON" (are [fhir json] (= json (write-read-json fhir)) - #fhir/Extension{} + #fhir/Age{} {} - #fhir/Extension{:id "id-135149"} - {:id "id-135149"} + #fhir/Age{:id "id-134908"} + {:id "id-134908"} - #fhir/Extension{:extension [#fhir/Extension{}]} + #fhir/Age{:extension [#fhir/Extension{}]} {:extension [{}]} - #fhir/Extension{:extension [#fhir/Extension{} #fhir/Extension{}]} + #fhir/Age{:extension [#fhir/Extension{} #fhir/Extension{}]} {:extension [{} {}]} - #fhir/Extension{:url "url-135208"} - {:url "url-135208"} - - #fhir/Extension{:value #fhir/code "code-135234"} - {:valueCode "code-135234"} - - #fhir/Extension{:value #fhir/CodeableConcept{}} - {:valueCodeableConcept {}} - - #fhir/Extension{:value #uuid"935eb22d-cf35-4351-ae71-e517e49ebcbc"} - {:valueUuid "urn:uuid:935eb22d-cf35-4351-ae71-e517e49ebcbc"} - - #fhir/Extension{:value #fhir/uuid{:id "id-172058" :value #uuid"935eb22d-cf35-4351-ae71-e517e49ebcbc"}} - {:valueUuid "urn:uuid:935eb22d-cf35-4351-ae71-e517e49ebcbc" - :_valueUuid {:id "id-172058"}} - - #fhir/Extension{:value #fhir/CodeableConcept{:text #fhir/string "text-104840"}} - {:valueCodeableConcept {:text "text-104840"}} - - #fhir/Extension{:value #fhir/CodeableConcept{:coding [#fhir/Coding{:system #fhir/uri "system-105127"}]}} - {:valueCodeableConcept {:coding [{:system "system-105127"}]}} - - #fhir/Extension{:value {:fhir/type :fhir/Annotation :text "text-105422"}} - {:valueAnnotation {:text "text-105422"}} - - #fhir/Extension{:url "foo" :extension [#fhir/Extension{:url "bar" :value #fhir/dateTime{:extension [#fhir/Extension{:url "baz" :value #fhir/code "qux"}]}}]} - {:url "foo" :extension [{:url "bar" :_valueDateTime {:extension [{:url "baz" :valueCode "qux"}]}}]})) - - (testing "CBOR" - (are [fhir cbor] (= cbor (read-cbor (write-cbor fhir))) - #fhir/Extension{} - {} - - #fhir/Extension{:id "id-135149"} - {:id "id-135149"} - - #fhir/Extension{:extension [#fhir/Extension{}]} - {:extension [{}]} - - #fhir/Extension{:extension [#fhir/Extension{} #fhir/Extension{}]} - {:extension [{} {}]} + #fhir/Age{:value #fhir/decimal 1M} + {:value 1} - #fhir/Extension{:url "url-135208"} - {:url "url-135208"} + #fhir/Age{:comparator #fhir/code "code-153342"} + {:comparator "code-153342"} - #fhir/Extension{:value #fhir/code "code-135234"} - {:valueCode "code-135234"} + #fhir/Age{:unit #fhir/string "string-153351"} + {:unit "string-153351"} - #fhir/Extension{:value #fhir/CodeableConcept{}} - {:valueCodeableConcept {}} + #fhir/Age{:system #fhir/uri "system-153337"} + {:system "system-153337"} - #fhir/Extension{:value #fhir/Address{}} - {:valueAddress {}} + #fhir/Age{:code #fhir/code "code-153427"} + {:code "code-153427"})))) - #fhir/Extension{:value #fhir/Address{:city "foo"}} - {:valueAddress {:city "foo"}} +(deftest ^:mem-size age-mem-size-test + (satisfies-prop 50 + (prop/for-all [source (gen/fmap write-cbor (fg/age))] + (>= (Base/memSize (parse-cbor "Age" source)) + (mem/total-size* (parse-cbor "Age" source) + (parse-cbor "Age" source))))) - #fhir/Extension{:url "foo" :extension [#fhir/Extension{:url "bar" :value #fhir/dateTime{:extension [#fhir/Extension{:url "baz" :value #fhir/code "qux"}]}}]} - {:url "foo" :extension [{:url "bar" :_valueDateTime {:extension [{:url "baz" :valueCode "qux"}]}}]})))) + (testing "examples" + (mem-size-test "Age" + {:value 11} 80))) -(deftest coding-test +(deftest annotation-test (testing "FHIR spec" (testing "valid" - (satisfies-prop 100 - (prop/for-all [x (fg/coding)] - (s2/valid? :fhir/Coding x)))) - - (testing "invalid" - (are [x] (not (s2/valid? :fhir/Coding x)) - #fhir/Coding{:system "foo"}))) + (satisfies-prop 20 + (prop/for-all [x (fg/annotation)] + (s2/valid? :fhir/Annotation x))))) (testing "round-trip" (testing "JSON" (satisfies-prop 100 - (prop/for-all [x (fg/coding)] + (prop/for-all [x (fg/annotation)] (= (->> (write-json x) - (parse-json "Coding")) + (parse-json "Annotation")) + (->> (write-json x) + (parse-json "Annotation") + (write-json) + (parse-json "Annotation")) x)))) (testing "XML" (satisfies-prop 100 - (prop/for-all [x (fg/coding)] + (prop/for-all [x (fg/annotation)] (= (->> x fhir-spec/unform-xml - (s2/conform :fhir.xml/Coding)) + (s2/conform :fhir.xml/Annotation)) x)))) (testing "CBOR" (satisfies-prop 100 - (prop/for-all [x (fg/coding)] + (prop/for-all [x (fg/annotation)] (= (->> (write-cbor x) - (parse-cbor "Coding")) - x))))) + (parse-cbor "Annotation")) + (->> (write-cbor x) + (parse-cbor "Annotation") + (write-cbor) + (parse-cbor "Annotation")) + x)))))) + +(deftest ^:mem-size annotation-mem-size-test + (satisfies-prop 50 + (prop/for-all [source (gen/fmap write-cbor (fg/annotation))] + (>= (Base/memSize (parse-cbor "Annotation" source)) + (mem/total-size* (parse-cbor "Annotation" source) + (parse-cbor "Annotation" source))))) + + (testing "examples" + (mem-size-test "Annotation" + {:text "text-174325"} 88))) + +(deftest attachment-test + (testing "FHIR spec" + (testing "valid" + (satisfies-prop 100 + (prop/for-all [x (fg/attachment)] + (s2/valid? :fhir/Attachment x))))) + + (testing "round-trip" + (testing "JSON" + (satisfies-prop 100 + (prop/for-all [x (fg/attachment)] + (= (->> (write-json x) + (parse-json "Attachment")) + (->> (write-json x) + (parse-json "Attachment") + (write-json) + (parse-json "Attachment")) + x)))) + + (testing "XML" + (satisfies-prop 100 + (prop/for-all [x (fg/attachment)] + (= (->> x + fhir-spec/unform-xml + (s2/conform :fhir.xml/Attachment)) + x)))) + + (testing "CBOR" + (satisfies-prop 100 + (prop/for-all [x (fg/attachment)] + (= (->> (write-cbor x) + (parse-cbor "Attachment")) + (->> (write-cbor x) + (parse-cbor "Attachment") + (write-cbor) + (parse-cbor "Attachment")) + x))))) + + (testing "parsing" + (testing "JSON" + (are [json fhir] (= fhir (write-parse-json "Attachment" json)) + {} + #fhir/Attachment{} + + {:contentType "code-115735"} + #fhir/Attachment{:contentType #fhir/code "code-115735"} + + {:contentType "code-115735" + :_contentType {:extension [{:url "url-101510"}]}} + #fhir/Attachment + {:contentType + #fhir/code{:value "code-115735" + :extension [#fhir/Extension{:url "url-101510"}]}} + + {:_contentType {:id "id-205332"}} + #fhir/Attachment{:contentType #fhir/code{:id "id-205332"}} + + {:_contentType {:extension [{:url "url-101510"}]}} + #fhir/Attachment + {:contentType + #fhir/code{:extension [#fhir/Extension{:url "url-101510"}]}} + + {:data "MTA1NjE0Cg=="} + #fhir/Attachment{:data #fhir/base64Binary "MTA1NjE0Cg=="} + + {:_data {:extension [{:url "url-115417"}]}} + #fhir/Attachment{:data #fhir/base64Binary{:extension [#fhir/Extension{:url "url-115417"}]}} + + {:_url {:extension [{:url "url-130143"}]}} + #fhir/Attachment{:url #fhir/url{:extension [#fhir/Extension{:url "url-130143"}]}} + + {:_size {:extension [{:url "url-130946"}]}} + #fhir/Attachment{:size #fhir/unsignedInt{:extension [#fhir/Extension{:url "url-130946"}]}} + + {:size 204737 + :_size {:extension [{:url "url-131115"}]}} + #fhir/Attachment{:size #fhir/unsignedInt{:extension [#fhir/Extension{:url "url-131115"}] :value 204737}} + + {:_creation {:extension [{:url "url-132312"}]}} + #fhir/Attachment{:creation #fhir/dateTime{:extension [#fhir/Extension{:url "url-132312"}]}}) + + (testing "unknown keys are ignored" + (given (write-parse-json "Attachment" {::unknown "unknown"}) + ::anom/category := ::anom/incorrect + ::anom/message := "Invalid JSON representation of a resource. Unknown property `blaze.fhir.spec-test/unknown`." + [:fhir/issues 0 :fhir.issues/code] := "invariant" + [:fhir/issues 0 :fhir.issues/diagnostics] := "Unknown property `blaze.fhir.spec-test/unknown`." + [:fhir/issues 0 :fhir.issues/expression] := "Attachment")) + + (testing "invalid underscore properties are ignored" + (given (write-parse-json "Attachment" {:_contentType "foo"}) + ::anom/category := ::anom/incorrect + ::anom/message := "Invalid JSON representation of a resource. Error on value `foo`. Expected type is `primitive extension map`." + [:fhir/issues 0 :fhir.issues/code] := "invariant" + [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on value `foo`. Expected type is `primitive extension map`." + [:fhir/issues 0 :fhir.issues/expression] := "Attachment.contentType")) + + (testing "invalid" + (given (write-parse-json "Attachment" {:contentType 1}) + ::anom/category := ::anom/incorrect + ::anom/message := "Invalid JSON representation of a resource. Error on integer value 1. Expected type is `code`." + [:fhir/issues 0 :fhir.issues/code] := "invariant" + [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on integer value 1. Expected type is `code`." + [:fhir/issues 0 :fhir.issues/expression] := "Attachment.contentType"))) + + (testing "XML" + (are [xml fhir] (= fhir (s2/conform :fhir.xml/Attachment xml)) + (sexp [::f/Attachment]) + #fhir/Attachment{} + + (sexp [::f/Attachment {} [::f/contentType {:value "code-115735"}]]) + #fhir/Attachment{:contentType #fhir/code "code-115735"} + + (sexp [::f/Attachment {} [::f/contentType {:value "code-115735"} [::f/extension {:url "url-101510"}]]]) + #fhir/Attachment{:contentType #fhir/code{:value "code-115735" :extension [#fhir/Extension{:url "url-101510"}]}} + + (sexp [::f/Attachment {} [::f/contentType {:id "id-205332"}]]) + #fhir/Attachment{:contentType #fhir/code{:id "id-205332"}} + + (sexp [::f/Attachment {} [::f/contentType {} [::f/extension {:url "url-101510"}]]]) + #fhir/Attachment{:contentType #fhir/code{:extension [#fhir/Extension{:url "url-101510"}]}} + + (sexp [::f/Attachment {} [::f/data {:value "MTA1NjE0Cg=="}]]) + #fhir/Attachment{:data #fhir/base64Binary "MTA1NjE0Cg=="} + + (sexp [::f/Attachment {} [::f/data {} [::f/extension {:url "url-115417"}]]]) + #fhir/Attachment{:data #fhir/base64Binary{:extension [#fhir/Extension{:url "url-115417"}]}} + + (sexp [::f/Attachment {} [::f/url {} [::f/extension {:url "url-130143"}]]]) + #fhir/Attachment{:url #fhir/url{:extension [#fhir/Extension{:url "url-130143"}]}} + + (sexp [::f/Attachment {} [::f/size {:value "204742"}]]) + #fhir/Attachment{:size #fhir/unsignedInt 204742} + + (sexp [::f/Attachment {} [::f/size {} [::f/extension {:url "url-130946"}]]]) + #fhir/Attachment{:size #fhir/unsignedInt{:extension [#fhir/Extension{:url "url-130946"}]}}))) + + (testing "writing" + (testing "JSON" + (are [fhir json] (= json (write-read-json fhir)) + #fhir/Attachment{} + {} + + #fhir/Attachment{:id "id-155426"} + {:id "id-155426"} + + #fhir/Attachment{:extension [#fhir/Extension{}]} + {:extension [{}]} + + #fhir/Attachment{:extension [#fhir/Extension{} #fhir/Extension{}]} + {:extension [{} {}]} + + #fhir/Attachment{:contentType #fhir/code "code-150209"} + {:contentType "code-150209"} + + #fhir/Attachment{:contentType #fhir/code{:extension [#fhir/Extension{:url "url-101529"}]}} + {:_contentType {:extension [{:url "url-101529"}]}} + + #fhir/Attachment{:contentType #fhir/code{:id "id-205332"}} + {:_contentType {:id "id-205332"}} + + #fhir/Attachment{:contentType #fhir/code{:extension [#fhir/Extension{}] :value "code-150225"}} + {:contentType "code-150225" + :_contentType {:extension [{}]}} + + #fhir/Attachment{:contentType #fhir/code{:id "id-205544" :extension [#fhir/Extension{}] :value "code-150225"}} + {:contentType "code-150225" + :_contentType {:id "id-205544" :extension [{}]}} + + #fhir/Attachment{:language #fhir/code "de"} + {:language "de"} + + #fhir/Attachment{:data #fhir/base64Binary "MTA1NjE0Cg=="} + {:data "MTA1NjE0Cg=="} + + #fhir/Attachment{:data #fhir/base64Binary{:extension [#fhir/Extension{:url "url-115417"}]}} + {:_data {:extension [{:url "url-115417"}]}} + + #fhir/Attachment{:data #fhir/base64Binary{:extension [#fhir/Extension{}] :value "MTA1NjE0Cg=="}} + {:data "MTA1NjE0Cg==" + :_data {:extension [{}]}} + + #fhir/Attachment{:url #fhir/url "url-210424"} + {:url "url-210424"} + + #fhir/Attachment{:url #fhir/url{:extension [#fhir/Extension{:url "url-130143"}]}} + {:_url {:extension [{:url "url-130143"}]}} + + #fhir/Attachment{:size #fhir/unsignedInt 204742} + {:size 204742} + + #fhir/Attachment{:size #fhir/unsignedInt{:extension [#fhir/Extension{:url "url-130946"}]}} + {:_size {:extension [{:url "url-130946"}]}} + + #fhir/Attachment{:size #fhir/unsignedInt{:extension [#fhir/Extension{:url "url-131115"}] :value 204737}} + {:size 204737 + :_size {:extension [{:url "url-131115"}]}} + + #fhir/Attachment{:hash #fhir/base64Binary "MTA1NjE0Cg=="} + {:hash "MTA1NjE0Cg=="} + + #fhir/Attachment{:title #fhir/string "title-210622"} + {:title "title-210622"} + + #fhir/Attachment{:creation #fhir/dateTime{:extension [#fhir/Extension{:url "url-132312"}]}} + {:_creation {:extension [{:url "url-132312"}]}} + + #fhir/Attachment{:creation #fhir/dateTime{:extension [#fhir/Extension{:url "url-132333"}] :value #system/date-time "2022"}} + {:creation "2022" + :_creation {:extension [{:url "url-132333"}]}})) + + (testing "XML" + (are [fhir xml] (= xml (fhir-spec/unform-xml fhir)) + #fhir/Attachment{} + (sexp []) + + #fhir/Attachment{:id "id-155426"} + (sexp [nil {:id "id-155426"}]) + + #fhir/Attachment{:extension [#fhir/Extension{}]} + (sexp [nil {} [::f/extension]]) + + #fhir/Attachment{:extension [#fhir/Extension{} #fhir/Extension{}]} + (sexp [nil {} [::f/extension] [::f/extension]]) + + #fhir/Attachment{:contentType #fhir/code "code-150209"} + (sexp [nil {} [::f/contentType {:value "code-150209"}]]) + + #fhir/Attachment{:contentType #fhir/code{:extension [#fhir/Extension{}]}} + (sexp [nil {} [::f/contentType {} [::f/extension]]]) + + #fhir/Attachment{:contentType #fhir/code{:id "id-205332"}} + (sexp [nil {} [::f/contentType {:id "id-205332"}]]) + + #fhir/Attachment{:contentType #fhir/code{:extension [#fhir/Extension{}] :value "code-150225"}} + (sexp [nil {} [::f/contentType {:value "code-150225"} [::f/extension]]]) + + #fhir/Attachment{:contentType #fhir/code{:id "id-205544" :extension [#fhir/Extension{}] :value "code-150225"}} + (sexp [nil {} [::f/contentType {:id "id-205544" :value "code-150225"} [::f/extension]]]) + + #fhir/Attachment{:language #fhir/code "de"} + (sexp [nil {} [::f/language {:value "de"}]]) + + #fhir/Attachment{:data #fhir/base64Binary "MTA1NjE0Cg=="} + (sexp [nil {} [::f/data {:value "MTA1NjE0Cg=="}]]) + + #fhir/Attachment{:data #fhir/base64Binary{:extension [#fhir/Extension{:url "url-115417"}]}} + (sexp [nil {} [::f/data {} [::f/extension {:url "url-115417"}]]]) + + #fhir/Attachment{:url #fhir/url "url-210424"} + (sexp [nil {} [::f/url {:value "url-210424"}]]) + + #fhir/Attachment{:url #fhir/url{:extension [#fhir/Extension{:url "url-130143"}]}} + (sexp [nil {} [::f/url {} [::f/extension {:url "url-130143"}]]]) + + #fhir/Attachment{:size #fhir/unsignedInt 204742} + (sexp [nil {} [::f/size {:value "204742"}]]) + + #fhir/Attachment{:size #fhir/unsignedInt{:extension [#fhir/Extension{:url "url-130946"}]}} + (sexp [nil {} [::f/size {} [::f/extension {:url "url-130946"}]]]) + + #fhir/Attachment{:hash #fhir/base64Binary "MTA1NjE0Cg=="} + (sexp [nil {} [::f/hash {:value "MTA1NjE0Cg=="}]]) + + #fhir/Attachment{:title #fhir/string "title-210622"} + (sexp [nil {} [::f/title {:value "title-210622"}]]) + + #fhir/Attachment{:creation #fhir/dateTime #system/date-time "2021"} + (sexp [nil {} [::f/creation {:value "2021"}]])))) + + (testing "summary parsing" + (satisfies-prop 20 + (prop/for-all [attachment (fg/attachment)] + (let [source (write-cbor attachment) + attachment (parse-cbor "Attachment" source :summary)] + (nil? (:data attachment))))))) + +(deftest ^:mem-size attachment-mem-size-test + (satisfies-prop 50 + (prop/for-all [source (gen/fmap write-cbor (fg/attachment))] + (>= (Base/memSize (parse-cbor "Attachment" source)) + (mem/total-size* (parse-cbor "Attachment" source) + (parse-cbor "Attachment" source))))) + + (testing "examples" + (mem-size-test "Attachment" + {:hash "MTA1NjE0Cg=="} 112))) + +(deftest bundle-entry-search-test + (testing "FHIR spec" + (testing "valid" + (satisfies-prop 100 + (prop/for-all [x (fg/bundle-entry-search)] + (s2/valid? :fhir.Bundle.entry/search x))))) + + (testing "round-trip" + (testing "JSON" + (satisfies-prop 100 + (prop/for-all [x (fg/bundle-entry-search)] + (= (->> (write-json x) + (parse-json "Bundle.entry.search")) + x)))) + + (testing "XML" + (satisfies-prop 100 + (prop/for-all [x (fg/bundle-entry-search)] + (= (->> x + (s2/unform :fhir.xml.Bundle.entry/search) + (s2/conform :fhir.xml.Bundle.entry/search)) + x)))) + + (testing "CBOR" + (satisfies-prop 100 + (prop/for-all [x (fg/bundle-entry-search)] + (= (->> (write-cbor x) + (parse-cbor "Bundle.entry.search")) + x))))) + + (testing "parsing" + (testing "JSON" + (are [json fhir] (= fhir (write-parse-json "Bundle.entry.search" json)) + {} + #fhir.Bundle.entry/search{} + + {:id "id-134805"} + #fhir.Bundle.entry/search{:id "id-134805"} + + {:mode "match"} + #fhir.Bundle.entry/search{:mode #fhir/code "match"}) + + (testing "invalid" + (given (write-parse-json "Bundle.entry.search" {:id 1}) + ::anom/category := ::anom/incorrect + ::anom/message := "Invalid JSON representation of a resource. Error on integer value 1. Expected type is `string`." + [:fhir/issues 0 :fhir.issues/code] := "invariant" + [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on integer value 1. Expected type is `string`." + [:fhir/issues 0 :fhir.issues/expression] := "Bundle.entry.search.id"))) + + (testing "CBOR" + (are [cbor fhir] (= fhir (write-parse-cbor "Bundle.entry.search" cbor)) + {} + #fhir.Bundle.entry/search{} + + {:id "id-134805"} + #fhir.Bundle.entry/search{:id "id-134805"} + + {:mode "match"} + #fhir.Bundle.entry/search{:mode #fhir/code "match"}) + + (testing "invalid" + (given (write-parse-cbor "Bundle.entry.search" {:id 1}) + ::anom/category := ::anom/incorrect + ::anom/message := "Invalid JSON representation of a resource. Error on integer value 1. Expected type is `string`." + [:fhir/issues 0 :fhir.issues/code] := "invariant" + [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on integer value 1. Expected type is `string`." + [:fhir/issues 0 :fhir.issues/expression] := "Bundle.entry.search.id")))) + + (testing "writing" + (testing "JSON" + (are [fhir json] (= json (write-read-json fhir)) + #fhir.Bundle.entry/search{} + {} + + #fhir.Bundle.entry/search{:id "id-115229"} + {:id "id-115229"} + + #fhir.Bundle.entry/search{:extension [#fhir/Extension{}]} + {:extension [{}]} + + #fhir.Bundle.entry/search{:extension [#fhir/Extension{} #fhir/Extension{}]} + {:extension [{} {}]} + + #fhir.Bundle.entry/search{:mode #fhir/code "match"} + {:mode "match"} + + #fhir.Bundle.entry/search{:score #fhir/decimal 1.1M} + {:score 1.1})) + + (testing "CBOR" + (are [fhir cbor] (= cbor (read-cbor (write-cbor fhir))) + #fhir.Bundle.entry/search{} + {} + + #fhir.Bundle.entry/search{:id "id-115229"} + {:id "id-115229"} + + #fhir.Bundle.entry/search{:extension [#fhir/Extension{}]} + {:extension [{}]} + + #fhir.Bundle.entry/search{:extension [#fhir/Extension{} #fhir/Extension{}]} + {:extension [{} {}]} + + #fhir.Bundle.entry/search{:mode #fhir/code "match"} + {:mode "match"} + + #fhir.Bundle.entry/search{:score #fhir/decimal 1.1M} + {:score 1.1M})))) + +(deftest ^:mem-size bundle-entry-search-mem-size-test + (satisfies-prop 50 + (prop/for-all [source (gen/fmap write-cbor (fg/bundle-entry-search))] + (>= (Base/memSize (parse-cbor "Bundle.entry.search" source)) + (mem/total-size* (parse-cbor "Bundle.entry.search" source) + (parse-cbor "Bundle.entry.search" source))))) + + (testing "examples" + (mem-size-test "Bundle.entry.search" + {:score 11} 72))) + +(deftest codeable-concept-test + (testing "FHIR spec" + (testing "valid" + (satisfies-prop 100 + (prop/for-all [x (fg/codeable-concept)] + (s2/valid? :fhir/CodeableConcept x))))) + + (testing "round-trip" + (testing "JSON" + (satisfies-prop 100 + (prop/for-all [x (fg/codeable-concept)] + (= (->> (write-json x) + (parse-json "CodeableConcept")) + (->> (write-json x) + (parse-json "CodeableConcept") + (write-json) + (parse-json "CodeableConcept")) + x)))) + + (testing "XML" + (satisfies-prop 100 + (prop/for-all [x (fg/codeable-concept)] + (= (->> x + fhir-spec/unform-xml + (s2/conform :fhir.xml/CodeableConcept)) + x)))) + + (testing "CBOR" + (satisfies-prop 100 + (prop/for-all [x (fg/codeable-concept)] + (= (->> (write-cbor x) + (parse-cbor "CodeableConcept")) + (->> (write-cbor x) + (parse-cbor "CodeableConcept") + (write-cbor) + (parse-cbor "CodeableConcept")) + x))))) + + (testing "parsing" + (testing "JSON" + (are [json fhir] (= fhir (write-parse-json "CodeableConcept" json)) + {} + #fhir/CodeableConcept{} + {:coding [{}]} + #fhir/CodeableConcept{:coding [#fhir/Coding{}]} + {:text "text-223528"} + #fhir/CodeableConcept{:text #fhir/string "text-223528"}) + + (testing "interned data elements" + (are [json key value] (identical? value (key (write-parse-json "CodeableConcept" json))) + {:text "143903"} :text #fhir/string-interned "143903"))) + + (testing "XML" + (testing "interned data elements" + (are [xml key value] (identical? value (key (s2/conform :fhir.xml/CodeableConcept xml))) + (sexp [nil {} [:text {:value "150413"}]]) :text #fhir/string-interned "150413"))) + + (testing "CBOR" + (are [json fhir] (= fhir (write-parse-cbor "CodeableConcept" json)) + {} + #fhir/CodeableConcept{} + {:coding [{}]} + #fhir/CodeableConcept{:coding [#fhir/Coding{}]} + {:coding [{:system "foo" :code "bar"}]} + #fhir/CodeableConcept{:coding [#fhir/Coding{:system #fhir/uri "foo" :code #fhir/code "bar"}]} + {:text "text-223528"} + #fhir/CodeableConcept{:text #fhir/string "text-223528"}) + + (testing "interned data elements" + (are [cbor key value] (identical? value (key (write-parse-cbor "CodeableConcept" cbor))) + {:text "143903"} :text #fhir/string-interned "143903")) + + (testing "interning works" + (are [x y] (identical? x y) + (write-parse-cbor "CodeableConcept" {:text "text-182912"}) + (write-parse-cbor "CodeableConcept" {:text "text-182912"}))))) + + (testing "writing" + (testing "JSON" + (are [fhir json] (= json (write-read-json fhir)) + #fhir/CodeableConcept{} + {} + + #fhir/CodeableConcept{:id "id-134927"} + {:id "id-134927"} + + #fhir/CodeableConcept{:extension [#fhir/Extension{}]} + {:extension [{}]} + + #fhir/CodeableConcept{:extension [#fhir/Extension{} #fhir/Extension{}]} + {:extension [{} {}]} + + #fhir/CodeableConcept{:coding [#fhir/Coding{}]} + {:coding [{}]} + + #fhir/CodeableConcept{:text #fhir/string "text-223528"} + {:text "text-223528"})))) + +(deftest ^:mem-size codeable-concept-mem-size-test + (satisfies-prop 50 + (prop/for-all [source (gen/fmap write-cbor (fg/codeable-concept))] + (>= (Base/memSize (parse-cbor "CodeableConcept" source)) + (mem/total-size* (parse-cbor "CodeableConcept" source) + (parse-cbor "CodeableConcept" source))))) + + (testing "interning" + (are [x] (zero? (mem/total-size* (write-parse-json "CodeableConcept" x) + (write-parse-json "CodeableConcept" x))) + {:coding + [{:system "http://snomed.info/sct" + :code "160903007" + :display "Full-time employment (finding)"}]} + + {:coding + [{:system "http://snomed.info/sct" + :code "160903007" + :display "Full-time employment (finding)"}] + :text "Full-time employment (finding)"})) + + (testing "examples" + (mem-size-test "CodeableConcept" + {:id "id-173830"} 96))) + +(deftest coding-test + (testing "FHIR spec" + (testing "valid" + (satisfies-prop 100 + (prop/for-all [x (fg/coding)] + (s2/valid? :fhir/Coding x))))) + + (testing "round-trip" + (testing "JSON" + (satisfies-prop 100 + (prop/for-all [x (fg/coding)] + (= (->> (write-json x) + (parse-json "Coding")) + (->> (write-json x) + (parse-json "Coding") + (write-json) + (parse-json "Coding")) + x)))) + + (testing "XML" + (satisfies-prop 100 + (prop/for-all [x (fg/coding)] + (= (->> x + fhir-spec/unform-xml + (s2/conform :fhir.xml/Coding)) + x)))) + + (testing "CBOR" + (satisfies-prop 100 + (prop/for-all [x (fg/coding)] + (= (->> (write-cbor x) + (parse-cbor "Coding")) + (->> (write-cbor x) + (parse-cbor "Coding") + (write-cbor) + (parse-cbor "Coding")) + x))))) + + (testing "parsing" + (testing "JSON" + (are [json fhir] (= fhir (write-parse-json "Coding" json)) + {:system "foo" :code "bar"} + #fhir/Coding{:system #fhir/uri "foo" :code #fhir/code "bar"} + {:display "foo"} + #fhir/Coding{:display #fhir/string "foo"}) + + (testing "interned data elements" + (are [json key value] (identical? value (key (write-parse-json "Coding" json))) + {:system "143123"} :system #fhir/uri-interned "143123" + {:version "143903"} :version #fhir/string-interned "143903" + {:display "143942"} :display #fhir/string-interned "143942"))) + + (testing "XML" + (testing "interned data elements" + (are [xml key value] (identical? value (key (s2/conform :fhir.xml/Coding xml))) + (sexp [nil {} [:system {:value "145550"}]]) :system #fhir/uri-interned "145550" + (sexp [nil {} [:version {:value "145938"}]]) :version #fhir/string-interned "145938" + (sexp [nil {} [:display {:value "150034"}]]) :display #fhir/string-interned "150034"))) + + (testing "CBOR" + (are [json fhir] (= fhir (write-parse-cbor "Coding" json)) + {:system "foo" :code "bar"} + #fhir/Coding{:system #fhir/uri "foo" :code #fhir/code "bar"}) + + (testing "interned data elements" + (are [cbor key value] (identical? value (key (write-parse-cbor "Coding" cbor))) + {:system "143123"} :system #fhir/uri-interned "143123" + {:version "143903"} :version #fhir/string-interned "143903" + {:display "143942"} :display #fhir/string-interned "143942")) + + (testing "interning works" + (are [x y] (identical? x y) + (write-parse-cbor "Coding" {:system "system-143620" :code "bar"}) + (write-parse-cbor "Coding" {:system "system-143620" :code "bar"}) + + (write-parse-cbor "Coding" {:system "system-143620" :version "version-182229" :code "bar"}) + (write-parse-cbor "Coding" {:system "system-143620" :version "version-182229" :code "bar"}) + + (write-parse-cbor "Coding" {:system "system-143620" :code "bar" :display "display-182103"}) + (write-parse-cbor "Coding" {:system "system-143620" :code "bar" :display "display-182103"}))))) + + (testing "writing" + (testing "JSON" + (are [fhir json] (= json (write-read-json fhir)) + #fhir/Coding{} + {} + + #fhir/Coding{:id "id-205424"} + {:id "id-205424"} + + #fhir/Coding{:extension [#fhir/Extension{}]} + {:extension [{}]} + + #fhir/Coding{:extension [#fhir/Extension{} #fhir/Extension{}]} + {:extension [{} {}]} + + #fhir/Coding{:system #fhir/uri "system-185812"} + {:system "system-185812"} + + #fhir/Coding{:version #fhir/string "version-185951"} + {:version "version-185951"} + + #fhir/Coding{:code #fhir/code "code-190226"} + {:code "code-190226"} + + #fhir/Coding{:display #fhir/string "display-190327"} + {:display "display-190327"})) + + (testing "XML" + (are [fhir xml] (= xml (fhir-spec/unform-xml fhir)) + #fhir/Coding{} + (sexp []) + + #fhir/Coding{:id "id-101320"} + (sexp [nil {:id "id-101320"}]) + + #fhir/Coding{:extension [#fhir/Extension{}]} + (sexp [nil {} [::f/extension]]) + + #fhir/Coding{:extension [#fhir/Extension{} #fhir/Extension{}]} + (sexp [nil {} [::f/extension] [::f/extension]]) + + #fhir/Coding{:system #fhir/uri "system-185812"} + (sexp [nil {} [::f/system {:value "system-185812"}]]) + + #fhir/Coding{:version #fhir/string "version-185951"} + (sexp [nil {} [::f/version {:value "version-185951"}]]) + + #fhir/Coding{:code #fhir/code "code-190226"} + (sexp [nil {} [::f/code {:value "code-190226"}]]) + + #fhir/Coding{:display #fhir/string "display-190327"} + (sexp [nil {} [::f/display {:value "display-190327"}]]))))) + +(deftest ^:mem-size coding-mem-size-test + (satisfies-prop 50 + (prop/for-all [source (gen/fmap write-cbor (fg/coding))] + (>= (Base/memSize (parse-cbor "Coding" source)) + (mem/total-size* (parse-cbor "Coding" source) + (parse-cbor "Coding" source))))) + + (testing "interning" + (are [x] (zero? (mem/total-size* (write-parse-json "Coding" x) + (write-parse-json "Coding" x))) + {:system "http://snomed.info/sct" + :code "160903007" + :display "Full-time employment (finding)"})) + + (testing "examples" + (mem-size-test "Coding" + {:id "id-173830"} 112))) + +(deftest contact-detail-test + (testing "FHIR spec" + (testing "valid" + (satisfies-prop 20 + (prop/for-all [x (fg/contact-detail)] + (s2/valid? :fhir/ContactDetail x))))) + + (testing "round-trip" + (testing "JSON" + (satisfies-prop 100 + (prop/for-all [x (fg/contact-detail)] + (= (->> (write-json x) + (parse-json "ContactDetail")) + (->> (write-json x) + (parse-json "ContactDetail") + (write-json) + (parse-json "ContactDetail")) + x)))) + + (testing "XML" + (satisfies-prop 100 + (prop/for-all [x (fg/contact-detail)] + (= (->> x + fhir-spec/unform-xml + (s2/conform :fhir.xml/ContactDetail)) + x)))) + + (testing "CBOR" + (satisfies-prop 100 + (prop/for-all [x (fg/contact-detail)] + (= (->> (write-cbor x) + (parse-cbor "ContactDetail")) + (->> (write-cbor x) + (parse-cbor "ContactDetail") + (write-cbor) + (parse-cbor "ContactDetail")) + x)))))) + +(deftest ^:mem-size contact-detail-mem-size-test + (satisfies-prop 50 + (prop/for-all [source (gen/fmap write-cbor (fg/contact-detail))] + (>= (Base/memSize (parse-cbor "ContactDetail" source)) + (mem/total-size* (parse-cbor "ContactDetail" source) + (parse-cbor "ContactDetail" source))))) + + (testing "examples" + (mem-size-test "ContactDetail" + {:name "string-131125"} 96))) + +(deftest contact-point-test + (testing "FHIR spec" + (testing "valid" + (satisfies-prop 100 + (prop/for-all [x (fg/contact-point)] + (s2/valid? :fhir/ContactPoint x))))) + + (testing "round-trip" + (testing "JSON" + (satisfies-prop 100 + (prop/for-all [x (fg/contact-point)] + (= (->> (write-json x) + (parse-json "ContactPoint")) + (->> (write-json x) + (parse-json "ContactPoint") + (write-json) + (parse-json "ContactPoint")) + x)))) + + (testing "XML" + (satisfies-prop 100 + (prop/for-all [x (fg/contact-point)] + (= (->> x + fhir-spec/unform-xml + (s2/conform :fhir.xml/ContactPoint)) + x)))) + + (testing "CBOR" + (satisfies-prop 100 + (prop/for-all [x (fg/contact-point)] + (= (->> (write-cbor x) + (parse-cbor "ContactPoint")) + (->> (write-cbor x) + (parse-cbor "ContactPoint") + (write-cbor) + (parse-cbor "ContactPoint")) + x)))))) + +(deftest ^:mem-size contact-point-mem-size-test + (satisfies-prop 50 + (prop/for-all [source (gen/fmap write-cbor (fg/contact-point))] + (>= (Base/memSize (parse-cbor "ContactPoint" source)) + (mem/total-size* (parse-cbor "ContactPoint" source) + (parse-cbor "ContactPoint" source))))) + + (testing "examples" + (mem-size-test "ContactPoint" + {:value "string-131125"} 104))) + +(deftest contributor-test + (testing "FHIR spec" + (testing "valid" + (satisfies-prop 100 + (prop/for-all [x (fg/contributor)] + (s2/valid? :fhir/Contributor x))))) + + (testing "round-trip" + (testing "JSON" + (satisfies-prop 100 + (prop/for-all [x (fg/contributor)] + (= (->> (write-json x) + (parse-json "Contributor")) + (->> (write-json x) + (parse-json "Contributor") + (write-json) + (parse-json "Contributor")) + x)))) + + (testing "XML" + (satisfies-prop 100 + (prop/for-all [x (fg/contributor)] + (= (->> x + fhir-spec/unform-xml + (s2/conform :fhir.xml/Contributor)) + x)))) + + (testing "CBOR" + (satisfies-prop 100 + (prop/for-all [x (fg/contributor)] + (= (->> (write-cbor x) + (parse-cbor "Contributor")) + (->> (write-cbor x) + (parse-cbor "Contributor") + (write-cbor) + (parse-cbor "Contributor")) + x))))) + + (testing "parsing" + (testing "JSON" + (are [json fhir] (= fhir (write-parse-json "Contributor" json)) + {} + #fhir/Contributor{} + + {:type "foo"} + #fhir/Contributor{:type #fhir/code "foo"}))) + + (testing "writing" + (testing "JSON" + (are [fhir json] (= json (write-read-json fhir)) + #fhir/Contributor{} + {} + + #fhir/Contributor{:id "id-134908"} + {:id "id-134908"} + + #fhir/Contributor{:extension [#fhir/Extension{}]} + {:extension [{}]} + + #fhir/Contributor{:extension [#fhir/Extension{} #fhir/Extension{}]} + {:extension [{} {}]} + + #fhir/Contributor{:type #fhir/code "foo"} + {:type "foo"})))) + +(deftest ^:mem-size contributor-mem-size-test + (satisfies-prop 50 + (prop/for-all [source (gen/fmap write-cbor (fg/contributor))] + (>= (Base/memSize (parse-cbor "Contributor" source)) + (mem/total-size* (parse-cbor "Contributor" source) + (parse-cbor "Contributor" source))))) + + (testing "examples" + (mem-size-test "Contributor" + {:type "foo"} 24))) + +(deftest count-test + (testing "FHIR spec" + (testing "valid" + (satisfies-prop 100 + (prop/for-all [x (fg/count)] + (s2/valid? :fhir/Count x))))) + + (testing "round-trip" + (testing "JSON" + (satisfies-prop 100 + (prop/for-all [x (fg/count)] + (= (->> (write-json x) + (parse-json "Count")) + (->> (write-json x) + (parse-json "Count") + (write-json) + (parse-json "Count")) + x)))) + + (testing "XML" + (satisfies-prop 100 + (prop/for-all [x (fg/count)] + (= (->> x + fhir-spec/unform-xml + (s2/conform :fhir.xml/Count)) + x)))) + + (testing "CBOR" + (satisfies-prop 100 + (prop/for-all [x (fg/count)] + (= (->> (write-cbor x) + (parse-cbor "Count")) + (->> (write-cbor x) + (parse-cbor "Count") + (write-cbor) + (parse-cbor "Count")) + x))))) + + (testing "parsing" + (testing "JSON" + (are [json fhir] (= fhir (write-parse-json "Count" json)) + {} + #fhir/Count{} + + {:value 1M} + #fhir/Count{:value #fhir/decimal 1M}) + + (testing "invalid" + (given (write-parse-json "Count" {:value "1"}) + ::anom/category := ::anom/incorrect + ::anom/message := "Invalid JSON representation of a resource. Error on value `1`. Expected type is `decimal`." + [:fhir/issues 0 :fhir.issues/code] := "invariant" + [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on value `1`. Expected type is `decimal`." + [:fhir/issues 0 :fhir.issues/expression] := "Count.value")) + + (testing "interned data elements" + (are [json key value] (identical? value (key (write-parse-json "Count" json))) + {:unit "151658"} :unit #fhir/string-interned "151658" + {:system "151641"} :system #fhir/uri-interned "151641"))) + + (testing "XML" + (testing "interned data elements" + (are [xml key value] (identical? value (key (s2/conform :fhir.xml/Count xml))) + (sexp [nil {} [:unit {:value "151938"}]]) :unit #fhir/string-interned "151938" + (sexp [nil {} [:system {:value "151921"}]]) :system #fhir/uri-interned "151921"))) + + (testing "CBOR" + (are [json fhir] (= fhir (write-parse-cbor "Count" json)) + {} + #fhir/Count{} + + {:value 1M} + #fhir/Count{:value #fhir/decimal 1M}) + + (testing "interned data elements" + (are [cbor key value] (identical? value (key (write-parse-cbor "Count" cbor))) + {:unit "151658"} :unit #fhir/string-interned "151658" + {:system "151641"} :system #fhir/uri-interned "151641")) + + (testing "interning works" + (are [x y] (identical? x y) + (write-parse-cbor "Count" {:unit "unit-183017"}) + (write-parse-cbor "Count" {:unit "unit-183017"}) + + (write-parse-cbor "Count" {:system "system-151829"}) + (write-parse-cbor "Count" {:system "system-151829"}))) + + (testing "invalid" + (given (write-parse-cbor "Count" {:value "1"}) + ::anom/category := ::anom/incorrect + ::anom/message := "Invalid JSON representation of a resource. Error on value `1`. Expected type is `decimal`." + [:fhir/issues 0 :fhir.issues/code] := "invariant" + [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on value `1`. Expected type is `decimal`." + [:fhir/issues 0 :fhir.issues/expression] := "Count.value")))) + + (testing "writing" + (testing "JSON" + (are [fhir json] (= json (write-read-json fhir)) + #fhir/Count{} + {} + + #fhir/Count{:id "id-134908"} + {:id "id-134908"} + + #fhir/Count{:extension [#fhir/Extension{}]} + {:extension [{}]} + + #fhir/Count{:extension [#fhir/Extension{} #fhir/Extension{}]} + {:extension [{} {}]} + + #fhir/Count{:value #fhir/decimal 1M} + {:value 1} + + #fhir/Count{:comparator #fhir/code "code-153342"} + {:comparator "code-153342"} + + #fhir/Count{:unit #fhir/string "string-153351"} + {:unit "string-153351"} + + #fhir/Count{:system #fhir/uri "system-153337"} + {:system "system-153337"} + + #fhir/Count{:code #fhir/code "code-153427"} + {:code "code-153427"})))) + +(deftest ^:mem-size count-mem-size-test + (satisfies-prop 50 + (prop/for-all [source (gen/fmap write-cbor (fg/count))] + (>= (Base/memSize (parse-cbor "Count" source)) + (mem/total-size* (parse-cbor "Count" source) + (parse-cbor "Count" source))))) + + (testing "examples" + (mem-size-test "Count" + {:value 11} 80))) + +(deftest data-requirement-test + (testing "FHIR spec" + (testing "valid" + (satisfies-prop 20 + (prop/for-all [x (fg/data-requirement)] + (s2/valid? :fhir/DataRequirement x))))) + + (testing "round-trip" + (testing "JSON" + (satisfies-prop 20 + (prop/for-all [x (fg/data-requirement)] + (= (->> (write-json x) + (parse-json "DataRequirement")) + (->> (write-json x) + (parse-json "DataRequirement") + (write-json) + (parse-json "DataRequirement")) + x)))) + + (testing "XML" + (satisfies-prop 20 + (prop/for-all [x (fg/data-requirement)] + (= (->> x + fhir-spec/unform-xml + (s2/conform :fhir.xml/DataRequirement)) + x)))) + + (testing "CBOR" + (satisfies-prop 20 + (prop/for-all [x (fg/data-requirement)] + (= (->> (write-cbor x) + (parse-cbor "DataRequirement")) + (->> (write-cbor x) + (parse-cbor "DataRequirement") + (write-cbor) + (parse-cbor "DataRequirement")) + x))))) + + (testing "parsing" + (testing "JSON" + (are [json fhir] (= fhir (write-parse-json "DataRequirement" json)) + {} + #fhir/DataRequirement{} + + {:type "Patient"} + #fhir/DataRequirement{:type #fhir/code "Patient"}) + + (testing "invalid" + (given (write-parse-json "DataRequirement" {:type 1}) + ::anom/category := ::anom/incorrect + ::anom/message := "Invalid JSON representation of a resource. Error on integer value 1. Expected type is `code`." + [:fhir/issues 0 :fhir.issues/code] := "invariant" + [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on integer value 1. Expected type is `code`." + [:fhir/issues 0 :fhir.issues/expression] := "DataRequirement.type"))) + + (testing "CBOR" + (are [json fhir] (= fhir (write-parse-cbor "DataRequirement" json)) + {} + #fhir/DataRequirement{} + + {:type "Patient"} + #fhir/DataRequirement{:type #fhir/code "Patient"}) + + (testing "invalid" + (given (write-parse-cbor "DataRequirement" {:type 1}) + ::anom/category := ::anom/incorrect + ::anom/message := "Invalid JSON representation of a resource. Error on integer value 1. Expected type is `code`." + [:fhir/issues 0 :fhir.issues/code] := "invariant" + [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on integer value 1. Expected type is `code`." + [:fhir/issues 0 :fhir.issues/expression] := "DataRequirement.type")))) + + (testing "writing" + (testing "JSON" + (are [fhir json] (= json (write-read-json fhir)) + #fhir/DataRequirement{} + {} + + #fhir/DataRequirement{:id "id-134908"} + {:id "id-134908"} + + #fhir/DataRequirement{:extension [#fhir/Extension{}]} + {:extension [{}]} + + #fhir/DataRequirement{:extension [#fhir/Extension{} #fhir/Extension{}]} + {:extension [{} {}]} + + #fhir/DataRequirement{:type #fhir/code "Patient"} + {:type "Patient"})))) + +(deftest ^:mem-size data-requirement-mem-size-test + (satisfies-prop 50 + (prop/for-all [source (gen/fmap write-cbor (fg/data-requirement))] + (>= (Base/memSize (parse-cbor "DataRequirement" source)) + (mem/total-size* (parse-cbor "DataRequirement" source) + (parse-cbor "DataRequirement" source))))) + + (testing "examples" + (mem-size-test "DataRequirement" + {:type "Patient"} 48))) + +(deftest distance-test + (testing "FHIR spec" + (testing "valid" + (satisfies-prop 100 + (prop/for-all [x (fg/distance)] + (s2/valid? :fhir/Distance x))))) + + (testing "round-trip" + (testing "JSON" + (satisfies-prop 100 + (prop/for-all [x (fg/distance)] + (= (->> (write-json x) + (parse-json "Distance")) + (->> (write-json x) + (parse-json "Distance") + (write-json) + (parse-json "Distance")) + x)))) + + (testing "XML" + (satisfies-prop 100 + (prop/for-all [x (fg/distance)] + (= (->> x + fhir-spec/unform-xml + (s2/conform :fhir.xml/Distance)) + x)))) + + (testing "CBOR" + (satisfies-prop 100 + (prop/for-all [x (fg/distance)] + (= (->> (write-cbor x) + (parse-cbor "Distance")) + (->> (write-cbor x) + (parse-cbor "Distance") + (write-cbor) + (parse-cbor "Distance")) + x))))) + + (testing "parsing" + (testing "JSON" + (are [json fhir] (= fhir (write-parse-json "Distance" json)) + {} + #fhir/Distance{} + + {:value 1M} + #fhir/Distance{:value #fhir/decimal 1M}) + + (testing "invalid" + (given (write-parse-json "Distance" {:value "1"}) + ::anom/category := ::anom/incorrect + ::anom/message := "Invalid JSON representation of a resource. Error on value `1`. Expected type is `decimal`." + [:fhir/issues 0 :fhir.issues/code] := "invariant" + [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on value `1`. Expected type is `decimal`." + [:fhir/issues 0 :fhir.issues/expression] := "Distance.value")) + + (testing "interned data elements" + (are [json key value] (identical? value (key (write-parse-json "Distance" json))) + {:unit "151658"} :unit #fhir/string-interned "151658" + {:system "151641"} :system #fhir/uri-interned "151641"))) + + (testing "XML" + (testing "interned data elements" + (are [xml key value] (identical? value (key (s2/conform :fhir.xml/Distance xml))) + (sexp [nil {} [:unit {:value "151938"}]]) :unit #fhir/string-interned "151938" + (sexp [nil {} [:system {:value "151921"}]]) :system #fhir/uri-interned "151921"))) + + (testing "CBOR" + (are [json fhir] (= fhir (write-parse-cbor "Distance" json)) + {} + #fhir/Distance{} + + {:value 1M} + #fhir/Distance{:value #fhir/decimal 1M}) + + (testing "interned data elements" + (are [cbor key value] (identical? value (key (write-parse-cbor "Distance" cbor))) + {:unit "151658"} :unit #fhir/string-interned "151658" + {:system "151641"} :system #fhir/uri-interned "151641")) + + (testing "interning works" + (are [x y] (identical? x y) + (write-parse-cbor "Distance" {:unit "unit-183017"}) + (write-parse-cbor "Distance" {:unit "unit-183017"}) + + (write-parse-cbor "Distance" {:system "system-151829"}) + (write-parse-cbor "Distance" {:system "system-151829"}))) + + (testing "invalid" + (given (write-parse-cbor "Distance" {:value "1"}) + ::anom/category := ::anom/incorrect + ::anom/message := "Invalid JSON representation of a resource. Error on value `1`. Expected type is `decimal`." + [:fhir/issues 0 :fhir.issues/code] := "invariant" + [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on value `1`. Expected type is `decimal`." + [:fhir/issues 0 :fhir.issues/expression] := "Distance.value")))) + + (testing "writing" + (testing "JSON" + (are [fhir json] (= json (write-read-json fhir)) + #fhir/Distance{} + {} + + #fhir/Distance{:id "id-134908"} + {:id "id-134908"} + + #fhir/Distance{:extension [#fhir/Extension{}]} + {:extension [{}]} + + #fhir/Distance{:extension [#fhir/Extension{} #fhir/Extension{}]} + {:extension [{} {}]} + + #fhir/Distance{:value #fhir/decimal 1M} + {:value 1} + + #fhir/Distance{:comparator #fhir/code "code-153342"} + {:comparator "code-153342"} + + #fhir/Distance{:unit #fhir/string "string-153351"} + {:unit "string-153351"} + + #fhir/Distance{:system #fhir/uri "system-153337"} + {:system "system-153337"} + + #fhir/Distance{:code #fhir/code "code-153427"} + {:code "code-153427"})))) + +(deftest ^:mem-size distance-mem-size-test + (satisfies-prop 50 + (prop/for-all [source (gen/fmap write-cbor (fg/distance))] + (>= (Base/memSize (parse-cbor "Distance" source)) + (mem/total-size* (parse-cbor "Distance" source) + (parse-cbor "Distance" source))))) + + (testing "examples" + (mem-size-test "Distance" + {:value 11} 80))) + +(deftest dosage-test + (testing "FHIR spec" + (testing "valid" + (satisfies-prop 20 + (prop/for-all [x (fg/dosage)] + (s2/valid? :fhir/Dosage x))))) + + (testing "round-trip" + (testing "JSON" + (satisfies-prop 20 + (prop/for-all [x (fg/dosage)] + (= (->> (write-json x) + (parse-json "Dosage")) + (->> (write-json x) + (parse-json "Dosage") + (write-json) + (parse-json "Dosage")) + x)))) + + (testing "XML" + (satisfies-prop 20 + (prop/for-all [x (fg/dosage)] + (= (->> x + fhir-spec/unform-xml + (s2/conform :fhir.xml/Dosage)) + x)))) + + (testing "CBOR" + (satisfies-prop 20 + (prop/for-all [x (fg/dosage)] + (= (->> (write-cbor x) + (parse-cbor "Dosage")) + (->> (write-cbor x) + (parse-cbor "Dosage") + (write-cbor) + (parse-cbor "Dosage")) + x))))) + + (testing "parsing" + (testing "JSON" + (are [json fhir] (= fhir (write-parse-json "Dosage" json)) + {} + #fhir/Dosage{} + + {:sequence 1} + #fhir/Dosage{:sequence #fhir/integer 1}) + + (testing "invalid" + (given (write-parse-json "Dosage" {:sequence "1"}) + ::anom/category := ::anom/incorrect + ::anom/message := "Invalid JSON representation of a resource. Error on value `1`. Expected type is `integer`." + [:fhir/issues 0 :fhir.issues/code] := "invariant" + [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on value `1`. Expected type is `integer`." + [:fhir/issues 0 :fhir.issues/expression] := "Dosage.sequence"))) + + (testing "CBOR" + (are [json fhir] (= fhir (write-parse-cbor "Dosage" json)) + {} + #fhir/Dosage{} + + {:sequence 1} + #fhir/Dosage{:sequence #fhir/integer 1}) + + (testing "invalid" + (given (write-parse-cbor "Dosage" {:sequence "1"}) + ::anom/category := ::anom/incorrect + ::anom/message := "Invalid JSON representation of a resource. Error on value `1`. Expected type is `integer`." + [:fhir/issues 0 :fhir.issues/code] := "invariant" + [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on value `1`. Expected type is `integer`." + [:fhir/issues 0 :fhir.issues/expression] := "Dosage.sequence")))) + + (testing "writing" + (testing "JSON" + (are [fhir json] (= json (write-read-json fhir)) + #fhir/Dosage{} + {})))) + +(deftest ^:mem-size dosage-mem-size-test + (satisfies-prop 50 + (prop/for-all [source (gen/fmap write-cbor (fg/dosage))] + (>= (Base/memSize (parse-cbor "Dosage" source)) + (mem/total-size* (parse-cbor "Dosage" source) + (parse-cbor "Dosage" source))))) + + (testing "examples" + (mem-size-test "Dosage" + {:sequence 1} 96))) + +(deftest duration-test + (testing "FHIR spec" + (testing "valid" + (satisfies-prop 100 + (prop/for-all [x (fg/duration)] + (s2/valid? :fhir/Duration x))))) + + (testing "round-trip" + (testing "JSON" + (satisfies-prop 100 + (prop/for-all [x (fg/duration)] + (= (->> (write-json x) + (parse-json "Duration")) + (->> (write-json x) + (parse-json "Duration") + (write-json) + (parse-json "Duration")) + x)))) + + (testing "XML" + (satisfies-prop 100 + (prop/for-all [x (fg/duration)] + (= (->> x + fhir-spec/unform-xml + (s2/conform :fhir.xml/Duration)) + x)))) + + (testing "CBOR" + (satisfies-prop 100 + (prop/for-all [x (fg/duration)] + (= (->> (write-cbor x) + (parse-cbor "Duration")) + (->> (write-cbor x) + (parse-cbor "Duration") + (write-cbor) + (parse-cbor "Duration")) + x))))) + + (testing "parsing" + (testing "JSON" + (are [json fhir] (= fhir (write-parse-json "Duration" json)) + {} + #fhir/Duration{} + + {:value 1M} + #fhir/Duration{:value #fhir/decimal 1M}) + + (testing "invalid" + (given (write-parse-json "Duration" {:value "1"}) + ::anom/category := ::anom/incorrect + ::anom/message := "Invalid JSON representation of a resource. Error on value `1`. Expected type is `decimal`." + [:fhir/issues 0 :fhir.issues/code] := "invariant" + [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on value `1`. Expected type is `decimal`." + [:fhir/issues 0 :fhir.issues/expression] := "Duration.value")) + + (testing "interned data elements" + (are [json key value] (identical? value (key (write-parse-json "Duration" json))) + {:unit "151658"} :unit #fhir/string-interned "151658" + {:system "151641"} :system #fhir/uri-interned "151641"))) + + (testing "XML" + (testing "interned data elements" + (are [xml key value] (identical? value (key (s2/conform :fhir.xml/Duration xml))) + (sexp [nil {} [:unit {:value "151938"}]]) :unit #fhir/string-interned "151938" + (sexp [nil {} [:system {:value "151921"}]]) :system #fhir/uri-interned "151921"))) + + (testing "CBOR" + (are [json fhir] (= fhir (write-parse-cbor "Duration" json)) + {} + #fhir/Duration{} + + {:value 1M} + #fhir/Duration{:value #fhir/decimal 1M}) + + (testing "interned data elements" + (are [cbor key value] (identical? value (key (write-parse-cbor "Duration" cbor))) + {:unit "151658"} :unit #fhir/string-interned "151658" + {:system "151641"} :system #fhir/uri-interned "151641")) + + (testing "interning works" + (are [x y] (identical? x y) + (write-parse-cbor "Duration" {:unit "unit-183017"}) + (write-parse-cbor "Duration" {:unit "unit-183017"}) + + (write-parse-cbor "Duration" {:system "system-151829"}) + (write-parse-cbor "Duration" {:system "system-151829"}))) + + (testing "invalid" + (given (write-parse-cbor "Duration" {:value "1"}) + ::anom/category := ::anom/incorrect + ::anom/message := "Invalid JSON representation of a resource. Error on value `1`. Expected type is `decimal`." + [:fhir/issues 0 :fhir.issues/code] := "invariant" + [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on value `1`. Expected type is `decimal`." + [:fhir/issues 0 :fhir.issues/expression] := "Duration.value")))) + + (testing "writing" + (testing "JSON" + (are [fhir json] (= json (write-read-json fhir)) + #fhir/Duration{} + {} + + #fhir/Duration{:id "id-134908"} + {:id "id-134908"} + + #fhir/Duration{:extension [#fhir/Extension{}]} + {:extension [{}]} + + #fhir/Duration{:extension [#fhir/Extension{} #fhir/Extension{}]} + {:extension [{} {}]} + + #fhir/Duration{:value #fhir/decimal 1M} + {:value 1} + + #fhir/Duration{:comparator #fhir/code "code-153342"} + {:comparator "code-153342"} + + #fhir/Duration{:unit #fhir/string "string-153351"} + {:unit "string-153351"} + + #fhir/Duration{:system #fhir/uri "system-153337"} + {:system "system-153337"} + + #fhir/Duration{:code #fhir/code "code-153427"} + {:code "code-153427"})))) + +(deftest ^:mem-size duration-mem-size-test + (satisfies-prop 50 + (prop/for-all [source (gen/fmap write-cbor (fg/duration))] + (>= (Base/memSize (parse-cbor "Duration" source)) + (mem/total-size* (parse-cbor "Duration" source) + (parse-cbor "Duration" source))))) + + (testing "examples" + (mem-size-test "Duration" + {:value 11} 80))) + +(deftest expression-test + (testing "FHIR spec" + (testing "valid" + (satisfies-prop 100 + (prop/for-all [x (fg/expression)] + (s2/valid? :fhir/Expression x))))) + + (testing "round-trip" + (testing "JSON" + (satisfies-prop 100 + (prop/for-all [x (fg/expression)] + (= (->> (write-json x) + (parse-json "Expression")) + (->> (write-json x) + (parse-json "Expression") + (write-json) + (parse-json "Expression")) + x)))) + + (testing "XML" + (satisfies-prop 100 + (prop/for-all [x (fg/expression)] + (= (->> x + fhir-spec/unform-xml + (s2/conform :fhir.xml/Expression)) + x)))) + + (testing "CBOR" + (satisfies-prop 100 + (prop/for-all [x (fg/expression)] + (= (->> (write-cbor x) + (parse-cbor "Expression")) + (->> (write-cbor x) + (parse-cbor "Expression") + (write-cbor) + (parse-cbor "Expression")) + x))))) + + (testing "parsing" + (testing "JSON" + (are [json fhir] (= fhir (write-parse-json "Expression" json)) + {} + #fhir/Expression{} + + {:expression "expr-165511"} + #fhir/Expression{:expression #fhir/string "expr-165511"}) + + (testing "invalid" + (given (write-parse-json "Expression" {:expression 1}) + ::anom/category := ::anom/incorrect + ::anom/message := "Invalid JSON representation of a resource. Error on integer value 1. Expected type is `string`." + [:fhir/issues 0 :fhir.issues/code] := "invariant" + [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on integer value 1. Expected type is `string`." + [:fhir/issues 0 :fhir.issues/expression] := "Expression.expression")))) + + (testing "writing" + (testing "JSON" + (are [fhir json] (= json (write-read-json fhir)) + #fhir/Expression{} + {} + + #fhir/Expression{:id "id-134908"} + {:id "id-134908"} + + #fhir/Expression{:extension [#fhir/Extension{}]} + {:extension [{}]} + + #fhir/Expression{:extension [#fhir/Extension{} #fhir/Extension{}]} + {:extension [{} {}]} + + #fhir/Expression{:expression #fhir/string "expr-165511"} + {:expression "expr-165511"})))) + +(deftest ^:mem-size expression-mem-size-test + (satisfies-prop 50 + (prop/for-all [source (gen/fmap write-cbor (fg/expression))] + (>= (Base/memSize (parse-cbor "Expression" source)) + (mem/total-size* (parse-cbor "Expression" source) + (parse-cbor "Expression" source))))) + + (testing "examples" + (mem-size-test "Expression" + {:expression "expression-174835"} 104))) + +(deftest extension-test + (testing "FHIR spec" + (testing "valid" + (satisfies-prop 50 + (prop/for-all [x (fg/extension :value (fg/extension-value))] + (s2/valid? :fhir/Extension x))))) + + (testing "round-trip" + (testing "JSON" + (satisfies-prop 50 + (prop/for-all [x (fg/extension :value (fg/extension-value))] + (= (->> (write-json x) + (parse-json "Extension")) + (->> (write-json x) + (parse-json "Extension") + (write-json) + (parse-json "Extension")) + x)))) + + (testing "XML" + (satisfies-prop 50 + (prop/for-all [x (fg/extension :value (fg/extension-value))] + (= (->> x + fhir-spec/unform-xml + (s2/conform :fhir.xml/Extension)) + x)))) + + (testing "CBOR" + (satisfies-prop 50 + (prop/for-all [x (fg/extension :value (fg/extension-value))] + (= (->> (write-cbor x) + (parse-cbor "Extension")) + (->> (write-cbor x) + (parse-cbor "Extension") + (write-cbor) + (parse-cbor "Extension")) + x))))) + + (testing "parsing" + (testing "JSON" + (testing "urls are interned" + (let [e1 (write-parse-json "Extension" {:url (String. "foo") :valueString "bar"}) + e2 (write-parse-json "Extension" {:url (String. "foo") :valueString "bar"})] + (is (identical? (:url e1) (:url e2))))) + + (are [json fhir] (= fhir (write-parse-json "Extension" json)) + {:url "foo" :valueString "bar"} + #fhir/Extension{:url "foo" :value #fhir/string "bar"} + + {:url "foo" :valueCode "bar"} + #fhir/Extension{:url "foo" :value #fhir/code "bar"} + + {:url "foo" :valueReference {:reference "bar"}} + #fhir/Extension{:url "foo" :value #fhir/Reference{:reference #fhir/string "bar"}} + + {:url "foo" :valueCodeableConcept {:text "bar"}} + #fhir/Extension{:url "foo" :value #fhir/CodeableConcept{:text #fhir/string "bar"}} + + {:url "foo" :extension [{:url "bar" :_valueDateTime {:extension [{:url "baz" :valueCode "qux"}]}}]} + #fhir/Extension{:url "foo" :extension [#fhir/Extension{:url "bar" :value #fhir/dateTime{:extension [#fhir/Extension{:url "baz" :value #fhir/code "qux"}]}}]})) + + (testing "XML" + (testing "urls are interned" + (let [e1 (s2/conform :fhir.xml/Extension (sexp [nil {:url (String. "foo")} [::f/valueString {:value "bar"}]])) + e2 (s2/conform :fhir.xml/Extension (sexp [nil {:url (String. "foo")} [::f/valueString {:value "bar"}]]))] + (is (identical? (:url e1) (:url e2))))) + + (are [xml fhir] (= fhir (s2/conform :fhir.xml/Extension xml)) + (sexp [nil {:url "foo"} [::f/valueString {:value "bar"}]]) + #fhir/Extension{:url "foo" :value #fhir/string "bar"} + + (sexp [nil {:url "foo"} [::f/valueCode {:value "bar"}]]) + #fhir/Extension{:url "foo" :value #fhir/code "bar"} + + (sexp [nil {:url "foo"} [::f/valueReference {} [::f/reference {:value "bar"}]]]) + #fhir/Extension{:url "foo" :value #fhir/Reference{:reference #fhir/string "bar"}} + + (sexp [nil {:url "foo"} [::f/valueCodeableConcept {} [::f/text {:value "bar"}]]]) + #fhir/Extension{:url "foo" :value #fhir/CodeableConcept{:text #fhir/string "bar"}} + + (sexp [nil {:url "foo"} [::f/extension {:url "bar"} [::f/valueDateTime {} [::f/extension {:url "baz"} [::f/valueCode {:value "qux"}]]]]]) + #fhir/Extension{:url "foo" :extension [#fhir/Extension{:url "bar" :value #fhir/dateTime{:extension [#fhir/Extension{:url "baz" :value #fhir/code "qux"}]}}]})) + + (testing "CBOR" + (testing "urls are interned" + (let [e1 (write-parse-cbor "Extension" {:url (String. "foo") :valueString "bar"}) + e2 (write-parse-cbor "Extension" {:url (String. "foo") :valueString "bar"})] + (is (identical? (:url e1) (:url e2))))) + + (are [cbor fhir] (= fhir (write-parse-cbor "Extension" cbor)) + {:url "foo" :valueString "bar"} + #fhir/Extension{:url "foo" :value #fhir/string "bar"} + + {:url "foo" :valueCode "bar"} + #fhir/Extension{:url "foo" :value #fhir/code "bar"} + + {:url "foo" :valueReference {:reference "bar"}} + #fhir/Extension{:url "foo" :value #fhir/Reference{:reference #fhir/string "bar"}} + + {:url "foo" :valueCodeableConcept {:text "bar"}} + #fhir/Extension{:url "foo" :value #fhir/CodeableConcept{:text #fhir/string "bar"}} + + {:url "foo" :extension [{:url "bar" :_valueDateTime {:extension [{:url "baz" :valueCode "qux"}]}}]} + #fhir/Extension{:url "foo" :extension [#fhir/Extension{:url "bar" :value #fhir/dateTime{:extension [#fhir/Extension{:url "baz" :value #fhir/code "qux"}]}}]}))) + + (testing "writing" + (testing "JSON" + (are [fhir json] (= json (write-read-json fhir)) + #fhir/Extension{} + {} + + #fhir/Extension{:id "id-135149"} + {:id "id-135149"} + + #fhir/Extension{:extension [#fhir/Extension{}]} + {:extension [{}]} + + #fhir/Extension{:extension [#fhir/Extension{} #fhir/Extension{}]} + {:extension [{} {}]} + + #fhir/Extension{:url "url-135208"} + {:url "url-135208"} + + #fhir/Extension{:value #fhir/code "code-135234"} + {:valueCode "code-135234"} + + #fhir/Extension{:value #fhir/CodeableConcept{}} + {:valueCodeableConcept {}} + + #fhir/Extension{:value #fhir/uuid "urn:uuid:935eb22d-cf35-4351-ae71-e517e49ebcbc"} + {:valueUuid "urn:uuid:935eb22d-cf35-4351-ae71-e517e49ebcbc"} + + #fhir/Extension{:value #fhir/uuid{:id "id-172058" :value "urn:uuid:935eb22d-cf35-4351-ae71-e517e49ebcbc"}} + {:valueUuid "urn:uuid:935eb22d-cf35-4351-ae71-e517e49ebcbc" + :_valueUuid {:id "id-172058"}} + + #fhir/Extension{:value #fhir/CodeableConcept{:text #fhir/string "text-104840"}} + {:valueCodeableConcept {:text "text-104840"}} + + #fhir/Extension{:value #fhir/CodeableConcept{:coding [#fhir/Coding{:system #fhir/uri "system-105127"}]}} + {:valueCodeableConcept {:coding [{:system "system-105127"}]}} + + #fhir/Extension{:value #fhir/Annotation{:text #fhir/markdown "text-105422"}} + {:valueAnnotation {:text "text-105422"}} + + #fhir/Extension{:value #fhir/Age{:value #fhir/decimal 1M}} + {:valueAge {:value 1}} + + #fhir/Extension{:value #fhir/Count{:value #fhir/decimal 1M}} + {:valueCount {:value 1}} + + #fhir/Extension{:value #fhir/Distance{:value #fhir/decimal 1M}} + {:valueDistance {:value 1}} + + #fhir/Extension{:value #fhir/Duration{:value #fhir/decimal 1M}} + {:valueDuration {:value 1}} + + #fhir/Extension{:url "foo" :extension [#fhir/Extension{:url "bar" :value #fhir/dateTime{:extension [#fhir/Extension{:url "baz" :value #fhir/code "qux"}]}}]} + {:url "foo" :extension [{:url "bar" :_valueDateTime {:extension [{:url "baz" :valueCode "qux"}]}}]})) + + (testing "XML" + (are [fhir xml] (= xml (fhir-spec/unform-xml fhir)) + #fhir/Extension{} + (sexp []) + + #fhir/Extension{:id "id-101320"} + (sexp [nil {:id "id-101320"}]) + + #fhir/Extension{:extension [#fhir/Extension{}]} + (sexp [nil {} [::f/extension]]) + + #fhir/Extension{:extension [#fhir/Extension{} #fhir/Extension{}]} + (sexp [nil {} [::f/extension] [::f/extension]]) + + #fhir/Extension{:url "url-185812"} + (sexp [nil {:url "url-185812"}]) + + #fhir/Extension{:value #fhir/Age{:value #fhir/decimal 1M}} + (sexp [nil {} [::f/valueAge {} [::f/value {:value 1}]]]) + + #fhir/Extension{:value #fhir/Count{:value #fhir/decimal 1M}} + (sexp [nil {} [::f/valueCount {} [::f/value {:value 1}]]]) + + #fhir/Extension{:value #fhir/Distance{:value #fhir/decimal 1M}} + (sexp [nil {} [::f/valueDistance {} [::f/value {:value 1}]]]) + + #fhir/Extension{:value #fhir/Duration{:value #fhir/decimal 1M}} + (sexp [nil {} [::f/valueDuration {} [::f/value {:value 1}]]]) + + #fhir/Extension{:value #fhir/Quantity{:value #fhir/decimal 1M}} + (sexp [nil {} [::f/valueQuantity {} [::f/value {:value 1}]]]))) + + (testing "CBOR" + (are [fhir cbor] (= cbor (read-cbor (write-cbor fhir))) + #fhir/Extension{} + {} + + #fhir/Extension{:id "id-135149"} + {:id "id-135149"} + + #fhir/Extension{:extension [#fhir/Extension{}]} + {:extension [{}]} + + #fhir/Extension{:extension [#fhir/Extension{} #fhir/Extension{}]} + {:extension [{} {}]} + + #fhir/Extension{:url "url-135208"} + {:url "url-135208"} + + #fhir/Extension{:value #fhir/code "code-135234"} + {:valueCode "code-135234"} + + #fhir/Extension{:value #fhir/CodeableConcept{}} + {:valueCodeableConcept {}} + + #fhir/Extension{:value #fhir/Address{}} + {:valueAddress {}} + + #fhir/Extension{:value #fhir/Address{:city #fhir/string "foo"}} + {:valueAddress {:city "foo"}} + + #fhir/Extension{:value #fhir/Age{:value #fhir/decimal 1M}} + {:valueAge {:value 1M}} + + #fhir/Extension{:value #fhir/Count{:value #fhir/decimal 1M}} + {:valueCount {:value 1M}} + + #fhir/Extension{:value #fhir/Distance{:value #fhir/decimal 1M}} + {:valueDistance {:value 1M}} + + #fhir/Extension{:value #fhir/Duration{:value #fhir/decimal 1M}} + {:valueDuration {:value 1M}} + + #fhir/Extension{:url "foo" :extension [#fhir/Extension{:url "bar" :value #fhir/dateTime{:extension [#fhir/Extension{:url "baz" :value #fhir/code "qux"}]}}]} + {:url "foo" :extension [{:url "bar" :_valueDateTime {:extension [{:url "baz" :valueCode "qux"}]}}]})))) + +(deftest ^:mem-size extension-mem-size-test + (satisfies-prop 50 + (prop/for-all [source (gen/fmap write-cbor (fg/extension :value (fg/extension-value)))] + (>= (Base/memSize (parse-cbor "Extension" source)) + (mem/total-size* (parse-cbor "Extension" source) + (parse-cbor "Extension" source))))) + + (testing "interning" + (are [x y] (zero? (mem/total-size* x y)) + #fhir/Extension{:url "url-191107"} + #fhir/Extension{:url "url-191107"} + + (write-parse-json "Extension" {:url "url-191107"}) + (write-parse-json "Extension" {:url "url-191107"})))) + +(deftest human-name-test + (testing "FHIR spec" + (testing "valid" + (satisfies-prop 100 + (prop/for-all [x (fg/human-name)] + (s2/valid? :fhir/HumanName x))))) + + (testing "round-trip" + (testing "JSON" + (satisfies-prop 100 + (prop/for-all [x (fg/human-name)] + (= (->> (write-json x) + (parse-json "HumanName")) + (->> (write-json x) + (parse-json "HumanName") + (write-json) + (parse-json "HumanName")) + x)))) + + (testing "XML" + (satisfies-prop 100 + (prop/for-all [x (fg/human-name)] + (= (->> x + fhir-spec/unform-xml + (s2/conform :fhir.xml/HumanName)) + x)))) + + (testing "CBOR" + (satisfies-prop 100 + (prop/for-all [x (fg/human-name)] + (= (->> (write-cbor x) + (parse-cbor "HumanName")) + (->> (write-cbor x) + (parse-cbor "HumanName") + (write-cbor) + (parse-cbor "HumanName")) + x))))) (testing "parsing" (testing "JSON" - (are [json fhir] (= fhir (write-parse-json "Coding" json)) - {:system "foo" :code "bar"} - #fhir/Coding{:system #fhir/uri "foo" :code #fhir/code "bar"})) + (are [json fhir] (= fhir (write-parse-json "HumanName" json)) + {} + #fhir/HumanName{} + + {:use "usual"} + #fhir/HumanName{:use #fhir/code "usual"} + + {:given ["given-212441"]} + #fhir/HumanName{:given [#fhir/string "given-212441"]} + + {:_given [{:extension [{:url "url-143610"}]}]} + #fhir/HumanName{:given [#fhir/string{:extension [#fhir/Extension{:url "url-143610"}]}]} + + {:given ["given-143625"] + :_given [{:extension [{:url "url-143619"}]}]} + #fhir/HumanName{:given [#fhir/string{:extension [#fhir/Extension{:url "url-143619"}] :value "given-143625"}]} + + {:given ["given-212448" "given-212454"]} + #fhir/HumanName{:given [#fhir/string "given-212448" #fhir/string "given-212454"]} + + {:given ["given-143759" "given-143809"] + :_given [{:extension [{:url "url-143750"}]} {:extension [{:url "url-143806"}]}]} + #fhir/HumanName + {:given + [#fhir/string{:extension [#fhir/Extension{:url "url-143750"}] + :value "given-143759"} + #fhir/string{:extension [#fhir/Extension{:url "url-143806"}] + :value "given-143809"}]} + + {:given ["given-143759" nil] + :_given [nil {:extension [{:url "url-143806"}]}]} + #fhir/HumanName + {:given + [#fhir/string "given-143759" + #fhir/string{:extension [#fhir/Extension{:url "url-143806"}]}]}) + + (testing "invalid" + (given (write-parse-json "HumanName" {:use 1}) + ::anom/category := ::anom/incorrect + ::anom/message := "Invalid JSON representation of a resource. Error on integer value 1. Expected type is `code`." + [:fhir/issues 0 :fhir.issues/code] := "invariant" + [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on integer value 1. Expected type is `code`." + [:fhir/issues 0 :fhir.issues/expression] := "HumanName.use"))) + + (testing "CBOR" + (are [cbor fhir] (= fhir (write-parse-cbor "HumanName" cbor)) + {} + #fhir/HumanName{} + + {:use "usual"} + #fhir/HumanName{:use #fhir/code "usual"} + + {:given ["given-212441"]} + #fhir/HumanName{:given [#fhir/string "given-212441"]}))) + + (testing "writing" + (testing "JSON" + (are [fhir json] (= json (write-read-json fhir)) + #fhir/HumanName{} + {} + + #fhir/HumanName{:id "id-155426"} + {:id "id-155426"} + + #fhir/HumanName{:extension [#fhir/Extension{}]} + {:extension [{}]} + + #fhir/HumanName{:extension [#fhir/Extension{} #fhir/Extension{}]} + {:extension [{} {}]} + + #fhir/HumanName{:use #fhir/code "use-155449"} + {:use "use-155449"} + + #fhir/HumanName{:text #fhir/string "text-141140"} + {:text "text-141140"} + + #fhir/HumanName{:family #fhir/string "family-141158"} + {:family "family-141158"} + + #fhir/HumanName{:given [#fhir/string "given-212441"]} + {:given ["given-212441"]} + + #fhir/HumanName{:given [#fhir/string{:extension [#fhir/Extension{:url "url-143610"}]}]} + {:_given [{:extension [{:url "url-143610"}]}]} + + #fhir/HumanName{:given [#fhir/string{:extension [#fhir/Extension{:url "url-143619"}] :value "given-143625"}]} + {:given ["given-143625"] + :_given [{:extension [{:url "url-143619"}]}]} + + #fhir/HumanName{:given [#fhir/string "given-212448" #fhir/string "given-212454"]} + {:given ["given-212448" "given-212454"]} + + #fhir/HumanName + {:given + [#fhir/string{:extension [#fhir/Extension{:url "url-143750"}] + :value "given-143759"} + #fhir/string{:extension [#fhir/Extension{:url "url-143806"}] + :value "given-143809"}]} + {:given ["given-143759" "given-143809"] + :_given [{:extension [{:url "url-143750"}]} {:extension [{:url "url-143806"}]}]} + + #fhir/HumanName + {:given + [#fhir/string "given-143759" + #fhir/string{:extension [#fhir/Extension{:url "url-143806"}]}]} + {:given ["given-143759" nil] + :_given [nil {:extension [{:url "url-143806"}]}]} + + #fhir/HumanName{:period #fhir/Period{}} + {:period {}})) + + (testing "XML" + (are [fhir xml] (= xml (fhir-spec/unform-xml fhir)) + #fhir/HumanName{} + (sexp []) + + #fhir/HumanName{:id "id-155426"} + (sexp [nil {:id "id-155426"}]) + + #fhir/HumanName{:extension [#fhir/Extension{}]} + (sexp [nil {} [::f/extension]]) + + #fhir/HumanName{:extension [#fhir/Extension{} #fhir/Extension{}]} + (sexp [nil {} [::f/extension] [::f/extension]]) + + #fhir/HumanName{:use #fhir/code "use-155449"} + (sexp [nil {} [::f/use {:value "use-155449"}]]) + + #fhir/HumanName{:text #fhir/string "text-141140"} + (sexp [nil {} [::f/text {:value "text-141140"}]]) + + #fhir/HumanName{:family #fhir/string "family-141158"} + (sexp [nil {} [::f/family {:value "family-141158"}]]) + + #fhir/HumanName{:given [#fhir/string "given-212441"]} + (sexp [nil {} [::f/given {:value "given-212441"}]]) + + #fhir/HumanName{:given [#fhir/string "given-212441" #fhir/string "given-170006"]} + (sexp [nil {} + [::f/given {:value "given-212441"}] + [::f/given {:value "given-170006"}]]) + + #fhir/HumanName{:period #fhir/Period{}} + (sexp [nil {} [::f/period]]))))) + +(deftest ^:mem-size human-name-mem-size-test + (satisfies-prop 50 + (prop/for-all [source (gen/fmap write-cbor (fg/human-name))] + (>= (Base/memSize (parse-cbor "HumanName" source)) + (mem/total-size* (parse-cbor "HumanName" source) + (parse-cbor "HumanName" source))))) + + (testing "examples" + (mem-size-test "HumanName" + {:given ["given-171612"]} 160))) + +(deftest identifier-test + (testing "FHIR spec" + (testing "valid" + (satisfies-prop 20 + (prop/for-all [x (fg/identifier :assigner (fg/often-nil (fg/reference)))] + (s2/valid? :fhir/Identifier x))))) + + (testing "round-trip" + (testing "JSON" + (satisfies-prop 100 + (prop/for-all [x (fg/identifier :assigner (fg/often-nil (fg/reference)))] + (= (->> (write-json x) + (parse-json "Identifier")) + (->> (write-json x) + (parse-json "Identifier") + (write-json) + (parse-json "Identifier")) + x)))) + + (testing "XML" + (satisfies-prop 100 + (prop/for-all [x (fg/identifier :assigner (fg/often-nil (fg/reference)))] + (= (->> x + fhir-spec/unform-xml + (s2/conform :fhir.xml/Identifier)) + x)))) (testing "CBOR" - (are [json fhir] (= fhir (write-parse-cbor "Coding" json)) - {:system "foo" :code "bar"} - #fhir/Coding{:system #fhir/uri "foo" :code #fhir/code "bar"}) + (satisfies-prop 100 + (prop/for-all [x (fg/identifier :assigner (fg/often-nil (fg/reference)))] + (= (->> (write-cbor x) + (parse-cbor "Identifier")) + (->> (write-cbor x) + (parse-cbor "Identifier") + (write-cbor) + (parse-cbor "Identifier")) + x))))) - (testing "interning works" - (is (identical? - (write-parse-cbor "Coding" {:system "foo" :code "bar"}) - (write-parse-cbor "Coding" {:system "foo" :code "bar"})))))) + (testing "parsing" + (testing "JSON" + (are [json fhir] (= fhir (write-parse-json "Identifier" json)) + {} + #fhir/Identifier{} + + {:use "usual"} + #fhir/Identifier{:use #fhir/code "usual"} + + {:value "value-151311"} + #fhir/Identifier{:value #fhir/string "value-151311"}) + + (testing "invalid" + (given (write-parse-json "Identifier" {:use 1}) + ::anom/category := ::anom/incorrect + ::anom/message := "Invalid JSON representation of a resource. Error on integer value 1. Expected type is `code`." + [:fhir/issues 0 :fhir.issues/code] := "invariant" + [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on integer value 1. Expected type is `code`." + [:fhir/issues 0 :fhir.issues/expression] := "Identifier.use")) + + (testing "interned data elements" + (are [json key value] (identical? value (key (write-parse-json "Identifier" json))) + {:system "152132"} :system #fhir/uri-interned "152132"))) + + (testing "XML" + (testing "interned data elements" + (are [xml key value] (identical? value (key (s2/conform :fhir.xml/Identifier xml))) + (sexp [nil {} [:system {:value "152057"}]]) :system #fhir/uri-interned "152057"))) + + (testing "CBOR" + (testing "interned data elements" + (are [cbor key value] (identical? value (key (write-parse-json "Identifier" cbor))) + {:system "152229"} :system #fhir/uri-interned "152229")))) (testing "writing" (testing "JSON" (are [fhir json] (= json (write-read-json fhir)) - #fhir/Coding{} + #fhir/Identifier{} {} - #fhir/Coding{:id "id-205424"} - {:id "id-205424"} + #fhir/Identifier{:id "id-155426"} + {:id "id-155426"} - #fhir/Coding{:extension [#fhir/Extension{}]} + #fhir/Identifier{:extension [#fhir/Extension{}]} {:extension [{}]} - #fhir/Coding{:extension [#fhir/Extension{} #fhir/Extension{}]} + #fhir/Identifier{:extension [#fhir/Extension{} #fhir/Extension{}]} {:extension [{} {}]} - #fhir/Coding{:system #fhir/uri "system-185812"} - {:system "system-185812"} + #fhir/Identifier{:use #fhir/code "use-155449"} + {:use "use-155449"} - #fhir/Coding{:version #fhir/string "version-185951"} - {:version "version-185951"} + #fhir/Identifier{:type #fhir/CodeableConcept{}} + {:type {}} - #fhir/Coding{:code #fhir/code "code-190226"} - {:code "code-190226"} + #fhir/Identifier{:system #fhir/uri "system-160011"} + {:system "system-160011"} - #fhir/Coding{:display #fhir/string "display-190327"} - {:display "display-190327"})) + #fhir/Identifier{:value #fhir/string "value-160034"} + {:value "value-160034"} + + #fhir/Identifier{:period #fhir/Period{}} + {:period {}} + + #fhir/Identifier{:assigner #fhir/Reference{}} + {:assigner {}})) (testing "XML" (are [fhir xml] (= xml (fhir-spec/unform-xml fhir)) - #fhir/Coding{} + #fhir/Identifier{} (sexp []) - #fhir/Coding{:id "id-101320"} - (sexp [nil {:id "id-101320"}]) + #fhir/Identifier{:id "id-155426"} + (sexp [nil {:id "id-155426"}]) - #fhir/Coding{:extension [#fhir/Extension{}]} + #fhir/Identifier{:extension [#fhir/Extension{}]} (sexp [nil {} [::f/extension]]) - #fhir/Coding{:extension [#fhir/Extension{} #fhir/Extension{}]} + #fhir/Identifier{:extension [#fhir/Extension{} #fhir/Extension{}]} (sexp [nil {} [::f/extension] [::f/extension]]) - #fhir/Coding{:system #fhir/uri "system-185812"} - (sexp [nil {} [::f/system {:value "system-185812"}]]) + #fhir/Identifier{:use #fhir/code "use-155449"} + (sexp [nil {} [::f/use {:value "use-155449"}]]) - #fhir/Coding{:version #fhir/string "version-185951"} - (sexp [nil {} [::f/version {:value "version-185951"}]]) + #fhir/Identifier{:type #fhir/CodeableConcept{}} + (sexp [nil {} [::f/type]]) - #fhir/Coding{:code #fhir/code "code-190226"} - (sexp [nil {} [::f/code {:value "code-190226"}]]) + #fhir/Identifier{:system #fhir/uri "system-160011"} + (sexp [nil {} [::f/system {:value "system-160011"}]]) - #fhir/Coding{:display #fhir/string "display-190327"} - (sexp [nil {} [::f/display {:value "display-190327"}]]))))) + #fhir/Identifier{:value #fhir/string "value-160034"} + (sexp [nil {} [::f/value {:value "value-160034"}]]) -(deftest codeable-concept-test + #fhir/Identifier{:period #fhir/Period{}} + (sexp [nil {} [::f/period]]) + + #fhir/Identifier{:assigner #fhir/Reference{}} + (sexp [nil {} [::f/assigner]]))))) + +(deftest ^:mem-size identifier-mem-size-test + (satisfies-prop 50 + (prop/for-all [source (gen/fmap write-cbor (fg/identifier))] + (>= (Base/memSize (parse-cbor "Identifier" source)) + (mem/total-size* (parse-cbor "Identifier" source) + (parse-cbor "Identifier" source))))) + + (testing "examples" + (mem-size-test "Identifier" + {:use "use-192207"} 40 + {:system "system-113123"} 40 + {:value "string-151847"} 112))) + +(deftest meta-test (testing "FHIR spec" (testing "valid" - (satisfies-prop 100 - (prop/for-all [x (fg/codeable-concept)] - (s2/valid? :fhir/CodeableConcept x)))) - - (testing "invalid" - (are [x] (not (s2/valid? :fhir/CodeableConcept x)) - #fhir/CodeableConcept{:text 1}))) + (satisfies-prop 20 + (prop/for-all [x (fg/meta)] + (s2/valid? :fhir/Meta x))))) (testing "round-trip" (testing "JSON" (satisfies-prop 100 - (prop/for-all [x (fg/codeable-concept)] + (prop/for-all [x (fg/meta)] (= (->> (write-json x) - (parse-json "CodeableConcept")) + (parse-json "Meta")) + (->> (write-json x) + (parse-json "Meta") + (write-json) + (parse-json "Meta")) x)))) (testing "XML" (satisfies-prop 100 - (prop/for-all [x (fg/codeable-concept)] + (prop/for-all [x (fg/meta)] (= (->> x fhir-spec/unform-xml - (s2/conform :fhir.xml/CodeableConcept)) + (s2/conform :fhir.xml/Meta)) x)))) (testing "CBOR" (satisfies-prop 100 - (prop/for-all [x (fg/codeable-concept)] + (prop/for-all [x (fg/meta)] (= (->> (write-cbor x) - (parse-cbor "CodeableConcept")) + (parse-cbor "Meta")) + (->> (write-cbor x) + (parse-cbor "Meta") + (write-cbor) + (parse-cbor "Meta")) x))))) (testing "parsing" (testing "JSON" - (are [json fhir] (= fhir (write-parse-json "CodeableConcept" json)) + (are [json fhir] (= fhir (write-parse-json "Meta" json)) {} - #fhir/CodeableConcept{} - {:coding [{}]} - #fhir/CodeableConcept{:coding [#fhir/Coding{}]} - {:text "text-223528"} - #fhir/CodeableConcept{:text #fhir/string "text-223528"})) + #fhir/Meta{} - (testing "CBOR" - (are [json fhir] (= fhir (write-parse-cbor "CodeableConcept" json)) - {} - #fhir/CodeableConcept{} - {:coding [{}]} - #fhir/CodeableConcept{:coding [#fhir/Coding{}]} - {:coding [{:system "foo" :code "bar"}]} - #fhir/CodeableConcept{:coding [#fhir/Coding{:system #fhir/uri "foo" :code #fhir/code "bar"}]} - {:text "text-223528"} - #fhir/CodeableConcept{:text #fhir/string "text-223528"}) + {:versionId "1"} + #fhir/Meta{:versionId #fhir/id "1"} - (testing "interning works" - (is (identical? - (write-parse-cbor "CodeableConcept" {:coding [{:system "foo" :code "bar"}]}) - (write-parse-cbor "CodeableConcept" {:coding [{:system "foo" :code "bar"}]})))))) + {:lastUpdated "1970-01-01T00:00:00Z"} + (type/meta {:lastUpdated #fhir/instant #system/date-time "1970-01-01T00:00:00Z"})))) (testing "writing" (testing "JSON" (are [fhir json] (= json (write-read-json fhir)) - #fhir/CodeableConcept{} + #fhir/Meta{} {} - #fhir/CodeableConcept{:id "id-134927"} - {:id "id-134927"} + #fhir/Meta{:id "id-155426"} + {:id "id-155426"} - #fhir/CodeableConcept{:extension [#fhir/Extension{}]} + #fhir/Meta{:extension [#fhir/Extension{}]} {:extension [{}]} - #fhir/CodeableConcept{:extension [#fhir/Extension{} #fhir/Extension{}]} + #fhir/Meta{:extension [#fhir/Extension{} #fhir/Extension{}]} {:extension [{} {}]} - #fhir/CodeableConcept{:coding [#fhir/Coding{}]} - {:coding [{}]} + #fhir/Meta{:versionId #fhir/id "versionId-161812"} + {:versionId "versionId-161812"} - #fhir/CodeableConcept{:text #fhir/string "text-223528"} - {:text "text-223528"})))) + (type/meta {:lastUpdated #fhir/instant #system/date-time "1970-01-01T00:00:00Z"}) + {:lastUpdated "1970-01-01T00:00:00Z"} -(deftest quantity-test + #fhir/Meta{:source #fhir/uri "source-162704"} + {:source "source-162704"} + + #fhir/Meta{:profile [#fhir/canonical "profile-uri-145024"]} + {:profile ["profile-uri-145024"]} + + #fhir/Meta{:security [#fhir/Coding{}]} + {:security [{}]} + + #fhir/Meta{:security [#fhir/Coding{} #fhir/Coding{}]} + {:security [{} {}]} + + #fhir/Meta{:tag [#fhir/Coding{}]} + {:tag [{}]} + + #fhir/Meta{:tag [#fhir/Coding{} #fhir/Coding{}]} + {:tag [{} {}]})))) + +(deftest ^:mem-size meta-mem-size-test + (satisfies-prop 50 + (prop/for-all [source (gen/fmap write-cbor (fg/meta))] + (>= (Base/memSize (parse-cbor "Meta" source)) + (mem/total-size* (parse-cbor "Meta" source) + (parse-cbor "Meta" source))))) + + (testing "examples" + (mem-size-test "Meta" + {:versionId "id-172408"} 104))) + +(deftest money-test (testing "FHIR spec" (testing "valid" - (satisfies-prop 100 - (prop/for-all [x (fg/quantity)] - (s2/valid? :fhir/Quantity x)))) - - (testing "invalid" - (are [x] (not (s2/valid? :fhir/Quantity x)) - #fhir/Quantity{:value "1"}))) + (satisfies-prop 20 + (prop/for-all [x (fg/money)] + (s2/valid? :fhir/Money x))))) (testing "round-trip" (testing "JSON" (satisfies-prop 100 - (prop/for-all [x (fg/quantity)] + (prop/for-all [x (fg/money)] (= (->> (write-json x) - (parse-json "Quantity")) + (parse-json "Money")) + (->> (write-json x) + (parse-json "Money") + (write-json) + (parse-json "Money")) x)))) (testing "XML" (satisfies-prop 100 - (prop/for-all [x (fg/quantity)] + (prop/for-all [x (fg/money)] (= (->> x fhir-spec/unform-xml - (s2/conform :fhir.xml/Quantity)) + (s2/conform :fhir.xml/Money)) x)))) (testing "CBOR" (satisfies-prop 100 - (prop/for-all [x (fg/quantity)] + (prop/for-all [x (fg/money)] (= (->> (write-cbor x) - (parse-cbor "Quantity")) + (parse-cbor "Money")) + (->> (write-cbor x) + (parse-cbor "Money") + (write-cbor) + (parse-cbor "Money")) x))))) (testing "parsing" (testing "JSON" - (are [json fhir] (= fhir (write-parse-json "Quantity" json)) - {} - #fhir/Quantity{} - - {:value 1M} - #fhir/Quantity{:value #fhir/decimal 1M}) - - (testing "invalid" - (given (write-parse-json "Quantity" {:value "1"}) - ::anom/category := ::anom/incorrect - ::anom/message := "Invalid JSON representation of a resource. Error on value `1`. Expected type is `decimal`." - [:fhir/issues 0 :fhir.issues/code] := "invariant" - [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on value `1`. Expected type is `decimal`." - [:fhir/issues 0 :fhir.issues/expression] := "Quantity.value"))) - - (testing "CBOR" - (are [json fhir] (= fhir (write-parse-cbor "Quantity" json)) + (are [json fhir] (= fhir (write-parse-json "Money" json)) {} - #fhir/Quantity{} + #fhir/Money{} - {:value 1M} - #fhir/Quantity{:value #fhir/decimal 1M}) + {:value 1.0M} + #fhir/Money{:value #fhir/decimal 1.0M} - (testing "invalid" - (given (write-parse-cbor "Quantity" {:value "1"}) - ::anom/category := ::anom/incorrect - ::anom/message := "Invalid JSON representation of a resource. Error on value `1`. Expected type is `decimal`." - [:fhir/issues 0 :fhir.issues/code] := "invariant" - [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on value `1`. Expected type is `decimal`." - [:fhir/issues 0 :fhir.issues/expression] := "Quantity.value")))) + {:currency "EUR"} + #fhir/Money{:currency #fhir/code"EUR"}))) (testing "writing" (testing "JSON" (are [fhir json] (= json (write-read-json fhir)) - #fhir/Quantity{} + #fhir/Money{} {} - #fhir/Quantity{:id "id-134908"} - {:id "id-134908"} + #fhir/Money{:id "id-162818"} + {:id "id-162818"} - #fhir/Quantity{:extension [#fhir/Extension{}]} + #fhir/Money{:extension [#fhir/Extension{}]} {:extension [{}]} - #fhir/Quantity{:extension [#fhir/Extension{} #fhir/Extension{}]} - {:extension [{} {}]} - - #fhir/Quantity{:value #fhir/decimal 1M} - {:value 1} - - #fhir/Quantity{:comparator #fhir/code "code-153342"} - {:comparator "code-153342"} + #fhir/Money{:value #fhir/decimal 1.0M} + {:value 1.0} - #fhir/Quantity{:unit #fhir/string "string-153351"} - {:unit "string-153351"} - - #fhir/Quantity{:system #fhir/uri "system-153337"} - {:system "system-153337"} + #fhir/Money{:currency #fhir/code"EUR"} + {:currency "EUR"})))) - #fhir/Quantity{:code #fhir/code "code-153427"} - {:code "code-153427"})))) +(deftest ^:mem-size money-mem-size-test + (satisfies-prop 50 + (prop/for-all [source (gen/fmap write-cbor (fg/money))] + (>= (Base/memSize (parse-cbor "Money" source)) + (mem/total-size* (parse-cbor "Money" source) + (parse-cbor "Money" source)))))) -(deftest ratio-test +(deftest parameter-definition-test (testing "FHIR spec" (testing "valid" - (satisfies-prop 100 - (prop/for-all [x (fg/ratio)] - (s2/valid? :fhir/Ratio x)))) - - (testing "invalid" - (are [x] (not (s2/valid? :fhir/Ratio x)) - #fhir/Ratio{:numerator "1"}))) + (satisfies-prop 20 + (prop/for-all [x (fg/parameter-definition)] + (s2/valid? :fhir/ParameterDefinition x))))) (testing "round-trip" (testing "JSON" (satisfies-prop 100 - (prop/for-all [x (fg/ratio)] + (prop/for-all [x (fg/parameter-definition)] (= (->> (write-json x) - (parse-json "Ratio")) + (parse-json "ParameterDefinition")) + (->> (write-json x) + (parse-json "ParameterDefinition") + (write-json) + (parse-json "ParameterDefinition")) x)))) (testing "XML" (satisfies-prop 100 - (prop/for-all [x (fg/ratio)] + (prop/for-all [x (fg/parameter-definition)] (= (->> x fhir-spec/unform-xml - (s2/conform :fhir.xml/Ratio)) + (s2/conform :fhir.xml/ParameterDefinition)) x)))) (testing "CBOR" (satisfies-prop 100 - (prop/for-all [x (fg/ratio)] + (prop/for-all [x (fg/parameter-definition)] (= (->> (write-cbor x) - (parse-cbor "Ratio")) + (parse-cbor "ParameterDefinition")) + (->> (write-cbor x) + (parse-cbor "ParameterDefinition") + (write-cbor) + (parse-cbor "ParameterDefinition")) x))))) (testing "parsing" (testing "JSON" - (are [json fhir] (= fhir (write-parse-json "Ratio" json)) + (are [json fhir] (= fhir (write-parse-json "ParameterDefinition" json)) {} - #fhir/Ratio{} + #fhir/ParameterDefinition{} - {:id "id-151304"} - #fhir/Ratio{:id "id-151304"} - - {:extension [{}]} - #fhir/Ratio{:extension [#fhir/Extension{}]} + {:name "param-name"} + #fhir/ParameterDefinition{:name #fhir/code "param-name"} - {:numerator {:value 1M}} - #fhir/Ratio{:numerator #fhir/Quantity{:value #fhir/decimal 1M}}) + {:use "in"} + #fhir/ParameterDefinition{:use #fhir/code "in"} - (testing "invalid" - (given (write-parse-json "Ratio" {:numerator "foo"}) - ::anom/category := ::anom/incorrect - ::anom/message := "Invalid JSON representation of a resource. Error on value `foo`. Expected type is `Quantity`." - [:fhir/issues 0 :fhir.issues/code] := "invariant" - [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on value `foo`. Expected type is `Quantity`." - [:fhir/issues 0 :fhir.issues/expression] := "Ratio.numerator")))) + {:type "string"} + #fhir/ParameterDefinition{:type #fhir/code "string"}))) (testing "writing" (testing "JSON" (are [fhir json] (= json (write-read-json fhir)) - #fhir/Ratio{} + #fhir/ParameterDefinition{} {} - #fhir/Ratio{:id "id-134428"} - {:id "id-134428"} - - #fhir/Ratio{:extension [#fhir/Extension{}]} - {:extension [{}]} - - #fhir/Ratio{:extension [#fhir/Extension{} #fhir/Extension{}]} - {:extension [{} {}]} + #fhir/ParameterDefinition{:id "id-123"} + {:id "id-123"} - #fhir/Ratio{:numerator #fhir/Quantity{:value #fhir/decimal 1M}} - {:numerator {:value 1}} + #fhir/ParameterDefinition{:name #fhir/code "param-name"} + {:name "param-name"})))) - #fhir/Ratio{:denominator #fhir/Quantity{:value #fhir/decimal 1M}} - {:denominator {:value 1}})))) +(deftest ^:mem-size parameter-definition-mem-size-test + (satisfies-prop 50 + (prop/for-all [source (gen/fmap write-cbor (fg/parameter-definition))] + (>= (Base/memSize (parse-cbor "ParameterDefinition" source)) + (mem/total-size* (parse-cbor "ParameterDefinition" source) + (parse-cbor "ParameterDefinition" source)))))) (deftest period-test (testing "FHIR spec" (testing "valid" (satisfies-prop 100 (prop/for-all [x (fg/period)] - (s2/valid? :fhir/Period x)))) - - (testing "invalid" - (are [x] (not (s2/valid? :fhir/Period x)) - #fhir/Period{:start "2020"}))) + (s2/valid? :fhir/Period x))))) (testing "round-trip" (testing "JSON" @@ -2977,6 +4931,10 @@ (prop/for-all [x (fg/period)] (= (->> (write-json x) (parse-json "Period")) + (->> (write-json x) + (parse-json "Period") + (write-json) + (parse-json "Period")) x)))) (testing "XML" @@ -2992,6 +4950,10 @@ (prop/for-all [x (fg/period)] (= (->> (write-cbor x) (parse-cbor "Period")) + (->> (write-cbor x) + (parse-cbor "Period") + (write-cbor) + (parse-cbor "Period")) x))))) (testing "parsing" @@ -3007,14 +4969,14 @@ #fhir/Period{:extension [#fhir/Extension{}]} {:start "2020"} - #fhir/Period{:start #fhir/dateTime "2020"}) + #fhir/Period{:start #fhir/dateTime #system/date-time "2020"}) (testing "invalid" (given (write-parse-json "Period" {:start "foo"}) ::anom/category := ::anom/incorrect - ::anom/message := "Invalid JSON representation of a resource. Error on value `foo`. Expected type is `date-time`." + ::anom/message := "Invalid JSON representation of a resource. Error on value `foo`. Expected type is `date-time`. Text cannot be parsed to a DateTime" [:fhir/issues 0 :fhir.issues/code] := "invariant" - [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on value `foo`. Expected type is `date-time`." + [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on value `foo`. Expected type is `date-time`. Text cannot be parsed to a DateTime" [:fhir/issues 0 :fhir.issues/expression] := "Period.start")))) (testing "writing" @@ -3032,480 +4994,338 @@ #fhir/Period{:extension [#fhir/Extension{} #fhir/Extension{}]} {:extension [{} {}]} - #fhir/Period{:start #fhir/dateTime "2020"} + #fhir/Period{:start #fhir/dateTime #system/date-time "2020"} {:start "2020"} - #fhir/Period{:end #fhir/dateTime "2020"} - {:end "2020"})))) - -(deftest identifier-test - (testing "FHIR spec" - (testing "valid" - (satisfies-prop 20 - (prop/for-all [x (fg/identifier :assigner (fg/often-nil (fg/reference)))] - (s2/valid? :fhir/Identifier x)))) - - (testing "invalid" - (are [x] (not (s2/valid? :fhir/Identifier x)) - #fhir/Identifier{:use "usual"}))) - - (testing "round-trip" - (testing "JSON" - (satisfies-prop 20 - (prop/for-all [x (fg/identifier :assigner (fg/often-nil (fg/reference)))] - (= (->> (write-json x) - (parse-json "Identifier")) - x)))) - - (testing "XML" - (satisfies-prop 20 - (prop/for-all [x (fg/identifier :assigner (fg/often-nil (fg/reference)))] - (= (->> x - fhir-spec/unform-xml - (s2/conform :fhir.xml/Identifier)) - x)))) - - (testing "CBOR" - (satisfies-prop 20 - (prop/for-all [x (fg/identifier :assigner (fg/often-nil (fg/reference)))] - (= (->> (write-cbor x) - (parse-cbor "Identifier")) - x))))) - - (testing "parsing" - (testing "JSON" - (are [json fhir] (= fhir (write-parse-json "Identifier" json)) - {} - #fhir/Identifier{} - - {:use "usual"} - #fhir/Identifier{:use #fhir/code "usual"} - - {:value "value-151311"} - #fhir/Identifier{:value #fhir/string "value-151311"}) - - (testing "invalid" - (given (write-parse-json "Identifier" {:use 1}) - ::anom/category := ::anom/incorrect - ::anom/message := "Invalid JSON representation of a resource. Error on integer value 1. Expected type is `code`." - [:fhir/issues 0 :fhir.issues/code] := "invariant" - [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on integer value 1. Expected type is `code`." - [:fhir/issues 0 :fhir.issues/expression] := "Identifier.use")))) - - (testing "writing" - (testing "JSON" - (are [fhir json] (= json (write-read-json fhir)) - #fhir/Identifier{} - {} - - #fhir/Identifier{:id "id-155426"} - {:id "id-155426"} - - #fhir/Identifier{:extension [#fhir/Extension{}]} - {:extension [{}]} - - #fhir/Identifier{:extension [#fhir/Extension{} #fhir/Extension{}]} - {:extension [{} {}]} - - #fhir/Identifier{:use #fhir/code "use-155449"} - {:use "use-155449"} - - #fhir/Identifier{:type #fhir/CodeableConcept{}} - {:type {}} - - #fhir/Identifier{:system #fhir/uri "system-160011"} - {:system "system-160011"} - - #fhir/Identifier{:value #fhir/string "value-160034"} - {:value "value-160034"} - - #fhir/Identifier{:period #fhir/Period{}} - {:period {}} - - #fhir/Identifier{:assigner #fhir/Reference{}} - {:assigner {}})) - - (testing "XML" - (are [fhir xml] (= xml (fhir-spec/unform-xml fhir)) - #fhir/Identifier{} - (sexp []) - - #fhir/Identifier{:id "id-155426"} - (sexp [nil {:id "id-155426"}]) - - #fhir/Identifier{:extension [#fhir/Extension{}]} - (sexp [nil {} [::f/extension]]) - - #fhir/Identifier{:extension [#fhir/Extension{} #fhir/Extension{}]} - (sexp [nil {} [::f/extension] [::f/extension]]) - - #fhir/Identifier{:use #fhir/code "use-155449"} - (sexp [nil {} [::f/use {:value "use-155449"}]]) - - #fhir/Identifier{:type #fhir/CodeableConcept{}} - (sexp [nil {} [::f/type]]) - - #fhir/Identifier{:system #fhir/uri "system-160011"} - (sexp [nil {} [::f/system {:value "system-160011"}]]) - - #fhir/Identifier{:value #fhir/string "value-160034"} - (sexp [nil {} [::f/value {:value "value-160034"}]]) - - #fhir/Identifier{:period #fhir/Period{}} - (sexp [nil {} [::f/period]]) + #fhir/Period{:end #fhir/dateTime #system/date-time "2020"} + {:end "2020"})))) - #fhir/Identifier{:assigner #fhir/Reference{}} - (sexp [nil {} [::f/assigner]]))))) +(deftest ^:mem-size period-mem-size-test + (satisfies-prop 50 + (prop/for-all [source (gen/fmap write-cbor (fg/period))] + (>= (Base/memSize (parse-cbor "Period" source)) + (mem/total-size* (parse-cbor "Period" source) + (parse-cbor "Period" source))))) -(deftest human-name-test + (testing "examples" + (mem-size-test "Period" + {:start "2025"} 56 + {:start "2025" :end "2026"} 88))) + +(deftest quantity-test (testing "FHIR spec" (testing "valid" (satisfies-prop 100 - (prop/for-all [x (fg/human-name)] - (s2/valid? :fhir/HumanName x)))) - - (testing "invalid" - (are [x] (not (s2/valid? :fhir/HumanName x)) - #fhir/HumanName{:use "usual"}))) + (prop/for-all [x (fg/quantity)] + (s2/valid? :fhir/Quantity x))))) (testing "round-trip" (testing "JSON" (satisfies-prop 100 - (prop/for-all [x (fg/human-name)] + (prop/for-all [x (fg/quantity)] (= (->> (write-json x) - (parse-json "HumanName")) + (parse-json "Quantity")) (->> (write-json x) - (parse-json "HumanName") + (parse-json "Quantity") (write-json) - (parse-json "HumanName")) + (parse-json "Quantity")) x)))) (testing "XML" (satisfies-prop 100 - (prop/for-all [x (fg/human-name)] + (prop/for-all [x (fg/quantity)] (= (->> x fhir-spec/unform-xml - (s2/conform :fhir.xml/HumanName)) + (s2/conform :fhir.xml/Quantity)) x)))) (testing "CBOR" (satisfies-prop 100 - (prop/for-all [x (fg/human-name)] + (prop/for-all [x (fg/quantity)] (= (->> (write-cbor x) - (parse-cbor "HumanName")) + (parse-cbor "Quantity")) (->> (write-cbor x) - (parse-cbor "HumanName") + (parse-cbor "Quantity") (write-cbor) - (parse-cbor "HumanName")) + (parse-cbor "Quantity")) x))))) (testing "parsing" (testing "JSON" - (are [json fhir] (= fhir (write-parse-json "HumanName" json)) + (are [json fhir] (= fhir (write-parse-json "Quantity" json)) {} - #fhir/HumanName{} + #fhir/Quantity{} - {:use "usual"} - #fhir/HumanName{:use #fhir/code "usual"} + {:value 1M} + #fhir/Quantity{:value #fhir/decimal 1M}) - {:given ["given-212441"]} - #fhir/HumanName{:given [#fhir/string "given-212441"]} + (testing "invalid" + (given (write-parse-json "Quantity" {:value "1"}) + ::anom/category := ::anom/incorrect + ::anom/message := "Invalid JSON representation of a resource. Error on value `1`. Expected type is `decimal`." + [:fhir/issues 0 :fhir.issues/code] := "invariant" + [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on value `1`. Expected type is `decimal`." + [:fhir/issues 0 :fhir.issues/expression] := "Quantity.value")) - {:_given [{:extension [{:url "url-143610"}]}]} - #fhir/HumanName{:given [#fhir/string{:extension [#fhir/Extension{:url "url-143610"}]}]} + (testing "interned data elements" + (are [json key value] (identical? value (key (write-parse-json "Quantity" json))) + {:unit "151658"} :unit #fhir/string-interned "151658" + {:system "151641"} :system #fhir/uri-interned "151641"))) - {:given ["given-143625"] - :_given [{:extension [{:url "url-143619"}]}]} - #fhir/HumanName{:given [#fhir/string{:extension [#fhir/Extension{:url "url-143619"}] :value "given-143625"}]} + (testing "XML" + (testing "interned data elements" + (are [xml key value] (identical? value (key (s2/conform :fhir.xml/Quantity xml))) + (sexp [nil {} [:unit {:value "151938"}]]) :unit #fhir/string-interned "151938" + (sexp [nil {} [:system {:value "151921"}]]) :system #fhir/uri-interned "151921"))) - {:given ["given-212448" "given-212454"]} - #fhir/HumanName{:given [#fhir/string "given-212448" #fhir/string "given-212454"]} + (testing "CBOR" + (are [json fhir] (= fhir (write-parse-cbor "Quantity" json)) + {} + #fhir/Quantity{} - {:given ["given-143759" "given-143809"] - :_given [{:extension [{:url "url-143750"}]} {:extension [{:url "url-143806"}]}]} - #fhir/HumanName - {:given - [#fhir/string{:extension [#fhir/Extension{:url "url-143750"}] - :value "given-143759"} - #fhir/string{:extension [#fhir/Extension{:url "url-143806"}] - :value "given-143809"}]} + {:value 1M} + #fhir/Quantity{:value #fhir/decimal 1M}) - {:given ["given-143759" nil] - :_given [nil {:extension [{:url "url-143806"}]}]} - #fhir/HumanName - {:given - [#fhir/string "given-143759" - #fhir/string{:extension [#fhir/Extension{:url "url-143806"}]}]}) + (testing "interned data elements" + (are [cbor key value] (identical? value (key (write-parse-cbor "Quantity" cbor))) + {:unit "151658"} :unit #fhir/string-interned "151658" + {:system "151641"} :system #fhir/uri-interned "151641")) + + (testing "interning works" + (are [x y] (identical? x y) + (write-parse-cbor "Quantity" {:unit "unit-183017"}) + (write-parse-cbor "Quantity" {:unit "unit-183017"}) + + (write-parse-cbor "Quantity" {:system "system-151829"}) + (write-parse-cbor "Quantity" {:system "system-151829"}))) (testing "invalid" - (given (write-parse-json "HumanName" {:use 1}) + (given (write-parse-cbor "Quantity" {:value "1"}) ::anom/category := ::anom/incorrect - ::anom/message := "Invalid JSON representation of a resource. Error on integer value 1. Expected type is `code`." + ::anom/message := "Invalid JSON representation of a resource. Error on value `1`. Expected type is `decimal`." [:fhir/issues 0 :fhir.issues/code] := "invariant" - [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on integer value 1. Expected type is `code`." - [:fhir/issues 0 :fhir.issues/expression] := "HumanName.use"))) - - (testing "CBOR" - (are [cbor fhir] (= fhir (write-parse-cbor "HumanName" cbor)) - {} - #fhir/HumanName{} - - {:use "usual"} - #fhir/HumanName{:use #fhir/code "usual"} - - {:given ["given-212441"]} - #fhir/HumanName{:given [#fhir/string "given-212441"]}))) + [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on value `1`. Expected type is `decimal`." + [:fhir/issues 0 :fhir.issues/expression] := "Quantity.value")))) (testing "writing" (testing "JSON" (are [fhir json] (= json (write-read-json fhir)) - #fhir/HumanName{} + #fhir/Quantity{} {} - #fhir/HumanName{:id "id-155426"} - {:id "id-155426"} + #fhir/Quantity{:id "id-134908"} + {:id "id-134908"} - #fhir/HumanName{:extension [#fhir/Extension{}]} + #fhir/Quantity{:extension [#fhir/Extension{}]} {:extension [{}]} - #fhir/HumanName{:extension [#fhir/Extension{} #fhir/Extension{}]} + #fhir/Quantity{:extension [#fhir/Extension{} #fhir/Extension{}]} {:extension [{} {}]} - #fhir/HumanName{:use #fhir/code "use-155449"} - {:use "use-155449"} - - #fhir/HumanName{:text #fhir/string "text-141140"} - {:text "text-141140"} - - #fhir/HumanName{:family #fhir/string "family-141158"} - {:family "family-141158"} - - #fhir/HumanName{:given [#fhir/string "given-212441"]} - {:given ["given-212441"]} - - #fhir/HumanName{:given [#fhir/string{:extension [#fhir/Extension{:url "url-143610"}]}]} - {:_given [{:extension [{:url "url-143610"}]}]} - - #fhir/HumanName{:given [#fhir/string{:extension [#fhir/Extension{:url "url-143619"}] :value "given-143625"}]} - {:given ["given-143625"] - :_given [{:extension [{:url "url-143619"}]}]} - - #fhir/HumanName{:given [#fhir/string "given-212448" #fhir/string "given-212454"]} - {:given ["given-212448" "given-212454"]} - - #fhir/HumanName - {:given - [#fhir/string{:extension [#fhir/Extension{:url "url-143750"}] - :value "given-143759"} - #fhir/string{:extension [#fhir/Extension{:url "url-143806"}] - :value "given-143809"}]} - {:given ["given-143759" "given-143809"] - :_given [{:extension [{:url "url-143750"}]} {:extension [{:url "url-143806"}]}]} - - #fhir/HumanName - {:given - [#fhir/string "given-143759" - #fhir/string{:extension [#fhir/Extension{:url "url-143806"}]}]} - {:given ["given-143759" nil] - :_given [nil {:extension [{:url "url-143806"}]}]} - - #fhir/HumanName{:period #fhir/Period{}} - {:period {}})) - - (testing "XML" - (are [fhir xml] (= xml (fhir-spec/unform-xml fhir)) - #fhir/HumanName{} - (sexp []) - - #fhir/HumanName{:id "id-155426"} - (sexp [nil {:id "id-155426"}]) - - #fhir/HumanName{:extension [#fhir/Extension{}]} - (sexp [nil {} [::f/extension]]) - - #fhir/HumanName{:extension [#fhir/Extension{} #fhir/Extension{}]} - (sexp [nil {} [::f/extension] [::f/extension]]) + #fhir/Quantity{:value #fhir/decimal 1M} + {:value 1} - #fhir/HumanName{:use #fhir/code "use-155449"} - (sexp [nil {} [::f/use {:value "use-155449"}]]) + #fhir/Quantity{:comparator #fhir/code "code-153342"} + {:comparator "code-153342"} - #fhir/HumanName{:text #fhir/string "text-141140"} - (sexp [nil {} [::f/text {:value "text-141140"}]]) + #fhir/Quantity{:unit #fhir/string "string-153351"} + {:unit "string-153351"} - #fhir/HumanName{:family #fhir/string "family-141158"} - (sexp [nil {} [::f/family {:value "family-141158"}]]) + #fhir/Quantity{:system #fhir/uri "system-153337"} + {:system "system-153337"} - #fhir/HumanName{:given [#fhir/string "given-212441"]} - (sexp [nil {} [::f/given {:value "given-212441"}]]) + #fhir/Quantity{:code #fhir/code "code-153427"} + {:code "code-153427"})))) - #fhir/HumanName{:given [#fhir/string "given-212441" #fhir/string "given-170006"]} - (sexp [nil {} - [::f/given {:value "given-212441"}] - [::f/given {:value "given-170006"}]]) +(deftest ^:mem-size quantity-mem-size-test + (satisfies-prop 50 + (prop/for-all [source (gen/fmap write-cbor (fg/quantity))] + (>= (Base/memSize (parse-cbor "Quantity" source)) + (mem/total-size* (parse-cbor "Quantity" source) + (parse-cbor "Quantity" source))))) - #fhir/HumanName{:period #fhir/Period{}} - (sexp [nil {} [::f/period]]))))) + (testing "examples" + (mem-size-test "Quantity" + {:value 11} 80))) -(deftest address-test +(deftest range-test (testing "FHIR spec" (testing "valid" - (satisfies-prop 20 - (prop/for-all [x (fg/address)] - (s2/valid? :fhir/Address x)))) - - (testing "invalid" - (are [x] (not (s2/valid? :fhir/Address x)) - #fhir/Address{:use "usual"}))) + (satisfies-prop 100 + (prop/for-all [x (fg/range)] + (s2/valid? :fhir/Range x))))) (testing "round-trip" (testing "JSON" - (satisfies-prop 20 - (prop/for-all [x (fg/address)] + (satisfies-prop 100 + (prop/for-all [x (fg/range)] (= (->> (write-json x) - (parse-json "Address")) - (->> (write-json x) - (parse-json "Address") - (write-json) - (parse-json "Address")) + (parse-json "Range")) x)))) (testing "XML" - (satisfies-prop 20 - (prop/for-all [x (fg/address)] + (satisfies-prop 100 + (prop/for-all [x (fg/range)] (= (->> x fhir-spec/unform-xml - (s2/conform :fhir.xml/Address)) + (s2/conform :fhir.xml/Range)) x)))) (testing "CBOR" - (satisfies-prop 20 - (prop/for-all [x (fg/address)] + (satisfies-prop 100 + (prop/for-all [x (fg/range)] (= (->> (write-cbor x) - (parse-cbor "Address")) - (->> (write-cbor x) - (parse-cbor "Address") - (write-cbor) - (parse-cbor "Address")) + (parse-cbor "Range")) x))))) (testing "parsing" (testing "JSON" - (are [json fhir] (= fhir (write-parse-json "Address" json)) + (are [json fhir] (= fhir (write-parse-json "Range" json)) {} - #fhir/Address{} + #fhir/Range{} - {:use "usual"} - #fhir/Address{:use #fhir/code "usual"}) + {:id "id-151304"} + #fhir/Range{:id "id-151304"} + + {:extension [{}]} + #fhir/Range{:extension [#fhir/Extension{}]} + + {:low {:value 1M}} + #fhir/Range{:low #fhir/Quantity{:value #fhir/decimal 1M}}) (testing "invalid" - (given (write-parse-json "Address" {:use 1}) + (given (write-parse-json "Range" {:low "foo"}) ::anom/category := ::anom/incorrect - ::anom/message := "Invalid JSON representation of a resource. Error on integer value 1. Expected type is `code`." + ::anom/message := "Invalid JSON representation of a resource. Error on value `foo`. Expected type is `Quantity`." [:fhir/issues 0 :fhir.issues/code] := "invariant" - [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on integer value 1. Expected type is `code`." - [:fhir/issues 0 :fhir.issues/expression] := "Address.use")))) + [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on value `foo`. Expected type is `Quantity`." + [:fhir/issues 0 :fhir.issues/expression] := "Range.low")))) (testing "writing" (testing "JSON" (are [fhir json] (= json (write-read-json fhir)) - #fhir/Address{} + #fhir/Range{} {} - #fhir/Address{:id "id-155426"} - {:id "id-155426"} + #fhir/Range{:id "id-134428"} + {:id "id-134428"} - #fhir/Address{:extension [#fhir/Extension{}]} + #fhir/Range{:extension [#fhir/Extension{}]} {:extension [{}]} - #fhir/Address{:extension [#fhir/Extension{} #fhir/Extension{}]} + #fhir/Range{:extension [#fhir/Extension{} #fhir/Extension{}]} {:extension [{} {}]} - #fhir/Address{:use #fhir/code "use-155449"} - {:use "use-155449"} - - #fhir/Address{:text #fhir/string "text-170345"} - {:text "text-170345"} - - #fhir/Address{:line [#fhir/string "line-171433"]} - {:line ["line-171433"]} - - #fhir/Address{:line [#fhir/string "line-171433" #fhir/string "line-171857"]} - {:line ["line-171433" "line-171857"]} - - #fhir/Address{:city #fhir/string "city-171937"} - {:city "city-171937"} + #fhir/Range{:low #fhir/Quantity{:value #fhir/decimal 1M}} + {:low {:value 1}} - #fhir/Address{:district #fhir/string "district-171937"} - {:district "district-171937"} + #fhir/Range{:high #fhir/Quantity{:value #fhir/decimal 1M}} + {:high {:value 1}})))) - #fhir/Address{:state #fhir/string "state-171937"} - {:state "state-171937"} +(deftest ^:mem-size range-mem-size-test + (satisfies-prop 50 + (prop/for-all [source (gen/fmap write-cbor (fg/range))] + (>= (Base/memSize (parse-cbor "Range" source)) + (mem/total-size* (parse-cbor "Range" source) + (parse-cbor "Range" source))))) - #fhir/Address{:postalCode #fhir/string "postalCode-171937"} - {:postalCode "postalCode-171937"} + (testing "examples" + (mem-size-test "Range" + {:low {:value 11}} 104 + {:low {:value 11} :high {:value 12}} 184))) - #fhir/Address{:country #fhir/string "country-171937"} - {:country "country-171937"} +(deftest ratio-test + (testing "FHIR spec" + (testing "valid" + (satisfies-prop 100 + (prop/for-all [x (fg/ratio)] + (s2/valid? :fhir/Ratio x))))) - #fhir/Address{:period #fhir/Period{}} - {:period {}})) + (testing "round-trip" + (testing "JSON" + (satisfies-prop 100 + (prop/for-all [x (fg/ratio)] + (= (->> (write-json x) + (parse-json "Ratio")) + x)))) (testing "XML" - (are [fhir xml] (= xml (fhir-spec/unform-xml fhir)) - #fhir/Address{} - (sexp []) + (satisfies-prop 100 + (prop/for-all [x (fg/ratio)] + (= (->> x + fhir-spec/unform-xml + (s2/conform :fhir.xml/Ratio)) + x)))) - #fhir/Address{:id "id-155426"} - (sexp [nil {:id "id-155426"}]) + (testing "CBOR" + (satisfies-prop 100 + (prop/for-all [x (fg/ratio)] + (= (->> (write-cbor x) + (parse-cbor "Ratio")) + x))))) - #fhir/Address{:extension [#fhir/Extension{}]} - (sexp [nil {} [::f/extension]]) + (testing "parsing" + (testing "JSON" + (are [json fhir] (= fhir (write-parse-json "Ratio" json)) + {} + #fhir/Ratio{} - #fhir/Address{:extension [#fhir/Extension{} #fhir/Extension{}]} - (sexp [nil {} [::f/extension] [::f/extension]]) + {:id "id-151304"} + #fhir/Ratio{:id "id-151304"} - #fhir/Address{:use #fhir/code "use-155449"} - (sexp [nil {} [::f/use {:value "use-155449"}]]) + {:extension [{}]} + #fhir/Ratio{:extension [#fhir/Extension{}]} - #fhir/Address{:text #fhir/string "text-170345"} - (sexp [nil {} [::f/text {:value "text-170345"}]]) + {:numerator {:value 1M}} + #fhir/Ratio{:numerator #fhir/Quantity{:value #fhir/decimal 1M}}) - #fhir/Address{:line [#fhir/string "line-171433"]} - (sexp [nil {} [::f/line {:value "line-171433"}]]) + (testing "invalid" + (given (write-parse-json "Ratio" {:numerator "foo"}) + ::anom/category := ::anom/incorrect + ::anom/message := "Invalid JSON representation of a resource. Error on value `foo`. Expected type is `Quantity`." + [:fhir/issues 0 :fhir.issues/code] := "invariant" + [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on value `foo`. Expected type is `Quantity`." + [:fhir/issues 0 :fhir.issues/expression] := "Ratio.numerator")))) - #fhir/Address{:line [#fhir/string "line-171433" #fhir/string "line-171857"]} - (sexp [nil {} - [::f/line {:value "line-171433"}] - [::f/line {:value "line-171857"}]]) + (testing "writing" + (testing "JSON" + (are [fhir json] (= json (write-read-json fhir)) + #fhir/Ratio{} + {} - #fhir/Address{:city #fhir/string "city-171937"} - (sexp [nil {} [::f/city {:value "city-171937"}]]) + #fhir/Ratio{:id "id-134428"} + {:id "id-134428"} - #fhir/Address{:district #fhir/string "district-171937"} - (sexp [nil {} [::f/district {:value "district-171937"}]]) + #fhir/Ratio{:extension [#fhir/Extension{}]} + {:extension [{}]} - #fhir/Address{:state #fhir/string "state-171937"} - (sexp [nil {} [::f/state {:value "state-171937"}]]) + #fhir/Ratio{:extension [#fhir/Extension{} #fhir/Extension{}]} + {:extension [{} {}]} - #fhir/Address{:postalCode #fhir/string "postalCode-171937"} - (sexp [nil {} [::f/postalCode {:value "postalCode-171937"}]]) + #fhir/Ratio{:numerator #fhir/Quantity{:value #fhir/decimal 1M}} + {:numerator {:value 1}} - #fhir/Address{:country #fhir/string "country-171937"} - (sexp [nil {} [::f/country {:value "country-171937"}]]) + #fhir/Ratio{:denominator #fhir/Quantity{:value #fhir/decimal 1M}} + {:denominator {:value 1}})))) - #fhir/Address{:period #fhir/Period{}} - (sexp [nil {} [::f/period]]))))) +(deftest ^:mem-size ratio-mem-size-test + (satisfies-prop 50 + (prop/for-all [source (gen/fmap write-cbor (fg/ratio))] + (>= (Base/memSize (parse-cbor "Ratio" source)) + (mem/total-size* (parse-cbor "Ratio" source) + (parse-cbor "Ratio" source))))) + + (testing "examples" + (mem-size-test "Ratio" + {:numerator {:value 11}} 104 + {:numerator {:value 11} :denominator {:value 12}} 184))) (deftest reference-test (testing "FHIR spec" (testing "valid" (satisfies-prop 100 (prop/for-all [x (fg/reference)] - (s2/valid? :fhir/Reference x)))) - - (testing "invalid" - (are [x] (not (s2/valid? :fhir/Reference x)) - #fhir/Reference{:reference 1}))) + (s2/valid? :fhir/Reference x))))) (testing "round-trip" (testing "JSON" @@ -3513,6 +5333,10 @@ (prop/for-all [x (fg/reference)] (= (->> (write-json x) (parse-json "Reference")) + (->> (write-json x) + (parse-json "Reference") + (write-json) + (parse-json "Reference")) x)))) (testing "XML" @@ -3528,6 +5352,10 @@ (prop/for-all [x (fg/reference)] (= (->> (write-cbor x) (parse-cbor "Reference")) + (->> (write-cbor x) + (parse-cbor "Reference") + (write-cbor) + (parse-cbor "Reference")) x))))) (testing "parsing" @@ -3574,196 +5402,367 @@ #fhir/Reference{:display #fhir/string "display-161314"} {:display "display-161314"})))) -(deftest meta-test +(deftest ^:mem-size reference-mem-size-test + (satisfies-prop 50 + (prop/for-all [source (gen/fmap write-cbor (fg/reference))] + (>= (Base/memSize (parse-cbor "Reference" source)) + (mem/total-size* (parse-cbor "Reference" source) + (parse-cbor "Reference" source))))) + + (testing "examples" + (mem-size-test "Reference" + {:reference "reference-171612"} 104))) + +(deftest related-artifact-test (testing "FHIR spec" (testing "valid" - (satisfies-prop 20 - (prop/for-all [x (fg/meta)] - (s2/valid? :fhir/Meta x)))) + (satisfies-prop 100 + (prop/for-all [x (fg/related-artifact)] + (s2/valid? :fhir/RelatedArtifact x))))) + + (testing "round-trip" + (testing "JSON" + (satisfies-prop 100 + (prop/for-all [x (fg/related-artifact)] + (= (->> (write-json x) + (parse-json "RelatedArtifact")) + (->> (write-json x) + (parse-json "RelatedArtifact") + (write-json) + (parse-json "RelatedArtifact")) + x)))) + + (testing "XML" + (satisfies-prop 100 + (prop/for-all [x (fg/related-artifact)] + (= (->> x + fhir-spec/unform-xml + (s2/conform :fhir.xml/RelatedArtifact)) + x)))) + + (testing "CBOR" + (satisfies-prop 100 + (prop/for-all [x (fg/related-artifact)] + (= (->> (write-cbor x) + (parse-cbor "RelatedArtifact")) + (->> (write-cbor x) + (parse-cbor "RelatedArtifact") + (write-cbor) + (parse-cbor "RelatedArtifact")) + x)))))) + +(deftest ^:mem-size related-artifact-mem-size-test + (satisfies-prop 50 + (prop/for-all [source (gen/fmap write-cbor (fg/related-artifact))] + (>= (Base/memSize (parse-cbor "RelatedArtifact" source)) + (mem/total-size* (parse-cbor "RelatedArtifact" source) + (parse-cbor "RelatedArtifact" source))))) - (testing "invalid" - (are [x] (not (s2/valid? :fhir/Meta x)) - #fhir/Identifier{:versionId "1"}))) + (testing "examples" + (mem-size-test "RelatedArtifact" + {:label "label-175537"} 104))) + +(deftest sampled-data-test + (testing "FHIR spec" + (testing "valid" + (satisfies-prop 100 + (prop/for-all [x (fg/sampled-data)] + (s2/valid? :fhir/SampledData x))))) (testing "round-trip" (testing "JSON" - (satisfies-prop 20 - (prop/for-all [x (fg/meta)] + (satisfies-prop 100 + (prop/for-all [x (fg/sampled-data)] (= (->> (write-json x) - (parse-json "Meta")) + (parse-json "SampledData")) + (->> (write-json x) + (parse-json "SampledData") + (write-json) + (parse-json "SampledData")) x)))) (testing "XML" - (satisfies-prop 20 - (prop/for-all [x (fg/meta)] + (satisfies-prop 100 + (prop/for-all [x (fg/sampled-data)] (= (->> x fhir-spec/unform-xml - (s2/conform :fhir.xml/Meta)) + (s2/conform :fhir.xml/SampledData)) x)))) (testing "CBOR" - (satisfies-prop 20 - (prop/for-all [x (fg/meta)] + (satisfies-prop 100 + (prop/for-all [x (fg/sampled-data)] (= (->> (write-cbor x) - (parse-cbor "Meta")) + (parse-cbor "SampledData")) + (->> (write-cbor x) + (parse-cbor "SampledData") + (write-cbor) + (parse-cbor "SampledData")) + x)))))) + +(deftest ^:mem-size sampled-data-mem-size-test + (satisfies-prop 50 + (prop/for-all [source (gen/fmap write-cbor (fg/sampled-data))] + (>= (Base/memSize (parse-cbor "SampledData" source)) + (mem/total-size* (parse-cbor "SampledData" source) + (parse-cbor "SampledData" source))))) + + (testing "examples" + (mem-size-test "SampledData" + {:data "data-161138"} 104))) + +(deftest signature-test + (testing "FHIR spec" + (testing "valid" + (satisfies-prop 100 + (prop/for-all [x (fg/signature)] + (s2/valid? :fhir/Signature x))))) + + (testing "round-trip" + (testing "JSON" + (satisfies-prop 100 + (prop/for-all [x (fg/signature :repeat (gen/return nil))] + (= (->> (write-json x) + (parse-json "Signature")) + x)))) + + (testing "XML" + (satisfies-prop 100 + (prop/for-all [x (fg/signature)] + (= (->> x + fhir-spec/unform-xml + (s2/conform :fhir.xml/Signature)) + x)))) + + (testing "CBOR" + (satisfies-prop 100 + (prop/for-all [x (fg/signature)] + (= (->> (write-cbor x) + (parse-cbor "Signature")) x))))) (testing "parsing" (testing "JSON" - (are [json fhir] (= fhir (write-parse-json "Meta" json)) + (are [json fhir] (= fhir (write-parse-json "Signature" json)) {} - #fhir/Meta{} + #fhir/Signature{} - {:versionId "1"} - #fhir/Meta{:versionId #fhir/id "1"} + {:id "id-151304"} + #fhir/Signature{:id "id-151304"} - {:lastUpdated "1970-01-01T00:00:00Z"} - (type/meta {:lastUpdated Instant/EPOCH})))) + {:extension [{}]} + #fhir/Signature{:extension [#fhir/Extension{}]} + + {:type [{:code "code-114122"}]} + #fhir/Signature{:type [#fhir/Coding{:code #fhir/code "code-114122"}]}) + + (testing "invalid" + (given (write-parse-json "Signature" {:type "foo"}) + ::anom/category := ::anom/incorrect + ::anom/message := "Invalid JSON representation of a resource. Error on value `foo`. Expected type is `Coding`." + [:fhir/issues 0 :fhir.issues/code] := "invariant" + [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on value `foo`. Expected type is `Coding`." + [:fhir/issues 0 :fhir.issues/expression] := "Signature.type")))) (testing "writing" (testing "JSON" (are [fhir json] (= json (write-read-json fhir)) - #fhir/Meta{} + #fhir/Signature{} {} - #fhir/Meta{:id "id-155426"} - {:id "id-155426"} + #fhir/Signature{:id "id-134428"} + {:id "id-134428"} - #fhir/Meta{:extension [#fhir/Extension{}]} + #fhir/Signature{:extension [#fhir/Extension{}]} {:extension [{}]} - #fhir/Meta{:extension [#fhir/Extension{} #fhir/Extension{}]} + #fhir/Signature{:extension [#fhir/Extension{} #fhir/Extension{}]} {:extension [{} {}]} - #fhir/Meta{:versionId #fhir/id "versionId-161812"} - {:versionId "versionId-161812"} + #fhir/Signature{:type [#fhir/Coding{:code #fhir/code "code-114122"}]} + {:type [{:code "code-114122"}]})))) - (type/meta {:lastUpdated Instant/EPOCH}) - {:lastUpdated "1970-01-01T00:00:00Z"} +(deftest timing-repeat-test + (testing "FHIR spec" + (testing "valid" + (satisfies-prop 100 + (prop/for-all [x (fg/timing-repeat)] + (s2/valid? :fhir.Timing/repeat x))))) - #fhir/Meta{:source #fhir/uri "source-162704"} - {:source "source-162704"} + (testing "round-trip" + (testing "JSON" + (satisfies-prop 100 + (prop/for-all [x (fg/timing-repeat)] + (= (->> (write-json x) + (parse-json "Timing.repeat")) + (->> (write-json x) + (parse-json "Timing.repeat") + (write-json) + (parse-json "Timing.repeat")) + x)))) - #fhir/Meta{:profile [#fhir/canonical "profile-uri-145024"]} - {:profile ["profile-uri-145024"]} + (testing "XML" + (satisfies-prop 100 + (prop/for-all [x (fg/timing-repeat)] + (= (->> x + (s2/unform :fhir.xml.Timing/repeat) + (s2/conform :fhir.xml.Timing/repeat)) + x)))) - #fhir/Meta{:security [#fhir/Coding{}]} - {:security [{}]} + (testing "CBOR" + (satisfies-prop 100 + (prop/for-all [x (fg/timing-repeat)] + (= (->> (write-cbor x) + (parse-cbor "Timing.repeat")) + (->> (write-cbor x) + (parse-cbor "Timing.repeat") + (write-cbor) + (parse-cbor "Timing.repeat")) + x)))))) - #fhir/Meta{:security [#fhir/Coding{} #fhir/Coding{}]} - {:security [{} {}]} +(deftest ^:mem-size timing-repeat-mem-size-test + (satisfies-prop 50 + (prop/for-all [source (gen/fmap write-cbor (fg/timing-repeat))] + (>= (Base/memSize (parse-cbor "Timing.repeat" source)) + (mem/total-size* (parse-cbor "Timing.repeat" source) + (parse-cbor "Timing.repeat" source))))) - #fhir/Meta{:tag [#fhir/Coding{}]} - {:tag [{}]} + (testing "examples" + (mem-size-test "Timing.repeat" + {:count 1} 88))) - #fhir/Meta{:tag [#fhir/Coding{} #fhir/Coding{}]} - {:tag [{} {}]})))) +(deftest timing-test + (testing "FHIR spec" + (testing "valid" + (satisfies-prop 100 + (prop/for-all [x (fg/timing)] + (s2/valid? :fhir/Timing x))))) -(deftest bundle-entry-search-test (testing "round-trip" (testing "JSON" (satisfies-prop 100 - (prop/for-all [x (fg/bundle-entry-search)] + (prop/for-all [x (fg/timing :repeat (gen/return nil))] (= (->> (write-json x) - (parse-json "Bundle.entry.search")) + (parse-json "Timing")) x)))) (testing "XML" (satisfies-prop 100 - (prop/for-all [x (fg/bundle-entry-search)] + (prop/for-all [x (fg/timing)] (= (->> x - (s2/unform :fhir.xml.Bundle.entry/search) - (s2/conform :fhir.xml.Bundle.entry/search)) + fhir-spec/unform-xml + (s2/conform :fhir.xml/Timing)) x)))) (testing "CBOR" (satisfies-prop 100 - (prop/for-all [x (fg/bundle-entry-search)] + (prop/for-all [x (fg/timing)] (= (->> (write-cbor x) - (parse-cbor "Bundle.entry.search")) + (parse-cbor "Timing")) x))))) (testing "parsing" (testing "JSON" - (are [json fhir] (= fhir (write-parse-json "Bundle.entry.search" json)) + (are [json fhir] (= fhir (write-parse-json "Timing" json)) {} - #fhir/BundleEntrySearch{} - - {:id "id-134805"} - #fhir/BundleEntrySearch{:id "id-134805"} - - {:mode "match"} - #fhir/BundleEntrySearch{:mode #fhir/code "match"}) - - (testing "invalid" - (given (write-parse-json "Bundle.entry.search" {:id 1}) - ::anom/category := ::anom/incorrect - ::anom/message := "Invalid JSON representation of a resource. Error on integer value 1. Expected type is `string`." - [:fhir/issues 0 :fhir.issues/code] := "invariant" - [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on integer value 1. Expected type is `string`." - [:fhir/issues 0 :fhir.issues/expression] := "Bundle.entry.search.id"))) + #fhir/Timing{} - (testing "CBOR" - (are [cbor fhir] (= fhir (write-parse-cbor "Bundle.entry.search" cbor)) - {} - #fhir/BundleEntrySearch{} + {:id "id-151304"} + #fhir/Timing{:id "id-151304"} - {:id "id-134805"} - #fhir/BundleEntrySearch{:id "id-134805"} + {:extension [{}]} + #fhir/Timing{:extension [#fhir/Extension{}]} - {:mode "match"} - #fhir/BundleEntrySearch{:mode #fhir/code "match"}) + {:event ["2025"]} + #fhir/Timing{:event [#fhir/dateTime #system/date-time "2025"]}) (testing "invalid" - (given (write-parse-cbor "Bundle.entry.search" {:id 1}) + (given (write-parse-json "Timing" {:event "foo"}) ::anom/category := ::anom/incorrect - ::anom/message := "Invalid JSON representation of a resource. Error on integer value 1. Expected type is `string`." + ::anom/message := "Invalid JSON representation of a resource. Error on value `foo`. Expected type is `date-time`. Text cannot be parsed to a DateTime" [:fhir/issues 0 :fhir.issues/code] := "invariant" - [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on integer value 1. Expected type is `string`." - [:fhir/issues 0 :fhir.issues/expression] := "Bundle.entry.search.id")))) + [:fhir/issues 0 :fhir.issues/diagnostics] := "Error on value `foo`. Expected type is `date-time`. Text cannot be parsed to a DateTime" + [:fhir/issues 0 :fhir.issues/expression] := "Timing.event")))) (testing "writing" (testing "JSON" (are [fhir json] (= json (write-read-json fhir)) - #fhir/BundleEntrySearch{} + #fhir/Timing{} {} - #fhir/BundleEntrySearch{:id "id-115229"} - {:id "id-115229"} + #fhir/Timing{:id "id-134428"} + {:id "id-134428"} - #fhir/BundleEntrySearch{:extension [#fhir/Extension{}]} + #fhir/Timing{:extension [#fhir/Extension{}]} {:extension [{}]} - #fhir/BundleEntrySearch{:extension [#fhir/Extension{} #fhir/Extension{}]} + #fhir/Timing{:extension [#fhir/Extension{} #fhir/Extension{}]} {:extension [{} {}]} - #fhir/BundleEntrySearch{:mode #fhir/code "match"} - {:mode "match"} - - #fhir/BundleEntrySearch{:score #fhir/decimal 1.1M} - {:score 1.1})) + #fhir/Timing{:event [#fhir/dateTime #system/date-time "2025"]} + {:event ["2025"]})))) - (testing "CBOR" - (are [fhir cbor] (= cbor (read-cbor (write-cbor fhir))) - #fhir/BundleEntrySearch{} - {} +(deftest ^:mem-size timing-mem-size-test + (satisfies-prop 50 + (prop/for-all [source (gen/fmap write-cbor (fg/timing))] + (>= (Base/memSize (parse-cbor "Timing" source)) + (mem/total-size* (parse-cbor "Timing" source) + (parse-cbor "Timing" source))))) - #fhir/BundleEntrySearch{:id "id-115229"} - {:id "id-115229"} + (testing "examples" + (mem-size-test "Timing" + {:event ["2025"]} 120))) - #fhir/BundleEntrySearch{:extension [#fhir/Extension{}]} - {:extension [{}]} +(deftest trigger-definition-test + (testing "FHIR spec" + (testing "valid" + (satisfies-prop 20 + (prop/for-all [x (fg/trigger-definition)] + (s2/valid? :fhir/TriggerDefinition x))))) - #fhir/BundleEntrySearch{:extension [#fhir/Extension{} #fhir/Extension{}]} - {:extension [{} {}]} + (testing "round-trip" + (testing "JSON" + (satisfies-prop 20 + (prop/for-all [x (fg/trigger-definition)] + (= (->> (write-json x) + (parse-json "TriggerDefinition")) + (->> (write-json x) + (parse-json "TriggerDefinition") + (write-json) + (parse-json "TriggerDefinition")) + x)))) - #fhir/BundleEntrySearch{:mode #fhir/code "match"} - {:mode "match"} + (testing "XML" + (satisfies-prop 20 + (prop/for-all [x (fg/trigger-definition)] + (= (->> x + fhir-spec/unform-xml + (s2/conform :fhir.xml/TriggerDefinition)) + x)))) - #fhir/BundleEntrySearch{:score #fhir/decimal 1.1M} - {:score 1.1M})))) + (testing "CBOR" + (satisfies-prop 20 + (prop/for-all [x (fg/trigger-definition)] + (= (->> (write-cbor x) + (parse-cbor "TriggerDefinition")) + (->> (write-cbor x) + (parse-cbor "TriggerDefinition") + (write-cbor) + (parse-cbor "TriggerDefinition")) + x)))))) + +(deftest ^:mem-size trigger-definition-mem-size-test + (satisfies-prop 20 + (prop/for-all [source (gen/fmap write-cbor (fg/trigger-definition))] + (>= (Base/memSize (parse-cbor "TriggerDefinition" source)) + (mem/total-size* (parse-cbor "TriggerDefinition" source) + (parse-cbor "TriggerDefinition" source))))) -(deftest bundle-entry-reference-test + (testing "examples" + (mem-size-test "TriggerDefinition" + {:type "named-event"} 32))) + +(deftest bundle-entry-resource-test (testing "round-trip" (testing "JSON" (satisfies-prop 100 @@ -3806,8 +5805,187 @@ :reference (gen/return #fhir/string "Patient/0"))))] (empty? (type/references x)))))) +(deftest ^:mem-size bundle-entry-resource-mem-size-test + (satisfies-prop 50 + (prop/for-all [source (gen/fmap write-cbor (fg/bundle-entry))] + (>= (Base/memSize (parse-cbor "Bundle.entry" source)) + (mem/total-size* (parse-cbor "Bundle.entry" source) + (parse-cbor "Bundle.entry" source))))) + + (testing "examples" + (are [x size] (= (mem/total-size-exclude x :fhir/type :fhir.Bundle/entry :fullUrl) + (Base/memSize x) size) + (write-parse-json "Bundle.entry" {:fullUrl "uri-155734"}) 120))) + ;; ---- Resources ------------------------------------------------------------- +(defn- murmur3 [x] + (let [hasher (.newHasher (Hashing/murmur3_32_fixed))] + (Base/hashInto x hasher) + (Integer/toHexString (.asInt (.hash hasher))))) + +(deftest activity-definition-test + (testing "round-trip" + (testing "JSON" + (satisfies-prop 20 + (prop/for-all [activity-definition (fg/activity-definition)] + (= (->> (write-json activity-definition) + (parse-json "ActivityDefinition")) + (->> (write-json activity-definition) + (parse-json "ActivityDefinition") + (write-json) + (parse-json "ActivityDefinition")) + activity-definition)))) + + (testing "XML" + (satisfies-prop 20 + (prop/for-all [activity-definition (fg/activity-definition)] + (= (-> activity-definition + fhir-spec/unform-xml + fhir-spec/conform-xml) + activity-definition)))) + + (testing "CBOR" + (satisfies-prop 20 + (prop/for-all [activity-definition (fg/activity-definition)] + (= (->> (write-cbor activity-definition) + (parse-cbor "ActivityDefinition")) + (->> (write-cbor activity-definition) + (parse-cbor "ActivityDefinition") + (write-cbor) + (parse-cbor "ActivityDefinition")) + activity-definition))))) + + (testing "hash-into" + (satisfies-prop 20 + (prop/for-all [activity-definition (fg/activity-definition)] + (murmur3 activity-definition))))) + +(deftest ^:mem-size activity-definition-mem-size-test + (satisfies-prop 20 + (prop/for-all [source (gen/fmap write-cbor (fg/activity-definition))] + (>= (Base/memSize (parse-cbor "ActivityDefinition" source)) + (mem/total-size* (parse-cbor "ActivityDefinition" source) + (parse-cbor "ActivityDefinition" source)))))) + +(deftest allergy-intolerance-test + (testing "FHIR spec" + (testing "valid" + (satisfies-prop 20 + (prop/for-all [x (fg/allergy-intolerance)] + (s2/valid? :fhir/AllergyIntolerance x))))) + + (testing "round-trip" + (testing "JSON" + (satisfies-prop 20 + (prop/for-all [allergy-intolerance (fg/allergy-intolerance)] + (= (->> (write-json allergy-intolerance) + (parse-json "AllergyIntolerance")) + allergy-intolerance)))) + + (testing "XML" + (satisfies-prop 20 + (prop/for-all [allergy-intolerance (fg/allergy-intolerance)] + (= (-> allergy-intolerance + fhir-spec/unform-xml + fhir-spec/conform-xml) + allergy-intolerance)))) + + (testing "CBOR" + (satisfies-prop 20 + (prop/for-all [allergy-intolerance (fg/allergy-intolerance)] + (= (->> (write-cbor allergy-intolerance) + (parse-cbor "AllergyIntolerance")) + allergy-intolerance))))) + + (testing "hash-into" + (satisfies-prop 20 + (prop/for-all [allergy-intolerance (fg/allergy-intolerance)] + (murmur3 allergy-intolerance))))) + +(deftest ^:mem-size allergy-intolerance-mem-size-test + (satisfies-prop 20 + (prop/for-all [source (gen/fmap write-cbor (fg/allergy-intolerance))] + (>= (Base/memSize (parse-cbor "AllergyIntolerance" source)) + (mem/total-size* (parse-cbor "AllergyIntolerance" source) + (parse-cbor "AllergyIntolerance" source)))))) + +(def ^:private code-system-non-summary-properties + #{:description :purpose :copyright :concept}) + +(deftest bundle-test + (testing "round-trip" + (testing "JSON" + (satisfies-prop 20 + (prop/for-all [bundle (fg/bundle)] + (= (->> (write-json bundle) + (parse-json "Bundle")) + (->> (write-json bundle) + (parse-json "Bundle") + (write-json) + (parse-json "Bundle")) + bundle)))) + + (testing "XML" + (satisfies-prop 20 + (prop/for-all [bundle (fg/bundle)] + (= (-> bundle + fhir-spec/unform-xml + fhir-spec/conform-xml) + bundle)))) + + (testing "CBOR" + (satisfies-prop 20 + (prop/for-all [bundle (fg/bundle)] + (= (->> (write-cbor bundle) + (parse-cbor "Bundle")) + (->> (write-cbor bundle) + (parse-cbor "Bundle") + (write-cbor) + (parse-cbor "Bundle")) + bundle))))) + + (testing "interned data elements" + (are [json path value] (identical? value (get-in (write-parse-json "Bundle" json) path)) + {:link [{:relation "141802"}]} [:link 0 :relation] #fhir/string-interned "141802" + {:entry [{:response {:status "200"}}]} [:entry 0 :response :status] #fhir/string-interned "200")) + + (testing "writing" + (testing "JSON" + (is (= (write-read-json {:fhir/type :fhir/Bundle}) + {:resourceType "Bundle"})) + + (is (= (write-read-json + {:fhir/type :fhir/Bundle + :entry + [{:fhir/type :fhir.Bundle/entry + :fullUrl #fhir/uri "url-104116"}]}) + {:resourceType "Bundle" :entry [{:fullUrl "url-104116"}]})) + + (is (= (write-read-json + {:fhir/type :fhir/Bundle + :entry + [{:fhir/type :fhir.Bundle/entry + :resource {:fhir/type :fhir/Patient}}]}) + {:resourceType "Bundle" :entry [{:resource {:resourceType "Patient"}}]})))) + + (testing "hash-into" + (satisfies-prop 20 + (prop/for-all [bundle (fg/bundle)] + (murmur3 bundle))))) + +(deftest ^:mem-size bundle-mem-size-test + (satisfies-prop 20 + (prop/for-all [source (gen/fmap write-cbor (fg/bundle))] + (>= (Base/memSize (parse-cbor "Bundle" source)) + (mem/total-size* (parse-cbor "Bundle" source) + (parse-cbor "Bundle" source))))) + + (testing "examples" + (are [x size] (= (mem/total-size-exclude x :fhir/type :fhir/Bundle :total) + (Base/memSize x) size) + (write-parse-json "Bundle" {:total 1}) 72))) + (deftest capability-statement-test (testing "writing" (testing "JSON" @@ -3821,98 +5999,289 @@ [{:searchParam [{:name "name-151346"}]}]}))))) -(deftest bundle-test +(deftest claim-test (testing "round-trip" (testing "JSON" (satisfies-prop 20 - (prop/for-all [bundle (fg/bundle)] - (= (->> (write-json bundle) - (parse-json "Bundle")) - (->> (write-json bundle) - (parse-json "Bundle") - (write-json) - (parse-json "Bundle")) - bundle)))) + (prop/for-all [claim (fg/claim)] + (= (->> (write-json claim) + (parse-json "Claim")) + claim)))) + + (testing "XML" + (satisfies-prop 20 + (prop/for-all [claim (fg/claim)] + (= (-> claim + fhir-spec/unform-xml + fhir-spec/conform-xml) + claim)))) + + (testing "CBOR" + (satisfies-prop 20 + (prop/for-all [claim (fg/claim)] + (= (->> (write-cbor claim) + (parse-cbor "Claim")) + claim))))) + + (testing "parsing" + (testing "JSON" + (are [json fhir] (= fhir (write-parse-json "Claim" json)) + {:resourceType "Claim"} + {:fhir/type :fhir/Claim} + + {:resourceType "Claim" + :total {:value 1.0M :currency "EUR"}} + {:fhir/type :fhir/Claim + :total #fhir/Money{:value #fhir/decimal 1.0M :currency #fhir/code"EUR"}}))) + + (testing "writing" + (testing "JSON" + (is (= (write-read-json + {:fhir/type :fhir/Claim + :total #fhir/Money{:value #fhir/decimal 1.0M :currency #fhir/code"EUR"}}) + {:resourceType "Claim" + :total {:value 1.0 :currency "EUR"}})))) + + (testing "hash-into" + (satisfies-prop 20 + (prop/for-all [claim (fg/claim)] + (murmur3 claim))))) + +(deftest ^:mem-size claim-mem-size-test + (satisfies-prop 20 + (prop/for-all [source (gen/fmap write-cbor (fg/claim))] + (>= (Base/memSize (parse-cbor "Claim" source)) + (mem/total-size* (parse-cbor "Claim" source) + (parse-cbor "Claim" source)))))) + +(deftest code-system-test + (testing "FHIR spec" + (testing "valid" + (satisfies-prop 20 + (prop/for-all [x (fg/code-system)] + (s2/valid? :fhir/CodeSystem x))))) + + (testing "summary parsing" + (satisfies-prop 20 + (prop/for-all [code-system (fg/code-system)] + (let [source (write-cbor code-system) + code-system (parse-cbor "CodeSystem" source :summary)] + (and + (->> code-system :meta :tag (some fu/subsetted?)) + (not-any? code-system-non-summary-properties (keys code-system))))))) + + (testing "hash-into" + (satisfies-prop 20 + (prop/for-all [code-system (fg/code-system)] + (murmur3 code-system))))) + +(deftest ^:mem-size code-system-mem-size-test + (satisfies-prop 20 + (prop/for-all [source (gen/fmap write-cbor (fg/code-system))] + (>= (Base/memSize (parse-cbor "CodeSystem" source)) + (mem/total-size* (parse-cbor "CodeSystem" source) + (parse-cbor "CodeSystem" source)))))) + +(def ^:private value-set-non-summary-properties + #{:description :purpose :copyright :compose :expansion}) + +(deftest condition-test + (testing "FHIR spec" + (testing "valid" + (satisfies-prop 20 + (prop/for-all [x (fg/condition)] + (s2/valid? :fhir/Condition x))))) + + (testing "round-trip" + (testing "JSON" + (satisfies-prop 20 + (prop/for-all [condition (fg/condition)] + (= (->> (write-json condition) + (parse-json "Condition")) + condition)))) + + (testing "XML" + (satisfies-prop 20 + (prop/for-all [condition (fg/condition)] + (= (-> condition + fhir-spec/unform-xml + fhir-spec/conform-xml) + condition)))) + + (testing "CBOR" + (satisfies-prop 20 + (prop/for-all [condition (fg/condition)] + (= (->> (write-cbor condition) + (parse-cbor "Condition")) + condition))))) + + (testing "writing" + (testing "JSON" + (is (= (write-read-json + {:fhir/type :fhir/Condition + :onset #fhir/Age{:value #fhir/decimal 23M :code #fhir/code "a"}}) + {:resourceType "Condition" + :onsetAge {:value 23 :code "a"}})))) + + (testing "references" + (are [x refs] (= refs (type/references x)) + {:fhir/type :fhir/Condition + :subject #fhir/Reference{:reference #fhir/string "Patient/0"}} + [["Patient" "0"]])) + + (testing "hash-into" + (satisfies-prop 20 + (prop/for-all [condition (fg/condition)] + (murmur3 condition))))) + +(deftest ^:mem-size condition-mem-size-test + (satisfies-prop 20 + (prop/for-all [source (gen/fmap write-cbor (fg/condition))] + (>= (Base/memSize (parse-cbor "Condition" source)) + (mem/total-size* (parse-cbor "Condition" source) + (parse-cbor "Condition" source)))))) + +(deftest consent-test + (testing "round-trip" + (testing "JSON" + (satisfies-prop 20 + (prop/for-all [consent (fg/consent)] + (= (->> (write-json consent) + (parse-json "Consent")) + consent)))) + + (testing "XML" + (satisfies-prop 20 + (prop/for-all [consent (fg/consent)] + (= (-> consent + fhir-spec/unform-xml + fhir-spec/conform-xml) + consent)))) + + (testing "CBOR" + (satisfies-prop 20 + (prop/for-all [consent (fg/consent)] + (= (->> (write-cbor consent) + (parse-cbor "Consent")) + consent))))) + + (testing "parsing" + (testing "JSON" + (are [json fhir] (= fhir (write-parse-json "Consent" json)) + {:resourceType "Consent" + :policyRule {}} + {:fhir/type :fhir/Consent + :policyRule #fhir/CodeableConcept{}}))) + + (testing "writing" + (testing "JSON" + (is (= (write-read-json + {:fhir/type :fhir/Consent + :policyRule #fhir/CodeableConcept{}}) + {:resourceType "Consent" + :policyRule {}})))) + + (testing "hash-into" + (satisfies-prop 20 + (prop/for-all [consent (fg/consent)] + (murmur3 consent))))) + +(deftest ^:mem-size consent-mem-size-test + (satisfies-prop 20 + (prop/for-all [source (gen/fmap write-cbor (fg/consent))] + (>= (Base/memSize (parse-cbor "Consent" source)) + (mem/total-size* (parse-cbor "Consent" source) + (parse-cbor "Consent" source)))))) + +(deftest imaging-study-test + (testing "round-trip" + (testing "JSON" + (satisfies-prop 20 + (prop/for-all [imaging-study (fg/imaging-study)] + (= (->> (write-json imaging-study) + (parse-json "ImagingStudy")) + imaging-study)))) (testing "XML" (satisfies-prop 20 - (prop/for-all [bundle (fg/bundle)] - (= (-> bundle + (prop/for-all [imaging-study (fg/imaging-study)] + (= (-> imaging-study fhir-spec/unform-xml fhir-spec/conform-xml) - bundle)))) + imaging-study)))) (testing "CBOR" (satisfies-prop 20 - (prop/for-all [bundle (fg/bundle)] - (= (->> (write-cbor bundle) - (parse-cbor "Bundle")) - (->> (write-cbor bundle) - (parse-cbor "Bundle") - (write-cbor) - (parse-cbor "Bundle")) - bundle))))) + (prop/for-all [imaging-study (fg/imaging-study)] + (= (->> (write-cbor imaging-study) + (parse-cbor "ImagingStudy")) + imaging-study))))) - (testing "writing" - (testing "JSON" - (is (= (write-read-json {:fhir/type :fhir/Bundle}) - {:resourceType "Bundle"})) + (testing "summary parsing" + (satisfies-prop 20 + (prop/for-all [imaging-study (fg/imaging-study)] + (let [source (write-cbor imaging-study) + imaging-study (parse-cbor "ImagingStudy" source :summary)] + (and + (->> imaging-study :meta :tag (some fu/subsetted?)) + (not-any? :data (:content imaging-study))))))) - (is (= (write-read-json - {:fhir/type :fhir/Bundle - :entry - [{:fhir/type :fhir.Bundle/entry - :fullUrl #fhir/uri "url-104116"}]}) - {:resourceType "Bundle" :entry [{:fullUrl "url-104116"}]})) + (testing "hash-into" + (satisfies-prop 20 + (prop/for-all [imaging-study (fg/imaging-study)] + (murmur3 imaging-study))))) - (is (= (write-read-json - {:fhir/type :fhir/Bundle - :entry - [{:fhir/type :fhir.Bundle/entry - :resource {:fhir/type :fhir/Patient}}]}) - {:resourceType "Bundle" :entry [{:resource {:resourceType "Patient"}}]}))))) +(deftest ^:mem-size imaging-study-mem-size-test + (satisfies-prop 20 + (prop/for-all [source (gen/fmap write-cbor (fg/imaging-study))] + (>= (Base/memSize (parse-cbor "ImagingStudy" source)) + (mem/total-size* (parse-cbor "ImagingStudy" source) + (parse-cbor "ImagingStudy" source)))))) -(deftest patient-test +(deftest library-test (testing "round-trip" (testing "JSON" - (satisfies-prop 100 - (prop/for-all [patient (fg/patient)] - (= (->> (write-json patient) - (parse-json "Patient")) - (->> (write-json patient) - (parse-json "Patient") - (write-json) - (parse-json "Patient")) - patient)))) + (satisfies-prop 20 + (prop/for-all [library (fg/library)] + (= (->> (write-json library) + (parse-json "Library")) + library)))) (testing "XML" - (satisfies-prop 100 - (prop/for-all [patient (fg/patient)] - (= (-> patient + (satisfies-prop 20 + (prop/for-all [library (fg/library)] + (= (-> library fhir-spec/unform-xml fhir-spec/conform-xml) - patient)))) + library)))) (testing "CBOR" - (satisfies-prop 100 - (prop/for-all [patient (fg/patient)] - (= (->> (write-cbor patient) - (parse-cbor "Patient")) - (->> (write-cbor patient) - (parse-cbor "Patient") - (write-cbor) - (parse-cbor "Patient")) - patient))))) + (satisfies-prop 20 + (prop/for-all [library (fg/library)] + (= (->> (write-cbor library) + (parse-cbor "Library")) + library))))) - (testing "writing" - (testing "JSON" - (is (= (write-read-json {:fhir/type :fhir/Patient}) - {:resourceType "Patient"})) - (is (= (write-read-json - {:fhir/type :fhir/Patient - :gender #fhir/code "female" - :active #fhir/boolean true}) - {:resourceType "Patient" :active true :gender "female"}))))) + (testing "summary parsing" + (satisfies-prop 20 + (prop/for-all [library (fg/library)] + (let [source (write-cbor library) + library (parse-cbor "Library" source :summary)] + (and + (->> library :meta :tag (some fu/subsetted?)) + (not-any? :data (:content library))))))) + + (testing "hash-into" + (satisfies-prop 20 + (prop/for-all [library (fg/library)] + (murmur3 library))))) + +(deftest ^:mem-size library-mem-size-test + (satisfies-prop 20 + (prop/for-all [source (gen/fmap write-cbor (fg/library))] + (>= (Base/memSize (parse-cbor "Library" source)) + (mem/total-size* (parse-cbor "Library" source) + (parse-cbor "Library" source)))))) (deftest list-test (testing "references" @@ -3931,6 +6300,12 @@ :specimen :device :referenceRange}) (deftest observation-test + (testing "FHIR spec" + (testing "valid" + (satisfies-prop 20 + (prop/for-all [x (fg/observation)] + (s2/valid? :fhir/Observation x))))) + (testing "round-trip" (testing "JSON" (satisfies-prop 20 @@ -3976,9 +6351,129 @@ (are [x refs] (= refs (type/references x)) {:fhir/type :fhir/Observation :subject #fhir/Reference{:reference #fhir/string "Patient/0"}} - [["Patient" "0"]]))) + [["Patient" "0"]])) + + (testing "hash-into" + (satisfies-prop 20 + (prop/for-all [observation (fg/observation)] + (murmur3 observation))))) + +(deftest ^:mem-size observation-mem-size-test + (satisfies-prop 20 + (prop/for-all [source (gen/fmap write-cbor (fg/observation))] + (>= (Base/memSize (parse-cbor "Observation" source)) + (mem/total-size* (parse-cbor "Observation" source) + (parse-cbor "Observation" source)))))) + +(deftest medication-administration-test + (testing "FHIR spec" + (testing "valid" + (satisfies-prop 20 + (prop/for-all [x (fg/medication-administration)] + (s2/valid? :fhir/MedicationAdministration x))))) + + (testing "round-trip" + (testing "JSON" + (satisfies-prop 20 + (prop/for-all [medication-administration (fg/medication-administration)] + (= (->> (write-json medication-administration) + (parse-json "MedicationAdministration")) + medication-administration)))) + + (testing "XML" + (satisfies-prop 20 + (prop/for-all [medication-administration (fg/medication-administration)] + (= (-> medication-administration + fhir-spec/unform-xml + fhir-spec/conform-xml) + medication-administration)))) + + (testing "CBOR" + (satisfies-prop 20 + (prop/for-all [medication-administration (fg/medication-administration)] + (= (->> (write-cbor medication-administration) + (parse-cbor "MedicationAdministration")) + medication-administration))))) + + (testing "hash-into" + (satisfies-prop 20 + (prop/for-all [medication-administration (fg/medication-administration)] + (murmur3 medication-administration))))) + +(deftest ^:mem-size medication-administration-mem-size-test + (satisfies-prop 20 + (prop/for-all [source (gen/fmap write-cbor (fg/medication-administration))] + (>= (Base/memSize (parse-cbor "MedicationAdministration" source)) + (mem/total-size* (parse-cbor "MedicationAdministration" source) + (parse-cbor "MedicationAdministration" source)))))) + +(deftest patient-test + (testing "FHIR spec" + (testing "valid" + (satisfies-prop 20 + (prop/for-all [x (fg/patient)] + (s2/valid? :fhir/Patient x))))) + + (testing "round-trip" + (testing "JSON" + (satisfies-prop 20 + (prop/for-all [patient (fg/patient)] + (= (->> (write-json patient) + (parse-json "Patient")) + (->> (write-json patient) + (parse-json "Patient") + (write-json) + (parse-json "Patient")) + patient)))) + + (testing "XML" + (satisfies-prop 20 + (prop/for-all [patient (fg/patient)] + (= (-> patient + fhir-spec/unform-xml + fhir-spec/conform-xml) + patient)))) + + (testing "CBOR" + (satisfies-prop 20 + (prop/for-all [patient (fg/patient)] + (= (->> (write-cbor patient) + (parse-cbor "Patient")) + (->> (write-cbor patient) + (parse-cbor "Patient") + (write-cbor) + (parse-cbor "Patient")) + patient))))) + + (testing "writing" + (testing "JSON" + (is (= (write-read-json {:fhir/type :fhir/Patient}) + {:resourceType "Patient"})) + (is (= (write-read-json + {:fhir/type :fhir/Patient + :gender #fhir/code "female" + :active #fhir/boolean true}) + {:resourceType "Patient" :active true :gender "female"})))) + + (testing "hash-into" + (satisfies-prop 20 + (prop/for-all [patient (fg/patient)] + (murmur3 patient))))) + +(deftest ^:mem-size patient-mem-size-test + (satisfies-prop 20 + (prop/for-all [source (gen/fmap write-cbor (fg/patient))] + (>= (Base/memSize (parse-cbor "Patient" source)) + (mem/total-size* (parse-cbor "Patient" source) + (parse-cbor "Patient" source)))))) (deftest procedure-test + (testing "FHIR spec" + (testing "valid" + (satisfies-prop 20 + (prop/for-all [x (fg/procedure)] + (s2/valid? :fhir/Procedure x))))) + (testing "round-trip" (testing "JSON" (satisfies-prop 20 @@ -4026,57 +6521,19 @@ [["Procedure" "153904"] ["Condition" "153931"] ["Patient" "153540"] - ["Observation" "153628"]]))) - -(deftest allergy-intolerance-test - (testing "round-trip" - (testing "JSON" - (satisfies-prop 20 - (prop/for-all [allergy-intolerance (fg/allergy-intolerance)] - (= (->> (write-json allergy-intolerance) - (parse-json "AllergyIntolerance")) - allergy-intolerance)))) - - (testing "XML" - (satisfies-prop 20 - (prop/for-all [allergy-intolerance (fg/allergy-intolerance)] - (= (-> allergy-intolerance - fhir-spec/unform-xml - fhir-spec/conform-xml) - allergy-intolerance)))) - - (testing "CBOR" - (satisfies-prop 20 - (prop/for-all [allergy-intolerance (fg/allergy-intolerance)] - (= (->> (write-cbor allergy-intolerance) - (parse-cbor "AllergyIntolerance")) - allergy-intolerance)))))) - -(def ^:private code-system-non-summary-properties - #{:description :purpose :copyright :concept}) + ["Observation" "153628"]])) -(deftest code-system-test - (testing "summary parsing" + (testing "hash-into" (satisfies-prop 20 - (prop/for-all [code-system (fg/code-system)] - (let [source (write-cbor code-system) - code-system (parse-cbor "CodeSystem" source :summary)] - (and - (->> code-system :meta :tag (some fu/subsetted?)) - (not-any? code-system-non-summary-properties (keys code-system)))))))) + (prop/for-all [procedure (fg/procedure)] + (murmur3 procedure))))) -(def ^:private value-set-non-summary-properties - #{:description :purpose :copyright :compose :expansion}) - -(deftest value-set-test - (testing "summary parsing" - (satisfies-prop 20 - (prop/for-all [value-set (fg/value-set)] - (let [source (write-cbor value-set) - value-set (parse-cbor "ValueSet" source :summary)] - (and - (->> value-set :meta :tag (some fu/subsetted?)) - (not-any? value-set-non-summary-properties (keys value-set)))))))) +(deftest ^:mem-size procedure-mem-size-test + (satisfies-prop 20 + (prop/for-all [source (gen/fmap write-cbor (fg/procedure))] + (>= (Base/memSize (parse-cbor "Procedure" source)) + (mem/total-size* (parse-cbor "Procedure" source) + (parse-cbor "Procedure" source)))))) (deftest provenance-test (testing "references" @@ -4089,6 +6546,12 @@ ["Observation" "204754"]]))) (deftest task-test + (testing "FHIR spec" + (testing "valid" + (satisfies-prop 20 + (prop/for-all [x (fg/task)] + (s2/valid? :fhir/Task x))))) + (testing "round-trip" (testing "JSON" (satisfies-prop 20 @@ -4141,54 +6604,44 @@ [{:fhir/type :fhir.Task/input :value #fhir/code "code-173329"}]}) {:resourceType "Task" - :input [{:valueCode "code-173329"}]}))))) + :input [{:valueCode "code-173329"}]})))) -(deftest library-test - (testing "summary parsing" + (testing "hash-into" (satisfies-prop 20 - (prop/for-all [library (fg/library)] - (let [source (write-cbor library) - library (parse-cbor "Library" source :summary)] - (and - (->> library :meta :tag (some fu/subsetted?)) - (not-any? :data (:content library)))))))) - -(deftest consent-test - (testing "round-trip" - (testing "JSON" - (satisfies-prop 20 - (prop/for-all [consent (fg/consent)] - (= (->> (write-json consent) - (parse-json "Consent")) - consent)))) + (prop/for-all [task (fg/task)] + (murmur3 task))))) - (testing "XML" - (satisfies-prop 20 - (prop/for-all [consent (fg/consent)] - (= (-> consent - fhir-spec/unform-xml - fhir-spec/conform-xml) - consent)))) +(deftest ^:mem-size task-mem-size-test + (satisfies-prop 20 + (prop/for-all [source (gen/fmap write-cbor (fg/task))] + (>= (Base/memSize (parse-cbor "Task" source)) + (mem/total-size* (parse-cbor "Task" source) + (parse-cbor "Task" source)))))) - (testing "CBOR" +(deftest value-set-test + (testing "FHIR spec" + (testing "valid" (satisfies-prop 20 - (prop/for-all [consent (fg/consent)] - (= (->> (write-cbor consent) - (parse-cbor "Consent")) - consent))))) + (prop/for-all [x (fg/value-set)] + (s2/valid? :fhir/ValueSet x))))) - (testing "parsing" - (testing "JSON" - (are [json fhir] (= fhir (write-parse-json "Consent" json)) - {:resourceType "Consent" - :policyRule {}} - {:fhir/type :fhir/Consent - :policyRule #fhir/CodeableConcept{}}))) + (testing "summary parsing" + (satisfies-prop 20 + (prop/for-all [value-set (fg/value-set)] + (let [source (write-cbor value-set) + value-set (parse-cbor "ValueSet" source :summary)] + (and + (->> value-set :meta :tag (some fu/subsetted?)) + (not-any? value-set-non-summary-properties (keys value-set))))))) - (testing "writing" - (testing "JSON" - (is (= (write-read-json - {:fhir/type :fhir/Consent - :policyRule #fhir/CodeableConcept{}}) - {:resourceType "Consent" - :policyRule {}}))))) + (testing "hash-into" + (satisfies-prop 20 + (prop/for-all [value-set (fg/value-set)] + (murmur3 value-set))))) + +(deftest ^:mem-size value-set-mem-size-test + (satisfies-prop 20 + (prop/for-all [source (gen/fmap write-cbor (fg/value-set))] + (>= (Base/memSize (parse-cbor "ValueSet" source)) + (mem/total-size* (parse-cbor "ValueSet" source) + (parse-cbor "ValueSet" source)))))) diff --git a/modules/fhir-structure/test/blaze/fhir/util_test.clj b/modules/fhir-structure/test/blaze/fhir/util_test.clj index 743c6b122..de2b57b12 100644 --- a/modules/fhir-structure/test/blaze/fhir/util_test.clj +++ b/modules/fhir-structure/test/blaze/fhir/util_test.clj @@ -36,7 +36,12 @@ (deftest subsetted-test (are [coding] (fu/subsetted? coding) {:system #fhir/uri "http://terminology.hl7.org/CodeSystem/v3-ObservationValue" - :code #fhir/code "SUBSETTED"}) + :code #fhir/code "SUBSETTED"} + {:system #fhir/uri "http://terminology.hl7.org/CodeSystem/v3-ObservationValue" + :code #fhir/code {:id "foo" :value "SUBSETTED"}} + {:system #fhir/uri {:id "foo" :value "http://terminology.hl7.org/CodeSystem/v3-ObservationValue"} + :code #fhir/code "SUBSETTED"} + fu/subsetted) (are [coding] (not (fu/subsetted? coding)) {:code #fhir/code "SUBSETTED"} diff --git a/modules/fhir-test-util/src/blaze/fhir/spec/generators.clj b/modules/fhir-test-util/src/blaze/fhir/spec/generators.clj index e0a3edb1e..76702823b 100644 --- a/modules/fhir-test-util/src/blaze/fhir/spec/generators.clj +++ b/modules/fhir-test-util/src/blaze/fhir/spec/generators.clj @@ -1,5 +1,5 @@ (ns blaze.fhir.spec.generators - (:refer-clojure :exclude [boolean meta str time]) + (:refer-clojure :exclude [boolean count meta range str time]) (:require [blaze.fhir.spec.type :as type] [blaze.fhir.spec.type.system :as system] @@ -32,15 +32,15 @@ (def ^:private char-printable-whitespace (gen/fmap char (gen/one-of [(gen/choose 9 10) (gen/return 13) (gen/choose 32 126) (gen/choose 160 255)]))) -(def ^:private char-printable - (gen/fmap char (gen/one-of [(gen/choose 32 126) (gen/choose 160 255)]))) - (def ^:private char-printable-non-blank (gen/fmap char (gen/one-of [(gen/choose 33 126) (gen/choose 161 255)]))) (def string-value (gen/fmap str/join (gen/vector char-printable-whitespace))) +(def large-string-value + (gen/fmap str/join (gen/vector char-printable-whitespace 5 100))) + (def decimal-value (gen/fmap #(BigDecimal/valueOf ^double %) (gen/double* {:infinite? false :NaN? false}))) @@ -86,34 +86,41 @@ (gen/one-of [(gen/return "Z") zone-offset])) (def instant-value - (gen/fmap (partial apply format "%04d-%02d-%02dT%02d:%02d:%02d%s") - (gen/tuple year month day hour minute time-second zone))) + (gen/fmap + system/parse-date-time + (gen/fmap (partial apply format "%04d-%02d-%02dT%02d:%02d:%02d%s") + (gen/tuple year month day hour minute time-second zone)))) (def date-value - (gen/one-of - [(gen/fmap (partial format "%04d") year) - (gen/fmap (partial apply format "%04d-%02d") - (gen/tuple year month)) - (gen/fmap (partial apply format "%04d-%02d-%02d") - (gen/tuple year month day))])) + (gen/fmap + system/parse-date + (gen/one-of + [(gen/fmap (partial format "%04d") year) + (gen/fmap (partial apply format "%04d-%02d") + (gen/tuple year month)) + (gen/fmap (partial apply format "%04d-%02d-%02d") + (gen/tuple year month day))]))) (defn dateTime-value [& {:keys [year] :or {year year}}] - (gen/one-of - [(gen/fmap (partial format "%04d") year) - (gen/fmap (partial apply format "%04d-%02d") - (gen/tuple year month)) - (gen/fmap (partial apply format "%04d-%02d-%02d") - (gen/tuple year month day)) - (gen/fmap (partial apply format "%04d-%02d-%02dT%02d:%02d:%02d%s") - (gen/tuple year month day hour minute time-second zone))])) + (gen/fmap + system/parse-date-time + (gen/one-of + [(gen/fmap (partial format "%04d") year) + (gen/fmap (partial apply format "%04d-%02d") + (gen/tuple year month)) + (gen/fmap (partial apply format "%04d-%02d-%02d") + (gen/tuple year month day)) + (gen/fmap (partial apply format "%04d-%02d-%02dT%02d:%02d:%02d%s") + (gen/tuple year month day hour minute time-second zone))]))) (def time-value - (gen/fmap (partial apply format "%02d:%02d:%02d") - (gen/tuple hour minute time-second))) + (gen/fmap + system/parse-time + (gen/fmap (partial apply format "%02d:%02d:%02d") + (gen/tuple hour minute time-second)))) (def code-value - (gen/such-that (partial re-matches #"(?U)[\p{Print}&&[^\p{Blank}]]+(\p{Blank}[\p{Print}&&[^\p{Blank}]]+)*") - (gen/fmap str/join (gen/vector char-printable)) 1000)) + (gen/fmap str/join (gen/vector char-printable-whitespace))) (def char-digit (gen/fmap char (gen/choose 48 57))) @@ -124,7 +131,7 @@ (gen/fmap str/join (gen/vector char-digit))))) (def id-value - (gen/such-that (partial re-matches #"[A-Za-z0-9\-\.]{1,64}") + (gen/such-that (partial re-matches #"[A-Za-z0-9\-\ .]{1,64}") (gen/fmap str/join (gen/vector gen/char-alphanumeric 1 64)) 1000)) @@ -150,7 +157,7 @@ m)) (defn- to-map [keys vals] - (gen/such-that seq (gen/fmap #(keep-vals (zipmap keys %)) vals) 1000)) + (gen/such-that seq (gen/fmap #(keep-vals (zipmap keys %)) vals) 100)) (declare extension) @@ -162,7 +169,7 @@ [& {:keys [id extension value] :or {id (often-nil id-value) extension (often-nil (extensions)) - value (rare-nil value-gen)}}] + value (nilable value-gen)}}] (->> (gen/tuple id extension value) (to-map [:id :extension :value]) (gen/fmap constructor)))) @@ -176,6 +183,9 @@ (def string (primitive-gen type/string string-value)) +(def large-string + (primitive-gen type/string large-string-value)) + (def decimal (primitive-gen type/decimal decimal-value)) @@ -224,6 +234,73 @@ (def uuid (primitive-gen type/uuid uuid-value)) +(declare coding) +(declare contact-point) +(declare data-requirement) +(declare dosage) +(declare duration) +(declare human-name) +(declare identifier) +(declare meta) +(declare money) +(declare parameter-definition) +(declare period) +(declare quantity) +(declare range) +(declare ratio) +(declare reference) +(declare related-artifact) +(declare sampled-data) +(declare signature) +(declare timing) +(declare trigger-definition) +(declare usage-context) + +(defn address + [& {:keys [id extension use type text line city district state postalCode + country period] + :or {id (often-nil id-value) + extension (extensions) + use (rare-nil (code)) + type (rare-nil (code)) + text (often-nil (string)) + line (gen/vector (string)) + city (rare-nil (string)) + district (rare-nil (string)) + state (rare-nil (string)) + postalCode (rare-nil (string)) + country (rare-nil (string)) + period (often-nil (period))}}] + (->> (gen/tuple id extension use type text line city district state postalCode + country period) + (to-map [:id :extension :use :type :text :line :city :district :state + :postalCode :country :period]) + (gen/fmap type/address))) + +(defn age + [& {:keys [id extension value comparator unit system code] + :or {id (often-nil id-value) + extension (extensions) + value (rare-nil (decimal)) + comparator (often-nil (code)) + unit (rare-nil (string)) + system (rare-nil (uri)) + code (rare-nil (code))}}] + (->> (gen/tuple id extension value comparator unit system code) + (to-map [:id :extension :value :comparator :unit :system :code]) + (gen/fmap type/age))) + +(defn annotation + [& {:keys [id extension author time text] + :or {id (often-nil id-value) + extension (gen/return nil) + author (rare-nil (gen/one-of [(reference) (string)])) + time (rare-nil (dateTime)) + text (rare-nil (markdown))}}] + (->> (gen/tuple id extension author time text) + (to-map [:id :extension :author :time :text]) + (gen/fmap type/annotation))) + (defn attachment [& {:keys [id extension contentType language data url size hash title creation] :or {id (often-nil id-value) @@ -242,14 +319,15 @@ :title :creation]) (gen/fmap type/attachment))) -(defn extension - [& {:keys [id extension value] +(defn codeable-concept + [& {:keys [id extension coding text] :or {id (often-nil id-value) - extension (gen/return nil) - value (gen/return nil)}}] - (->> (gen/tuple id extension uri-value value) - (to-map [:id :extension :url :value]) - (gen/fmap type/extension))) + extension (extensions) + coding (gen/vector (coding)) + text (often-nil (string))}}] + (->> (gen/tuple id extension coding text) + (to-map [:id :extension :coding :text]) + (gen/fmap type/codeable-concept))) (defn coding [& {:keys [id extension system version code display user-selected] @@ -264,17 +342,41 @@ (to-map [:id :extension :system :version :code :display :userSelected]) (gen/fmap type/coding))) -(defn codeable-concept - [& {:keys [id extension coding text] +(defn contact-detail + [& {:keys [id extension name telecom] :or {id (often-nil id-value) extension (extensions) - coding (gen/vector (coding)) - text (often-nil (string))}}] - (->> (gen/tuple id extension coding text) - (to-map [:id :extension :coding :text]) - (gen/fmap type/codeable-concept))) + name (rare-nil (string)) + telecom (gen/vector (contact-point) 0 3)}}] + (->> (gen/tuple id extension name telecom) + (to-map [:id :extension :name :telecom]) + (gen/fmap type/contact-detail))) + +(defn contact-point + [& {:keys [id extension system value use rank period] + :or {id (often-nil id-value) + extension (extensions) + system (rare-nil (code)) + value (rare-nil (string)) + use (nilable (code)) + rank (often-nil (positiveInt)) + period (rare-nil (period))}}] + (->> (gen/tuple id extension system value use rank period) + (to-map [:id :extension :system :value :use :rank :period]) + (gen/fmap type/contact-point))) + +(defn contributor + [& {:keys [id extension type name contact] + :or {id (often-nil id-value) + extension (extensions) + type (rare-nil (code)) + name (rare-nil (string)) + contact (gen/vector (contact-detail) 0 3)}}] + (->> (gen/tuple id extension type name contact) + (to-map [:id :extension :type :name :contact]) + (gen/fmap type/contributor))) -(defn quantity +(defn count [& {:keys [id extension value comparator unit system code] :or {id (often-nil id-value) extension (extensions) @@ -285,53 +387,199 @@ code (rare-nil (code))}}] (->> (gen/tuple id extension value comparator unit system code) (to-map [:id :extension :value :comparator :unit :system :code]) - (gen/fmap type/quantity))) - -;; TODO: Range + (gen/fmap type/count))) -(defn ratio - [& {:keys [id extension numerator denominator] +(defn data-requirement-code-filter + [& {:keys [id extension path searchParam valueSet code] :or {id (often-nil id-value) extension (extensions) - numerator (nilable (quantity)) - denominator (nilable (quantity))}}] - (->> (gen/tuple id extension numerator denominator) - (to-map [:id :extension :numerator :denominator]) - (gen/fmap type/ratio))) - -;; TODO: RatioRange + path (nilable (string)) + searchParam (nilable (string)) + valueSet (nilable (canonical)) + code (gen/vector (coding))}}] + (->> (gen/tuple id extension path searchParam valueSet code) + (to-map [:id :extension :path :searchParam :valueSet :code]) + (gen/fmap type/data-requirement-code-filter))) + +(defn data-requirement-date-filter + [& {:keys [id extension path searchParam value] + :or {id (often-nil id-value) + extension (extensions) + path (nilable (string)) + searchParam (nilable (string)) + value (nilable (gen/one-of [(dateTime) (period) (duration)]))}}] + (->> (gen/tuple id extension path searchParam value) + (to-map [:id :extension :path :searchParam :value]) + (gen/fmap type/data-requirement-date-filter))) + +(defn data-requirement-sort + [& {:keys [id extension path direction] + :or {id (often-nil id-value) + extension (extensions) + path (string) + direction (code)}}] + (->> (gen/tuple id extension path direction) + (to-map [:id :extension :path :direction]) + (gen/fmap type/data-requirement-sort))) + +(defn data-requirement + [& {:keys [id extension type profile subject mustSupport codeFilter dateFilter + limit sort] + :or {id (often-nil id-value) + extension (extensions) + type (code) + profile (gen/vector (canonical)) + subject (nilable (gen/one-of [(codeable-concept) (reference)])) + mustSupport (gen/vector (string)) + codeFilter (gen/vector (data-requirement-code-filter)) + dateFilter (gen/vector (data-requirement-date-filter)) + limit (nilable (positiveInt)) + sort (gen/vector (data-requirement-sort))}}] + (->> (gen/tuple id extension type profile subject mustSupport codeFilter + dateFilter limit sort) + (to-map [:id :extension :type :profile :subject :mustSupport :codeFilter + :dateFilter :limit :sort]) + (gen/fmap type/data-requirement))) + +(defn distance + [& {:keys [id extension value comparator unit system code] + :or {id (often-nil id-value) + extension (extensions) + value (rare-nil (decimal)) + comparator (often-nil (code)) + unit (rare-nil (string)) + system (rare-nil (uri)) + code (rare-nil (code))}}] + (->> (gen/tuple id extension value comparator unit system code) + (to-map [:id :extension :value :comparator :unit :system :code]) + (gen/fmap type/distance))) -(defn period - [& {:keys [id extension start end] +(defn duration + [& {:keys [id extension value comparator unit system code] :or {id (often-nil id-value) extension (extensions) - start (nilable (dateTime)) - end (nilable (dateTime))}}] - (as-> (gen/tuple id extension start end) x - (to-map [:id :extension :start :end] x) - (gen/such-that #(<= (system/date-time-lower-bound (type/value (:start %))) - (system/date-time-upper-bound (type/value (:end %)))) - x - 1000) - (gen/fmap type/period x))) + value (rare-nil (decimal)) + comparator (often-nil (code)) + unit (rare-nil (string)) + system (rare-nil (uri)) + code (rare-nil (code))}}] + (->> (gen/tuple id extension value comparator unit system code) + (to-map [:id :extension :value :comparator :unit :system :code]) + (gen/fmap type/duration))) -;; TODO: SampledData +(defn expression + [& {:keys [id extension description name language expression reference] + :or {id (often-nil id-value) + extension (extensions) + description (often-nil (string)) + name (often-nil (blaze.fhir.spec.generators/id)) + language (rare-nil (code)) + expression (rare-nil (string)) + reference (often-nil (uri))}}] + (->> (gen/tuple id extension description name language expression reference) + (to-map [:id :extension :description :name :language :expression :reference]) + (gen/fmap type/expression))) + +(defn extension-value [] + (gen/one-of + [(base64Binary) + (boolean) + (canonical) + (code) + (date) + (dateTime) + (decimal) + (id) + (instant) + (integer) + (markdown) + (oid) + (positiveInt) + (string) + (time) + (unsignedInt) + (uri) + (url) + (uuid) + (address) + (age) + (annotation) + (attachment) + (codeable-concept) + (coding) + (contact-point) + (count) + (data-requirement) + (distance) + (duration) + (human-name) + (identifier) + (money) + (period) + (quantity) + (range) + (ratio) + (reference) + (sampled-data) + (signature) + (timing) + (contact-detail) + (contributor) + (expression) + (parameter-definition) + (related-artifact) + (trigger-definition) + (usage-context) + (dosage) + (meta)])) -(declare reference) +(defn extension + [& {:keys [id extension value] + :or {id (often-nil id-value) + extension (gen/return nil) + value (gen/return nil)}}] + (->> (gen/tuple id extension uri-value value) + (to-map [:id :extension :url :value]) + (gen/fmap type/extension))) -(defn identifier - [& {:keys [id extension use type system value period assigner] +(defn dosage-dose-and-rate + [& {:keys [id extension type dose rate] :or {id (often-nil id-value) extension (extensions) - use (rare-nil (code)) type (nilable (codeable-concept)) - system (rare-nil (uri)) - value (rare-nil (string)) - period (often-nil (period)) - assigner (gen/return nil)}}] - (->> (gen/tuple id extension use type system value period assigner) - (to-map [:id :extension :use :type :system :value :period :assigner]) - (gen/fmap type/identifier))) + dose (nilable (gen/one-of [(range) (quantity)])) + rate (nilable (gen/one-of [(ratio) (range) (quantity)]))}}] + (->> (gen/tuple id extension type dose rate) + (to-map [:id :extension :type :dose :rate]) + (gen/fmap type/dosage-dose-and-rate))) + +(defn dosage + [& {:keys [id extension modifierExtension sequence text additionalInstruction + patientInstruction timing asNeeded site route method doseAndRate + maxDosePerPeriod maxDosePerAdministration maxDosePerLifetime] + :or {id (often-nil id-value) + extension (extensions) + modifierExtension (extensions) + sequence (nilable (integer)) + text (nilable (string)) + additionalInstruction (gen/vector (codeable-concept)) + patientInstruction (nilable (string)) + timing (nilable (timing)) + asNeeded (nilable (gen/one-of [(boolean) (codeable-concept)])) + site (nilable (codeable-concept)) + route (nilable (codeable-concept)) + method (nilable (codeable-concept)) + doseAndRate (gen/vector (dosage-dose-and-rate)) + maxDosePerPeriod (nilable (ratio)) + maxDosePerAdministration (nilable (quantity)) + maxDosePerLifetime (nilable (quantity))}}] + (->> (gen/tuple id extension modifierExtension sequence text additionalInstruction + patientInstruction timing asNeeded site route method doseAndRate + maxDosePerPeriod maxDosePerAdministration maxDosePerLifetime) + (to-map [:id :extension :modifierExtension :sequence :text :additionalInstruction + :patientInstruction :timing :asNeeded :site :route :method :doseAndRate + :maxDosePerPeriod :maxDosePerAdministration :maxDosePerLifetime]) + (gen/fmap type/dosage))) (defn human-name [& {:keys [id extension use text family given prefix suffix period] @@ -348,34 +596,106 @@ (to-map [:id :extension :use :text :family :given :prefix :suffix :period]) (gen/fmap type/human-name))) -(defn address - [& {:keys [id extension use type text line city district state postalCode - country period] +(defn identifier + [& {:keys [id extension use type system value period assigner] :or {id (often-nil id-value) extension (extensions) use (rare-nil (code)) + type (nilable (codeable-concept)) + system (rare-nil (uri)) + value (rare-nil (string)) + period (often-nil (period)) + assigner (gen/return nil)}}] + (->> (gen/tuple id extension use type system value period assigner) + (to-map [:id :extension :use :type :system :value :period :assigner]) + (gen/fmap type/identifier))) + +(defn meta + [& {:keys [id extension versionId lastUpdated source profile security tag] + :or {id (often-nil id-value) + extension (extensions) + versionId (rare-nil (blaze.fhir.spec.generators/id)) + lastUpdated (rare-nil (instant)) + source (nilable (uri)) + profile (gen/vector (canonical)) + security (gen/vector (coding)) + tag (gen/vector (coding))}}] + (->> (gen/tuple id extension versionId lastUpdated source profile security tag) + (to-map [:id :extension :versionId :lastUpdated :source :profile + :security :tag]) + (gen/fmap type/meta))) + +(defn money + [& {:keys [id extension value currency] + :or {id (often-nil id-value) + extension (extensions) + value (rare-nil (decimal)) + currency (rare-nil (code))}}] + (->> (gen/tuple id extension value currency) + (to-map [:id :extension :value :currency]) + (gen/fmap type/money))) + +(defn parameter-definition + [& {:keys [id extension name use min max documentation type profile] + :or {id (often-nil id-value) + extension (extensions) + name (rare-nil (code)) + use (rare-nil (code)) + min (rare-nil (integer)) + max (rare-nil (string)) + documentation (rare-nil (string)) type (rare-nil (code)) - text (often-nil (string)) - line (gen/vector (string)) - city (rare-nil (string)) - district (rare-nil (string)) - state (rare-nil (string)) - postalCode (rare-nil (string)) - country (rare-nil (string)) - period (often-nil (period))}}] - (->> (gen/tuple id extension use type text line city district state postalCode - country period) - (to-map [:id :extension :use :type :text :line :city :district :state - :postalCode :country :period]) - (gen/fmap type/address))) + profile (rare-nil (canonical))}}] + (->> (gen/tuple id extension name use min max documentation type profile) + (to-map [:id :extension :name :use :min :max :documentation :type :profile]) + (gen/fmap type/parameter-definition))) -;; TODO: ContactPoint +(defn period + [& {:keys [id extension start end] + :or {id (often-nil id-value) + extension (extensions) + start (nilable (dateTime)) + end (nilable (dateTime))}}] + (as-> (gen/tuple id extension start end) x + (to-map [:id :extension :start :end] x) + (gen/such-that #(<= (system/date-time-lower-bound (:value (:start %))) + (system/date-time-upper-bound (:value (:end %)))) + x + 100) + (gen/fmap type/period x))) -;; TODO: Timing +(defn quantity + [& {:keys [id extension value comparator unit system code] + :or {id (often-nil id-value) + extension (extensions) + value (rare-nil (decimal)) + comparator (often-nil (code)) + unit (rare-nil (string)) + system (rare-nil (uri)) + code (rare-nil (code))}}] + (->> (gen/tuple id extension value comparator unit system code) + (to-map [:id :extension :value :comparator :unit :system :code]) + (gen/fmap type/quantity))) -;; TODO: Signature +(defn range + [& {:keys [id extension low high] + :or {id (often-nil id-value) + extension (extensions) + low (nilable (quantity)) + high (nilable (quantity))}}] + (->> (gen/tuple id extension low high) + (to-map [:id :extension :low :high]) + (gen/fmap type/range))) -;; TODO: Annotation +(defn ratio + [& {:keys [id extension numerator denominator] + :or {id (often-nil id-value) + extension (extensions) + numerator (nilable (quantity)) + denominator (nilable (quantity))}}] + (->> (gen/tuple id extension numerator denominator) + (to-map [:id :extension :numerator :denominator]) + (gen/fmap type/ratio))) (defn reference [& {:keys [id extension reference type identifier display] @@ -389,20 +709,123 @@ (to-map [:id :extension :reference :type :identifier :display]) (gen/fmap type/reference))) -(defn meta - [& {:keys [id extension versionId lastUpdated source profile security tag] +(defn related-artifact + [& {:keys [id extension type label display citation url document resource] :or {id (often-nil id-value) extension (extensions) - versionId (rare-nil (blaze.fhir.spec.generators/id)) - lastUpdated (rare-nil (instant)) - source (nilable (uri)) - profile (gen/vector (canonical)) - security (gen/vector (coding)) - tag (gen/vector (coding))}}] - (->> (gen/tuple id extension versionId lastUpdated source profile security tag) - (to-map [:id :extension :versionId :lastUpdated :source :profile - :security :tag]) - (gen/fmap type/meta))) + type (rare-nil (code)) + label (nilable (string)) + display (nilable (string)) + citation (often-nil (markdown)) + url (nilable (url)) + document (nilable (attachment)) + resource (nilable (canonical))}}] + (->> (gen/tuple id extension type label display citation url document resource) + (to-map [:id :extension :type :label :display :citation :url :document :resource]) + (gen/fmap type/related-artifact))) + +(defn sampled-data + [& {:keys [id extension origin period factor lowerLimit upperLimit dimensions data] + :or {id (often-nil id-value) + extension (extensions) + origin (quantity :comparator (gen/return nil)) + period (decimal) + factor (nilable (decimal)) + lowerLimit (nilable (decimal)) + upperLimit (nilable (decimal)) + dimensions (positiveInt) + data (nilable (string))}}] + (->> (gen/tuple id extension origin period factor lowerLimit upperLimit dimensions data) + (to-map [:id :extension :origin :period :factor :lowerLimit :upperLimit :dimensions :data]) + (gen/fmap type/sampled-data))) + +(defn signature + [& {:keys [id extension type when who onBehalfOf targetFormat sigFormat data] + :or {id (often-nil id-value) + extension (extensions) + type (gen/vector (coding)) + when (rare-nil (instant)) + who (rare-nil (reference)) + onBehalfOf (nilable (reference)) + targetFormat (nilable (code)) + sigFormat (nilable (code)) + data (nilable (base64Binary))}}] + (->> (gen/tuple id extension type when who onBehalfOf targetFormat sigFormat data) + (to-map [:id :extension :type :when :who :onBehalfOf :targetFormat :sigFormat :data]) + (gen/fmap type/signature))) + +(defn timing-repeat-bounds [] + (gen/one-of [(duration) (range) (period)])) + +(defn timing-repeat + [& {:keys [id extension bounds count countMax duration durationMax durationUnit + frequency frequencyMax period periodMax periodUnit dayOfWeek timeOfDay + when offset] + :or {id (often-nil id-value) + extension (extensions) + bounds (nilable (timing-repeat-bounds)) + count (nilable (positiveInt)) + countMax (nilable (positiveInt)) + duration (nilable (decimal)) + durationMax (nilable (decimal)) + durationUnit (nilable (code)) + frequency (nilable (positiveInt)) + frequencyMax (nilable (positiveInt)) + period (nilable (decimal)) + periodMax (nilable (decimal)) + periodUnit (nilable (code)) + dayOfWeek (gen/vector (code)) + timeOfDay (gen/vector (time)) + when (gen/vector (code)) + offset (nilable (unsignedInt))}}] + (->> (gen/tuple id extension bounds count countMax duration durationMax + durationUnit frequency frequencyMax period periodMax periodUnit + dayOfWeek timeOfDay when offset) + (to-map [:id :extension :bounds :count :countMax :duration :durationMax + :durationUnit :frequency :frequencyMax :period :periodMax + :periodUnit :dayOfWeek :timeOfDay :when :offset]) + (gen/fmap type/timing-repeat))) + +(defn timing + [& {:keys [id extension modifierExtension event repeat code] + :or {id (often-nil id-value) + extension (extensions) + modifierExtension (extensions) + event (gen/vector (dateTime)) + repeat (nilable (timing-repeat)) + code (nilable (codeable-concept))}}] + (->> (gen/tuple id extension modifierExtension event repeat code) + (to-map [:id :extension :modifierExtension :event :repeat :code]) + (gen/fmap type/timing))) + +(defn- trigger-definition-timing [] + (gen/one-of [(timing) (reference) (date) (dateTime)])) + +(defn trigger-definition + [& {:keys [id extension type name timing data condition] + :or {id (often-nil id-value) + extension (extensions) + type (code) + name (nilable (string)) + timing (nilable (trigger-definition-timing)) + data (gen/vector (data-requirement)) + condition (nilable (expression))}}] + (->> (gen/tuple id extension type name timing data condition) + (to-map [:id :extension :type :name :timing :data :condition]) + (gen/fmap type/trigger-definition))) + +(defn- usage-context-value [] + (gen/one-of [(codeable-concept) (quantity) (range) (reference)])) + +(defn usage-context + [& {:keys [id extension code value] + :or {id (often-nil id-value) + extension (extensions) + code (coding) + value (usage-context-value)}}] + (->> (gen/tuple id extension code value) + (to-map [:id :extension :code :value]) + (gen/fmap type/usage-context))) (defn bundle-entry-search [& {:keys [id extension mode score] @@ -439,95 +862,29 @@ (to-map [~@(map keyword field-syms)]) (fhir-type ~(keyword "fhir" (kebab->pascal (str type)))))))) -(def-resource-gen bundle - [id id-value - identifier (identifier) - type (rare-nil (code)) - entry (gen/vector (bundle-entry))]) - -(def-resource-gen patient +(def-resource-gen activity-definition [id id-value - gender (rare-nil (code)) - birthDate (rare-nil (date)) - multipleBirth (rare-nil (gen/one-of [(boolean) (integer)]))]) - -(defn- observation-value [] - (gen/one-of [(quantity) (codeable-concept) (string) (boolean) (integer) - #_(range) #_(ratio) #_(sampled-data) (time) (dateTime) (period)])) - -(def-resource-gen observation - [id id-value - meta (meta) identifier (gen/vector (identifier)) - status (rare-nil (code)) - category (gen/vector (codeable-concept)) - code (rare-nil (codeable-concept)) - subject (rare-nil (reference :reference (gen/return nil))) - encounter (rare-nil (reference :reference (gen/return nil))) - effective (rare-nil (gen/one-of [(dateTime) (period)])) - performer (gen/vector (reference :reference (gen/return nil))) - value (rare-nil (observation-value))]) - -(def-resource-gen encounter - [id id-value - meta (meta) - identifier (gen/vector (identifier)) - status (rare-nil (code)) - type (gen/vector (codeable-concept)) - priority (rare-nil (codeable-concept)) - subject (rare-nil (reference :reference (gen/return nil))) - period (rare-nil (period))]) - -(def-resource-gen procedure - [id id-value - meta (meta) - identifier (gen/vector (identifier)) - instantiatesCanonical (gen/vector (canonical)) - instantiatesUri (gen/vector (uri)) - status (rare-nil (code)) - category (codeable-concept) - code (rare-nil (codeable-concept)) - subject (rare-nil (reference :reference (gen/return nil))) - encounter (rare-nil (reference :reference (gen/return nil)))]) + relatedArtifact (gen/vector (related-artifact)) + timing (nilable (gen/one-of [(timing) (dateTime) (age) (period) (range) (duration)])) + dosage (gen/vector (dosage))]) (def-resource-gen allergy-intolerance [id id-value - meta (meta) + meta (rare-nil (meta)) category (gen/vector (code))]) -(def-resource-gen medication-administration - [id id-value - meta (meta) - medication (rare-nil (reference :reference (gen/return nil))) - subject (rare-nil (reference :reference (gen/return nil)))]) - -(def-resource-gen diagnostic-report +(def-resource-gen bundle [id id-value - meta (meta) - identifier (gen/vector (identifier)) - status (rare-nil (code)) - category (gen/vector (codeable-concept)) - code (rare-nil (codeable-concept)) - subject (rare-nil (reference :reference (gen/return nil))) - encounter (rare-nil (reference :reference (gen/return nil))) - effective (rare-nil (gen/one-of [(dateTime) (period)])) - issued (nilable (instant)) - performer (gen/vector (reference :reference (gen/return nil)))]) + identifier (identifier) + type (rare-nil (code)) + entry (gen/vector (bundle-entry))]) -(def-resource-gen library +(def-resource-gen claim [id id-value - meta (meta) - url (uri) identifier (gen/vector (identifier)) - version (rare-nil (string)) - name (nilable (string)) - title (nilable (string)) - subtitle (nilable (string)) status (rare-nil (code)) - experimental (nilable (boolean)) - type (codeable-concept) - subject (rare-nil (gen/one-of [(codeable-concept) (reference :reference (gen/return nil))])) - content (gen/vector (attachment))]) + total (nilable (money))]) (defn- code-system-concept [& {:keys [code] @@ -538,7 +895,7 @@ (def-resource-gen code-system [id id-value - meta (meta) + meta (rare-nil (meta)) url (uri) identifier (gen/vector (identifier)) version (rare-nil (string)) @@ -548,41 +905,14 @@ experimental (nilable (boolean)) concept (gen/vector (code-system-concept))]) -(defn- value-set-compose - [& {:keys [inactive] - :or {inactive (often-nil (boolean))}}] - (->> (gen/tuple inactive) - (to-map [:inactive]) - (fhir-type :fhir.ValueSet/compose))) +(defn condition-onset [] + (gen/one-of [(dateTime) (age) (period) (range) (string)])) -(def-resource-gen value-set +(def-resource-gen condition [id id-value - meta (meta) - url (uri) + meta (rare-nil (meta)) identifier (gen/vector (identifier)) - version (rare-nil (string)) - name (nilable (string)) - title (nilable (string)) - status (rare-nil (code)) - experimental (nilable (boolean)) - compose (value-set-compose)]) - -(defn- task-value [] - (gen/one-of [(quantity) (codeable-concept) (string) (boolean) (integer) - #_(range) #_(ratio) #_(sampled-data) (time) (dateTime) (period)])) - -(defn- task-input - [& {:keys [type value] - :or {type (codeable-concept) - value (task-value)}}] - (->> (gen/tuple type value) - (to-map [:type :value]) - (fhir-type :fhir.Task/input))) - -(def-resource-gen task - [identifier (gen/vector (identifier)) - status (rare-nil (code)) - input (gen/vector (task-input))]) + onset (nilable (condition-onset))]) (defn consent-policy [& {:keys [id extension authority uri] @@ -647,9 +977,265 @@ (fhir-type :fhir.Consent/provision))) (def-resource-gen consent - [identifier (gen/vector (identifier)) + [id id-value + identifier (gen/vector (identifier)) status (code) policy (gen/vector (consent-policy)) policyRule (nilable (codeable-concept)) verification (gen/vector (consent-verification)) provision (nilable (consent-provision {:provision (gen/vector (consent-provision))}))]) + +(def-resource-gen diagnostic-report + [id id-value + meta (rare-nil (meta)) + identifier (gen/vector (identifier)) + status (rare-nil (code)) + category (gen/vector (codeable-concept)) + code (rare-nil (codeable-concept)) + subject (rare-nil (reference :reference (gen/return nil))) + encounter (rare-nil (reference :reference (gen/return nil))) + effective (rare-nil (gen/one-of [(dateTime) (period)])) + issued (nilable (instant)) + performer (gen/vector (reference :reference (gen/return nil)))]) + +(def-resource-gen encounter + [id id-value + meta (rare-nil (meta)) + identifier (gen/vector (identifier)) + status (rare-nil (code)) + type (gen/vector (codeable-concept)) + priority (rare-nil (codeable-concept)) + subject (rare-nil (reference :reference (gen/return nil))) + period (rare-nil (period))]) + +(defn- imaging-study-series-instance + [& {:keys [id extension modifierExtension uid sopClass number title] + :or {id (often-nil id-value) + extension (extensions) + modifierExtension (extensions) + uid (rare-nil (blaze.fhir.spec.generators/id)) + sopClass (rare-nil (coding)) + number (nilable (unsignedInt)) + title (nilable (string))}}] + (->> (gen/tuple id extension modifierExtension uid sopClass number title) + (to-map [:id :extension :modifierExtension :uid :sopClass :number :title]) + (fhir-type :fhir.ImagingStudy.series/instance))) + +(defn- imaging-study-series-performer + [& {:keys [id extension modifierExtension function actor] + :or {id (often-nil id-value) + extension (extensions) + modifierExtension (extensions) + function (nilable (codeable-concept)) + actor (reference)}}] + (->> (gen/tuple id extension modifierExtension function actor) + (to-map [:id :extension :modifierExtension :function :actor]) + (fhir-type :fhir.ImagingStudy.series/performer))) + +(defn- imaging-study-series + [& {:keys [id extension modifierExtension uid number modality description + numberOfInstances endpoint bodySite laterality specimen started + performer instance] + :or {id (often-nil id-value) + extension (extensions) + modifierExtension (extensions) + uid (rare-nil (blaze.fhir.spec.generators/id)) + number (nilable (unsignedInt)) + modality (coding) + description (nilable (string)) + numberOfInstances (nilable (unsignedInt)) + endpoint (gen/vector (reference)) + bodySite (nilable (coding)) + laterality (nilable (coding)) + specimen (gen/vector (reference)) + started (nilable (dateTime)) + performer (gen/vector (imaging-study-series-performer)) + instance (gen/vector (imaging-study-series-instance))}}] + (->> (gen/tuple id extension modifierExtension uid number modality description + numberOfInstances endpoint bodySite laterality specimen started + performer instance) + (to-map [:id :extension :modifierExtension :uid :number :modality :description + :numberOfInstances :endpoint :bodySite :laterality :specimen :started + :performer :instance]) + (fhir-type :fhir.ImagingStudy/series))) + +(def-resource-gen imaging-study + [id id-value + meta (rare-nil (meta)) + identifier (gen/vector (identifier)) + status (rare-nil (code)) + modality (gen/vector (coding)) + subject (rare-nil (reference :reference (gen/return nil))) + encounter (nilable (reference :reference (gen/return nil))) + started (nilable (dateTime)) + basedOn (gen/vector (reference :reference (gen/return nil))) + referrer (nilable (reference :reference (gen/return nil))) + interpreter (gen/vector (reference :reference (gen/return nil))) + endpoint (gen/vector (reference :reference (gen/return nil))) + numberOfSeries (nilable (unsignedInt)) + numberOfInstances (nilable (unsignedInt)) + procedureReference (nilable (reference :reference (gen/return nil))) + procedureCode (gen/vector (codeable-concept)) + location (nilable (reference :reference (gen/return nil))) + reasonCode (gen/vector (codeable-concept)) + reasonReference (gen/vector (reference :reference (gen/return nil))) + note (gen/vector (annotation)) + description (nilable (string)) + series (gen/vector (imaging-study-series))]) + +(def-resource-gen library + [id id-value + meta (rare-nil (meta)) + url (uri) + identifier (gen/vector (identifier)) + version (nilable (string)) + name (nilable (string)) + title (nilable (string)) + subtitle (nilable (string)) + status (rare-nil (code)) + experimental (nilable (boolean)) + type (codeable-concept) + subject (nilable (gen/one-of [(codeable-concept) (reference :reference (gen/return nil))])) + date (nilable (dateTime)) + publisher (nilable (string)) + contact (gen/vector (contact-detail)) + description (nilable (markdown)) + useContext (gen/vector (usage-context)) + jurisdiction (gen/vector (codeable-concept)) + purpose (nilable (markdown)) + usage (nilable (string)) + copyright (nilable (markdown)) + approvalDate (nilable (blaze.fhir.spec.generators/date)) + lastReviewDate (nilable (blaze.fhir.spec.generators/date)) + effectivePeriod (nilable (period)) + topic (gen/vector (codeable-concept)) + author (gen/vector (contact-detail)) + editor (gen/vector (contact-detail)) + reviewer (gen/vector (contact-detail)) + endorser (gen/vector (contact-detail)) + relatedArtifact (gen/vector (related-artifact)) + parameter (gen/vector (parameter-definition)) + dataRequirement (gen/vector (data-requirement)) + content (gen/vector (attachment))]) + +(def-resource-gen medication-administration + [id id-value + meta (rare-nil (meta)) + medication (rare-nil (reference :reference (gen/return nil))) + subject (rare-nil (reference :reference (gen/return nil)))]) + +(defn- observation-value [] + (gen/one-of [(quantity) (codeable-concept) (string) (boolean) (integer) + (range) (ratio) (sampled-data) (time) (dateTime) (period)])) + +(defn- observation-reference-range + [& {:keys [id extension modifierExtension low high type appliesTo age text] + :or {id (often-nil id-value) + extension (extensions) + modifierExtension (extensions) + low (nilable (quantity)) + high (nilable (quantity)) + type (nilable (codeable-concept)) + appliesTo (gen/vector (codeable-concept)) + age (nilable (range)) + text (nilable (string))}}] + (->> (gen/tuple id extension modifierExtension low high type appliesTo age text) + (to-map [:id :extension :modifierExtension :low :high :type :appliesTo :age :text]) + (fhir-type :fhir.Observation/referenceRange))) + +(defn- observation-component + [& {:keys [id extension modifierExtension code value dataAbsentReason interpretation referenceRange] + :or {id (often-nil id-value) + extension (extensions) + modifierExtension (extensions) + code (codeable-concept) + value (nilable (observation-value)) + dataAbsentReason (nilable (codeable-concept)) + interpretation (gen/vector (codeable-concept)) + referenceRange (gen/vector (observation-reference-range))}}] + (->> (gen/tuple id extension modifierExtension code value dataAbsentReason interpretation referenceRange) + (to-map [:id :extension :modifierExtension :code :value :dataAbsentReason :interpretation :referenceRange]) + (fhir-type :fhir.Observation/component))) + +(def-resource-gen observation + [id id-value + meta (rare-nil (meta)) + identifier (gen/vector (identifier)) + basedOn (gen/vector (reference :reference (gen/return nil))) + partOf (gen/vector (reference :reference (gen/return nil))) + status (rare-nil (code)) + category (gen/vector (codeable-concept)) + code (rare-nil (codeable-concept)) + subject (rare-nil (reference :reference (gen/return nil))) + focus (gen/vector (reference :reference (gen/return nil))) + encounter (nilable (reference :reference (gen/return nil))) + effective (nilable (gen/one-of [(dateTime) (period) (timing) (instant)])) + issued (nilable (instant)) + performer (gen/vector (reference :reference (gen/return nil))) + value (rare-nil (observation-value)) + dataAbsentReason (often-nil (codeable-concept)) + interpretation (gen/vector (codeable-concept)) + note (gen/vector (annotation)) + bodySite (nilable (codeable-concept)) + method (nilable (codeable-concept)) + specimen (nilable (reference :reference (gen/return nil))) + device (nilable (reference :reference (gen/return nil))) + referenceRange (gen/vector (observation-reference-range)) + hasMember (gen/vector (reference :reference (gen/return nil))) + derivedFrom (gen/vector (reference :reference (gen/return nil))) + component (gen/vector (observation-component))]) + +(def-resource-gen patient + [id id-value + gender (rare-nil (code)) + birthDate (rare-nil (date)) + multipleBirth (rare-nil (gen/one-of [(boolean) (integer)]))]) + +(def-resource-gen procedure + [id id-value + meta (rare-nil (meta)) + identifier (gen/vector (identifier)) + instantiatesCanonical (gen/vector (canonical)) + instantiatesUri (gen/vector (uri)) + status (rare-nil (code)) + category (codeable-concept) + code (rare-nil (codeable-concept)) + subject (rare-nil (reference :reference (gen/return nil))) + encounter (rare-nil (reference :reference (gen/return nil)))]) + +(defn- task-value [] + (gen/one-of [(quantity) (codeable-concept) (string) (boolean) (integer) + (range) (ratio) (sampled-data) (time) (dateTime) (period)])) + +(defn- task-input + [& {:keys [type value] + :or {type (codeable-concept) + value (task-value)}}] + (->> (gen/tuple type value) + (to-map [:type :value]) + (fhir-type :fhir.Task/input))) + +(def-resource-gen task + [id id-value + identifier (gen/vector (identifier)) + status (rare-nil (code)) + input (gen/vector (task-input))]) + +(defn- value-set-compose + [& {:keys [inactive] + :or {inactive (often-nil (boolean))}}] + (->> (gen/tuple inactive) + (to-map [:inactive]) + (fhir-type :fhir.ValueSet/compose))) + +(def-resource-gen value-set + [id id-value + meta (rare-nil (meta)) + url (uri) + identifier (gen/vector (identifier)) + version (rare-nil (string)) + name (nilable (string)) + title (nilable (string)) + status (rare-nil (code)) + experimental (nilable (boolean)) + compose (value-set-compose)]) diff --git a/modules/fhir-test-util/src/blaze/fhir/test_util.clj b/modules/fhir-test-util/src/blaze/fhir/test_util.clj index b1cad942f..3330fdfab 100644 --- a/modules/fhir-test-util/src/blaze/fhir/test_util.clj +++ b/modules/fhir-test-util/src/blaze/fhir/test_util.clj @@ -3,7 +3,6 @@ [blaze.byte-buffer :as bb] [blaze.executors :as ex] [blaze.fhir.spec :as fhir-spec] - [blaze.fhir.spec.type :as type] [blaze.fhir.structure-definition-repo] [integrant.core :as ig] [java-time.api :as time]) @@ -64,7 +63,7 @@ (ig/init {:blaze.fhir/structure-definition-repo {}}))) (defn link-url [body link-relation] - (->> body :link (filter (comp #{link-relation} type/value :relation)) first :url type/value)) + (->> body :link (filter (comp #{link-relation} :value :relation)) first :url :value)) (defmethod ig/init-key :blaze.test/page-id-cipher [_ _] diff --git a/modules/frontend-e2e/docker-compose.yml b/modules/frontend-e2e/docker-compose.yml index 5e92e3f91..697545054 100644 --- a/modules/frontend-e2e/docker-compose.yml +++ b/modules/frontend-e2e/docker-compose.yml @@ -40,7 +40,6 @@ services: ENABLE_ADMIN_API: "true" ENABLE_TERMINOLOGY_SERVICE: "true" ENABLE_TERMINOLOGY_LOINC: "true" - DB_RESOURCE_CACHE_SIZE: "10000" OPENID_PROVIDER_URL: "https://keycloak.localhost/realms/blaze" OPENID_CLIENT_TRUST_STORE: "/app/keycloak-trust-store.p12" OPENID_CLIENT_TRUST_STORE_PASS: "insecure" diff --git a/modules/interaction/src/blaze/interaction/history/util.clj b/modules/interaction/src/blaze/interaction/history/util.clj index 303a3438f..cb46152bd 100644 --- a/modules/interaction/src/blaze/interaction/history/util.clj +++ b/modules/interaction/src/blaze/interaction/history/util.clj @@ -4,6 +4,7 @@ [blaze.db.api :as d] [blaze.fhir.spec.type :as type] [blaze.handler.fhir.util :as fhir-util] + [blaze.handler.util :as handler-util] [blaze.interaction.search.util :as search-util] [blaze.middleware.fhir.decrypt-page-id :as decrypt-page-id] [blaze.module :as m] @@ -87,8 +88,8 @@ :response {:fhir/type :fhir.Bundle.entry/response :status (status resource) - :etag (type/string (str "W/\"" (-> resource :meta :versionId type/value) "\"")) - :lastModified (-> resource meta :blaze.db/tx :blaze.db.tx/instant)}} + :etag (type/string (str "W/\"" (-> resource :meta :versionId :value) "\"")) + :lastModified (-> resource meta :blaze.db/tx handler-util/instant)}} (-> resource meta :blaze.db/op #{:delete} not) (assoc :resource resource))) diff --git a/modules/interaction/src/blaze/interaction/transaction.clj b/modules/interaction/src/blaze/interaction/transaction.clj index aef904d77..9c31e573b 100644 --- a/modules/interaction/src/blaze/interaction/transaction.clj +++ b/modules/interaction/src/blaze/interaction/transaction.clj @@ -41,7 +41,7 @@ (transduce validate-entry-xf conj [] entries)) (defn- prepare-entry [res {{:keys [method]} :request :as entry}] - (case (type/value method) + (case (:value method) "POST" (let [entry (update entry :resource assoc :id (luid/head (::luid/generator res)))] (-> (update res ::luid/generator luid/next) @@ -54,7 +54,7 @@ Returns an anomaly in case of errors." [context {resource-type :fhir/type :keys [type] entries :entry :as bundle}] - (let [type (type/value type)] + (let [type (:value type)] (cond (nil? bundle) (ba/incorrect @@ -86,7 +86,7 @@ "Builds the response entry of `request-entry` using the `db` after the transaction." {:arglists '([context idx entry])} - (fn [_ _ {{:keys [method]} :request}] (type/value method))) + (fn [_ _ {{:keys [method]} :request}] (:value method))) (defn- location [context type id vid] (fhir-util/versioned-instance-url context type id vid)) @@ -101,7 +101,7 @@ :status #fhir/string "201" :location (type/uri (location context type id vid)) :etag (type/string (str "W/\"" vid "\"")) - :lastModified (:blaze.db.tx/instant tx)}})) + :lastModified (handler-util/instant tx)}})) (defn- noop-entry [db handle] (let [tx (d/tx db (:t handle)) @@ -111,7 +111,7 @@ {:fhir/type :fhir.Bundle.entry/response :status #fhir/string "200" :etag (type/string (str "W/\"" vid "\"")) - :lastModified (:blaze.db.tx/instant tx)}})) + :lastModified (handler-util/instant tx)}})) (defn- conditional-clauses [if-none-exist] (when-not (str/blank? if-none-exist) @@ -144,7 +144,7 @@ (do-sync [resource (pull db handle)] (assoc (created-entry context type handle) :resource resource)) (ac/completed-future (created-entry context type handle))) - (let [if-none-exist (-> entry :request :ifNoneExist) + (let [if-none-exist (-> entry :request :ifNoneExist :value) clauses (conditional-clauses if-none-exist) handle (coll/first (d/type-query db type clauses))] (if (identical? :blaze.preference.return/representation return-preference) @@ -164,7 +164,7 @@ {:fhir/type :fhir.Bundle.entry/response :status (type/string (if created "201" "200")) :etag (type/string (str "W/\"" vid "\"")) - :lastModified (:blaze.db.tx/instant tx)} + :lastModified (handler-util/instant tx)} created (assoc :location (type/uri (location context type id vid))))})) @@ -188,7 +188,7 @@ {:fhir/type :fhir.Bundle.entry/response :status #fhir/string "204" :etag (type/string (str "W/\"" t "\"")) - :lastModified (:blaze.db.tx/instant (d/tx db t))}}))) + :lastModified (handler-util/instant (d/tx db t))}}))) (defmethod build-response-entry "GET" [context idx entry] (fhir-util/process-batch-entry context idx entry)) @@ -221,7 +221,7 @@ complete with the response entries or will complete exceptionally with an anomaly in case of errors." {:arglists '([context type entries])} - (fn [_ type _] (type/value type))) + (fn [_ type _] (:value type))) (defmethod process-entries "batch" [context _ entries] @@ -255,7 +255,7 @@ (assoc :blaze.preference/return return-preference)))) (defn- process-context [context type request] - (if (= "batch" (type/value type)) + (if (= "batch" (:value type)) (-> (fhir-util/sync (:node context) (:db-sync-timeout context)) (ac/then-apply #(assoc (process-context* context request) :blaze/db %))) (ac/completed-future (process-context* context request)))) @@ -263,7 +263,7 @@ (defn- response-bundle [context type entries] {:fhir/type :fhir/Bundle :id (m/luid context) - :type (type/code (str (type/value type) "-response")) + :type (type/code (str (:value type) "-response")) :entry entries}) (defmethod m/pre-init-spec :blaze.interaction/transaction [_] diff --git a/modules/interaction/src/blaze/interaction/transaction/bundle.clj b/modules/interaction/src/blaze/interaction/transaction/bundle.clj index c085aea7b..3473ed461 100644 --- a/modules/interaction/src/blaze/interaction/transaction/bundle.clj +++ b/modules/interaction/src/blaze/interaction/transaction/bundle.clj @@ -2,7 +2,6 @@ "FHIR Bundle specific stuff." (:require [blaze.anomaly :as ba :refer [when-ok]] - [blaze.fhir.spec.type :as type] [blaze.handler.fhir.util :as fhir-util] [blaze.interaction.transaction.bundle.links :as links] [blaze.interaction.util :as iu] @@ -10,14 +9,14 @@ [clojure.string :as str] [ring.util.codec :as ring-codec])) -(defmulti entry-tx-op (fn [_ {{:keys [method]} :request}] (type/value method))) +(defmulti entry-tx-op (fn [_ {{:keys [method]} :request}] (:value method))) (defn- conditional-clauses [if-none-exist] (when-not (str/blank? if-none-exist) (-> if-none-exist ring-codec/form-decode uc/search-clauses))) (defmethod entry-tx-op "POST" - [_ {:keys [resource] {if-none-exist :ifNoneExist} :request :as entry}] + [_ {:keys [resource] {{if-none-exist :value} :ifNoneExist} :request :as entry}] (let [clauses (conditional-clauses if-none-exist)] (assoc entry :tx-op @@ -27,17 +26,18 @@ (conj clauses))))) (defmethod entry-tx-op "PUT" - [db {{if-match :ifMatch if-none-match :ifNoneMatch} :request :keys [resource] - :as entry}] + [db + {{{if-match :value} :ifMatch {if-none-match :value} :ifNoneMatch} :request + :keys [resource] :as entry}] (when-ok [tx-op (iu/update-tx-op db (iu/strip-meta resource) if-match if-none-match)] (assoc entry :tx-op tx-op))) (defmethod entry-tx-op "DELETE" [_ {{:keys [url]} :request :as entry}] - (if-let [[type id] (fhir-util/match-type-id (type/value url))] + (if-let [[type id] (fhir-util/match-type-id (:value url))] (assoc entry :tx-op [:delete type id]) - (when-let [[type query-params] (fhir-util/match-type-query-params (type/value url))] + (when-let [[type query-params] (fhir-util/match-type-query-params (:value url))] (assoc entry :tx-op (cond-> [:conditional-delete type] (not (str/blank? query-params)) (conj (-> query-params ring-codec/form-decode uc/search-clauses))))))) (defmethod entry-tx-op :default diff --git a/modules/interaction/src/blaze/interaction/transaction/bundle/links.clj b/modules/interaction/src/blaze/interaction/transaction/bundle/links.clj index 780e8aa43..4669a5734 100644 --- a/modules/interaction/src/blaze/interaction/transaction/bundle/links.clj +++ b/modules/interaction/src/blaze/interaction/transaction/bundle/links.clj @@ -6,7 +6,6 @@ (:require [blaze.fhir.spec :as fhir-spec] [blaze.fhir.spec.references :as fsr] - [blaze.fhir.spec.type :as type] [blaze.util :refer [str]] [clojure.spec.alpha :as s] [clojure.string :as str])) @@ -19,16 +18,16 @@ (cond->> uri (fsr/split-literal-ref uri) (str base-url "/"))) (defn- resolve-link [{:keys [base-url index]} link] - (let [uri (some->> (type/value link) (resolve-uri base-url))] + (let [uri (some->> (:value link) (resolve-uri base-url))] (if-let [{:fhir/keys [type] :keys [id]} (get index uri)] - (type/assoc-value link (str (name type) "/" id)) + (assoc link :value (str (name type) "/" id)) link))) (declare resolve-links) (defn- resolve-single-element-links [context value] - (let [type (fhir-spec/fhir-type value)] + (if-let [type (:fhir/type value)] (cond (identical? :fhir/Reference type) (update value :reference (partial resolve-link context)) @@ -37,15 +36,16 @@ (identical? :fhir/url type)) (resolve-link context value) - (fhir-spec/primitive? type) + (fhir-spec/primitive-val? value) value :else - (resolve-links context value)))) + (resolve-links context value)) + value)) (defn- resolve-element-links [context value] (if (sequential? value) - (mapv (partial resolve-single-element-links context) value) + (mapv #(resolve-single-element-links context %) value) (resolve-single-element-links context value))) (defn- resolve-links [context complex-value] @@ -60,10 +60,8 @@ (defn- index-resources-by-full-url [entries] (reduce - (fn [r {:keys [fullUrl resource]}] - (if-let [fullUrl (type/value fullUrl)] - (assoc r fullUrl resource) - r)) + (fn [r {{full-url :value} :fullUrl :keys [resource]}] + (cond-> r full-url (assoc full-url resource))) {} entries)) @@ -86,7 +84,7 @@ (mapv (fn [{full-url :fullUrl :as entry}] (if-let [resource (:resource entry)] - (let [context {:index index :base-url (some-> (type/value full-url) base-url)}] + (let [context {:index index :base-url (some-> full-url :value base-url)}] (assoc entry :resource (resolve-links context resource))) entry)) entries))) diff --git a/modules/interaction/test/blaze/interaction/create_test.clj b/modules/interaction/test/blaze/interaction/create_test.clj index ade9aa559..00de3d6db 100644 --- a/modules/interaction/test/blaze/interaction/create_test.clj +++ b/modules/interaction/test/blaze/interaction/create_test.clj @@ -12,7 +12,6 @@ [blaze.db.resource-store :as rs] [blaze.db.spec] [blaze.fhir.response.create-spec] - [blaze.fhir.spec.type] [blaze.interaction.create] [blaze.interaction.test-util :refer [wrap-error]] [blaze.interaction.util-spec] @@ -26,9 +25,7 @@ [integrant.core :as ig] [juxt.iota :refer [given]] [reitit.core :as reitit] - [taoensso.timbre :as log]) - (:import - [java.time Instant])) + [taoensso.timbre :as log])) (st/instrument) (log/set-min-level! :trace) @@ -206,7 +203,7 @@ :fhir/type := :fhir/Patient :id := "AAAAAAAAAAAAAAAA" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH))) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z"))) (testing "Meta source is preserved" (with-handler [handler] @@ -284,7 +281,7 @@ :fhir/type := :fhir/Patient :id := "AAAAAAAAAAAAAAAA" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH)))) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")))) (testing "with return=OperationOutcome Prefer header" (with-handler [handler] @@ -426,7 +423,7 @@ :fhir/type := :fhir/Observation :id := "AAAAAAAAAAAAAAAA" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z" [:subject :reference] := #fhir/string "Patient/0")))) (testing "with a Bundle with references" @@ -463,4 +460,4 @@ :fhir/type := :fhir/Bundle :id := "AAAAAAAAAAAAAAAA" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH))))) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z"))))) diff --git a/modules/interaction/test/blaze/interaction/history/instance_test.clj b/modules/interaction/test/blaze/interaction/history/instance_test.clj index e5bd670c3..23f97e884 100644 --- a/modules/interaction/test/blaze/interaction/history/instance_test.clj +++ b/modules/interaction/test/blaze/interaction/history/instance_test.clj @@ -9,7 +9,7 @@ [blaze.async.comp :as ac] [blaze.db.api :as d] [blaze.db.api-stub :as api-stub :refer [with-system-data]] - [blaze.db.resource-store :as rs] + [blaze.db.resource-cache :as rc] [blaze.db.tx-log :as-alias tx-log] [blaze.fhir.test-util :refer [link-url]] [blaze.interaction.history.instance] @@ -31,9 +31,7 @@ [java-time.api :as time] [juxt.iota :refer [given]] [reitit.core :as reitit] - [taoensso.timbre :as log]) - (:import - [java.time Instant])) + [taoensso.timbre :as log])) (set! *warn-on-reflection* true) (st/instrument) @@ -243,7 +241,7 @@ :fhir/type := :fhir/Patient :id := "0" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH)) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")) (testing "the second entry has the right request" (given (:request first-entry) @@ -254,7 +252,7 @@ (given (:response first-entry) :status := #fhir/string "201" :etag := #fhir/string "W/\"1\"" - :lastModified := Instant/EPOCH))))) + :lastModified := #fhir/instant #system/date-time "1970-01-01T00:00:00Z"))))) (testing "returns history with one code system" (with-handler [handler] @@ -303,7 +301,7 @@ :fhir/type := :fhir/CodeSystem :id := "0" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z" [:concept 0 :code] := #fhir/code "code-115927")) (testing "the second entry has the right request" @@ -315,7 +313,7 @@ (given (:response first-entry) :status := #fhir/string "201" :etag := #fhir/string "W/\"1\"" - :lastModified := Instant/EPOCH))) + :lastModified := #fhir/instant #system/date-time "1970-01-01T00:00:00Z"))) (testing "in summary mode" (let [{:keys [status] {[first-entry] :entry :as body} :body} @@ -356,7 +354,7 @@ :fhir/type := :fhir/CodeSystem :id := "0" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z" [:meta :tag (coding v3-ObservationValue) 0 :code] := #fhir/code "SUBSETTED" :concept := nil)) @@ -369,7 +367,7 @@ (given (:response first-entry) :status := #fhir/string "201" :etag := #fhir/string "W/\"1\"" - :lastModified := Instant/EPOCH)))))) + :lastModified := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")))))) (testing "returns history with one currently deleted patient" (with-handler [handler] @@ -420,7 +418,7 @@ (given (:response first-entry) :status := #fhir/string "204" :etag := #fhir/string "W/\"2\"" - :lastModified := Instant/EPOCH)) + :lastModified := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")) (testing "the first entry has the right fullUrl" (is (= (str base-url context-path "/Patient/0") @@ -431,7 +429,7 @@ :fhir/type := :fhir/Patient :id := "0" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH)) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")) (testing "the second entry has the right request" (given (:request second-entry) @@ -442,7 +440,7 @@ (given (:response second-entry) :status := #fhir/string "201" :etag := #fhir/string "W/\"1\"" - :lastModified := Instant/EPOCH))))) + :lastModified := #fhir/instant #system/date-time "1970-01-01T00:00:00Z"))))) (testing "with two versions of one patient" (with-handler [handler node page-id-cipher] @@ -526,7 +524,7 @@ :gender := #fhir/code "female"))))))) (testing "missing resource contents" - (with-redefs [rs/multi-get (fn [_ _] (ac/completed-future {}))] + (with-redefs [rc/multi-get (fn [_ _] (ac/completed-future {}))] (with-handler [handler] [[[:put {:fhir/type :fhir/Patient :id "0"}]]] diff --git a/modules/interaction/test/blaze/interaction/history/system_test.clj b/modules/interaction/test/blaze/interaction/history/system_test.clj index c5a49c28a..0ac1509e8 100644 --- a/modules/interaction/test/blaze/interaction/history/system_test.clj +++ b/modules/interaction/test/blaze/interaction/history/system_test.clj @@ -8,7 +8,7 @@ [blaze.async.comp :as ac] [blaze.db.api :as d] [blaze.db.api-stub :as api-stub :refer [with-system-data]] - [blaze.db.resource-store :as rs] + [blaze.db.resource-cache :as rc] [blaze.db.tx-log :as-alias tx-log] [blaze.fhir.test-util :refer [link-url]] [blaze.interaction.history.system] @@ -31,9 +31,7 @@ [java-time.api :as time] [juxt.iota :refer [given]] [reitit.core :as reitit] - [taoensso.timbre :as log]) - (:import - [java.time Instant])) + [taoensso.timbre :as log])) (set! *warn-on-reflection* true) (st/instrument) @@ -230,7 +228,7 @@ :fhir/type := :fhir/Patient :id := "0" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH)) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")) (testing "the second entry has the right request" (given (:request first-entry) @@ -241,7 +239,7 @@ (given (:response first-entry) :status := #fhir/string "201" :etag := #fhir/string "W/\"1\"" - :lastModified := Instant/EPOCH))))) + :lastModified := #fhir/instant #system/date-time "1970-01-01T00:00:00Z"))))) (testing "with one code system" (with-handler [handler] @@ -289,7 +287,7 @@ :fhir/type := :fhir/CodeSystem :id := "0" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z" [:concept 0 :code] := #fhir/code "code-115927")) (testing "the second entry has the right request" @@ -301,7 +299,7 @@ (given (:response first-entry) :status := #fhir/string "201" :etag := #fhir/string "W/\"1\"" - :lastModified := Instant/EPOCH))) + :lastModified := #fhir/instant #system/date-time "1970-01-01T00:00:00Z"))) (testing "in summary mode" (let [{:keys [status] {[first-entry] :entry :as body} :body} @@ -340,7 +338,7 @@ :fhir/type := :fhir/CodeSystem :id := "0" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z" [:meta :tag (coding v3-ObservationValue) 0 :code] := #fhir/code "SUBSETTED" :concept := nil)) @@ -353,7 +351,7 @@ (given (:response first-entry) :status := #fhir/string "201" :etag := #fhir/string "W/\"1\"" - :lastModified := Instant/EPOCH)))))) + :lastModified := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")))))) (testing "with two patients in one transaction" (with-handler [handler node page-id-cipher] @@ -502,7 +500,7 @@ :gender := #fhir/code "female"))))))) (testing "missing resource contents" - (with-redefs [rs/multi-get (fn [_ _] (ac/completed-future {}))] + (with-redefs [rc/multi-get (fn [_ _] (ac/completed-future {}))] (with-handler [handler] [[[:put {:fhir/type :fhir/Patient :id "0"}]]] diff --git a/modules/interaction/test/blaze/interaction/history/type_test.clj b/modules/interaction/test/blaze/interaction/history/type_test.clj index f866675c4..5faf03f44 100644 --- a/modules/interaction/test/blaze/interaction/history/type_test.clj +++ b/modules/interaction/test/blaze/interaction/history/type_test.clj @@ -8,7 +8,7 @@ [blaze.async.comp :as ac] [blaze.db.api :as d] [blaze.db.api-stub :as api-stub :refer [with-system-data]] - [blaze.db.resource-store :as rs] + [blaze.db.resource-cache :as rc] [blaze.db.tx-log :as-alias tx-log] [blaze.fhir.test-util :refer [link-url]] [blaze.interaction.history.type] @@ -31,9 +31,7 @@ [java-time.api :as time] [juxt.iota :refer [given]] [reitit.core :as reitit] - [taoensso.timbre :as log]) - (:import - [java.time Instant])) + [taoensso.timbre :as log])) (set! *warn-on-reflection* true) (st/instrument) @@ -252,7 +250,7 @@ :fhir/type := :fhir/Patient :id := "0" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH)) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")) (testing "the second entry has the right request" (given (:request first-entry) @@ -263,7 +261,7 @@ (given (:response first-entry) :status := #fhir/string "201" :etag := #fhir/string "W/\"1\"" - :lastModified := Instant/EPOCH))))) + :lastModified := #fhir/instant #system/date-time "1970-01-01T00:00:00Z"))))) (testing "with one code system" (with-handler [handler] @@ -311,7 +309,7 @@ :fhir/type := :fhir/CodeSystem :id := "0" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z" [:concept 0 :code] := #fhir/code "code-115927")) (testing "the second entry has the right request" @@ -323,7 +321,7 @@ (given (:response first-entry) :status := #fhir/string "201" :etag := #fhir/string "W/\"1\"" - :lastModified := Instant/EPOCH))) + :lastModified := #fhir/instant #system/date-time "1970-01-01T00:00:00Z"))) (testing "in summary mode" (let [{:keys [status] {[first-entry] :entry :as body} :body} @@ -363,7 +361,7 @@ :fhir/type := :fhir/CodeSystem :id := "0" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z" [:meta :tag (coding v3-ObservationValue) 0 :code] := #fhir/code "SUBSETTED" :concept := nil)) @@ -376,7 +374,7 @@ (given (:response first-entry) :status := #fhir/string "201" :etag := #fhir/string "W/\"1\"" - :lastModified := Instant/EPOCH)))))) + :lastModified := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")))))) (testing "with two patients in one transaction" (with-handler [handler node page-id-cipher] @@ -462,7 +460,7 @@ :gender := #fhir/code "female"))))))) (testing "missing resource contents" - (with-redefs [rs/multi-get (fn [_ _] (ac/completed-future {}))] + (with-redefs [rc/multi-get (fn [_ _] (ac/completed-future {}))] (with-handler [handler] [[[:put {:fhir/type :fhir/Patient :id "0"}]]] diff --git a/modules/interaction/test/blaze/interaction/history/util_test.clj b/modules/interaction/test/blaze/interaction/history/util_test.clj index 65bdbe5a8..91e98c587 100644 --- a/modules/interaction/test/blaze/interaction/history/util_test.clj +++ b/modules/interaction/test/blaze/interaction/history/util_test.clj @@ -68,7 +68,7 @@ [:resource :fhir/type] := :fhir/Patient [:resource :id] := "0" [:response :status] := #fhir/string "201" - [:response :lastModified] := Instant/EPOCH + [:response :lastModified] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z" [:response :etag] := #fhir/string "W/\"1\"")) (testing "Initial version with client assigned id" @@ -87,7 +87,7 @@ [:resource :fhir/type] := :fhir/Patient [:resource :id] := "0" [:response :status] := #fhir/string "201" - [:response :lastModified] := Instant/EPOCH + [:response :lastModified] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z" [:response :etag] := #fhir/string "W/\"1\"")) (testing "Non-initial version" @@ -106,7 +106,7 @@ [:resource :fhir/type] := :fhir/Patient [:resource :id] := "0" [:response :status] := #fhir/string "200" - [:response :lastModified] := Instant/EPOCH + [:response :lastModified] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z" [:response :etag] := #fhir/string "W/\"2\"")) (testing "Deleted version" @@ -123,7 +123,7 @@ [:request :method] := #fhir/code "DELETE" [:request :url] := #fhir/uri "Patient/0" [:response :status] := #fhir/string "204" - [:response :lastModified] := Instant/EPOCH + [:response :lastModified] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z" [:response :etag] := #fhir/string "W/\"2\""))) (def ^:private config @@ -145,19 +145,19 @@ (let [min-int 0] (with-system [{::keys [context]} config] (given (history-util/build-bundle context min-int {}) - [:total type/value] := min-int)))) + [:total :value] := min-int)))) (testing "maximum allowed FHIR unsignedInt value" (let [max-int (dec (bit-shift-left 1 31))] (with-system [{::keys [context]} config] (given (history-util/build-bundle context max-int {}) - [:total type/value] := max-int)))) + [:total :value] := max-int)))) (testing "one above the maximum allowed FHIR unsignedInt value" (let [overflowed-int (bit-shift-left 1 31)] (with-system [{::keys [context]} config] (given (history-util/build-bundle context overflowed-int {}) - [:total type/value] := nil + [:total :value] := nil [:total :extension 0 :url] := "https://samply.github.io/blaze/fhir/StructureDefinition/grand-total" [:total :extension 0 :value] := (type/string (str overflowed-int)))))) @@ -165,6 +165,6 @@ (let [trillion-int 1000000000000] (with-system [{::keys [context]} config] (given (history-util/build-bundle context trillion-int {}) - [:total type/value] := nil + [:total :value] := nil [:total :extension 0 :url] := "https://samply.github.io/blaze/fhir/StructureDefinition/grand-total" [:total :extension 0 :value] := (type/string (str trillion-int)))))))) diff --git a/modules/interaction/test/blaze/interaction/read_test.clj b/modules/interaction/test/blaze/interaction/read_test.clj index 4d872123f..c5b1462b4 100644 --- a/modules/interaction/test/blaze/interaction/read_test.clj +++ b/modules/interaction/test/blaze/interaction/read_test.clj @@ -19,9 +19,7 @@ [clojure.test :as test :refer [deftest is testing]] [juxt.iota :refer [given]] [reitit.core :as reitit] - [taoensso.timbre :as log]) - (:import - [java.time Instant])) + [taoensso.timbre :as log])) (st/instrument) (log/set-min-level! :trace) @@ -137,4 +135,4 @@ :fhir/type := :fhir/Patient :id := "0" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH))))) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z"))))) diff --git a/modules/interaction/test/blaze/interaction/search_compartment_test.clj b/modules/interaction/test/blaze/interaction/search_compartment_test.clj index 4f90d3530..3db894860 100644 --- a/modules/interaction/test/blaze/interaction/search_compartment_test.clj +++ b/modules/interaction/test/blaze/interaction/search_compartment_test.clj @@ -5,8 +5,7 @@ (:require [blaze.async.comp :as ac] [blaze.db.api-stub :as api-stub :refer [with-system-data]] - [blaze.db.resource-store :as rs] - [blaze.fhir.spec.type] + [blaze.db.resource-cache :as rc] [blaze.fhir.test-util :refer [link-url]] [blaze.interaction.search-compartment] [blaze.interaction.search.nav-spec] @@ -653,7 +652,7 @@ [:resource :id] := "1"))))) (testing "missing resource contents" - (with-redefs [rs/multi-get (fn [_ _] (ac/completed-future {}))] + (with-redefs [rc/multi-get (fn [_ _] (ac/completed-future {}))] (let [{:keys [status body]} @(handler request)] (is (= 500 status)) diff --git a/modules/interaction/test/blaze/interaction/search_system_test.clj b/modules/interaction/test/blaze/interaction/search_system_test.clj index 91679d6a4..791635dcd 100644 --- a/modules/interaction/test/blaze/interaction/search_system_test.clj +++ b/modules/interaction/test/blaze/interaction/search_system_test.clj @@ -6,7 +6,7 @@ [blaze.async.comp :as ac] [blaze.db.api :as d] [blaze.db.api-stub :as api-stub :refer [with-system-data]] - [blaze.db.resource-store :as rs] + [blaze.db.resource-cache :as rc] [blaze.fhir.test-util :refer [link-url]] [blaze.interaction.search-system] [blaze.interaction.search.nav-spec] @@ -31,9 +31,7 @@ [integrant.core :as ig] [juxt.iota :refer [given]] [reitit.core :as reitit] - [taoensso.timbre :as log]) - (:import - [java.time Instant])) + [taoensso.timbre :as log])) (st/instrument) (log/set-min-level! :trace) @@ -240,7 +238,7 @@ :fhir/type := :fhir/Patient :id := "0" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z" [:meta :tag (coding v3-ObservationValue) count] := 0 :multipleBirth := #fhir/boolean true))))) @@ -281,7 +279,7 @@ :fhir/type := :fhir/Patient :id := "0" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z" [:meta :tag (coding v3-ObservationValue) 0 :code] := #fhir/code "SUBSETTED" :multipleBirth := nil)))) @@ -427,7 +425,7 @@ [:issue 0 :diagnostics] := #fhir/string "Missing search parameter code in _include search parameter with source type `Observation`."))))) (testing "missing resource contents" - (with-redefs [rs/multi-get (fn [_ _] (ac/completed-future {}))] + (with-redefs [rc/multi-get (fn [_ _] (ac/completed-future {}))] (with-handler [handler] [[[:put {:fhir/type :fhir/Patient :id "0"}]]] diff --git a/modules/interaction/test/blaze/interaction/search_type_test.clj b/modules/interaction/test/blaze/interaction/search_type_test.clj index 391a36ae3..eb0b66bae 100644 --- a/modules/interaction/test/blaze/interaction/search_type_test.clj +++ b/modules/interaction/test/blaze/interaction/search_type_test.clj @@ -8,8 +8,7 @@ [blaze.db.api :as d] [blaze.db.api-stub :as api-stub :refer [with-system-data]] [blaze.db.query.plan.spec] - [blaze.db.resource-store :as rs] - [blaze.fhir.spec :as fhir-spec] + [blaze.db.resource-cache :as rc] [blaze.fhir.spec.type :as type] [blaze.fhir.test-util :refer [link-url]] [blaze.interaction.search-type] @@ -39,9 +38,7 @@ [integrant.core :as ig] [juxt.iota :refer [given]] [reitit.core :as reitit] - [taoensso.timbre :as log]) - (:import - [java.time Instant])) + [taoensso.timbre :as log])) (set! *warn-on-reflection* true) (st/instrument) @@ -683,11 +680,11 @@ :fhir/type := :fhir/Location :id := id [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH)) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")) (testing "the entry has the right search mode" (given (:search entry) - fhir-spec/fhir-type := :fhir.Bundle.entry/search + :fhir/type := :fhir.Bundle.entry/search :mode := #fhir/code "match")))))) (testing "near search" @@ -722,21 +719,21 @@ :fhir/type := :fhir/Location :id := "0" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH)) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")) (testing "the entry has the right search mode" (given (:search first-entry) - fhir-spec/fhir-type := :fhir.Bundle.entry/search + :fhir/type := :fhir.Bundle.entry/search :mode := #fhir/code "match")) (testing "the entry has distance search extension" (given (-> first-entry :search :extension location-distance) - fhir-spec/fhir-type := :fhir/Extension - [:value fhir-spec/fhir-type] := :fhir/Distance + :fhir/type := :fhir/Extension + [:value :fhir/type] := :fhir/Distance [:value :unit] := #fhir/string "m" [:value :code] := #fhir/code "m" [:value :system] := #fhir/uri "http://unitsofmeasure.org" - [:value :value type/value] :? #(< 27520M % 27530M))))))) + [:value :value :value] :? #(< 27520M % 27530M))))))) (testing "with one patient" (with-handler [handler] @@ -775,13 +772,13 @@ :fhir/type := :fhir/Patient :id := "0" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z" [:meta :tag (coding v3-ObservationValue) count] := 0 :multipleBirth := #fhir/boolean true)) (testing "the entry has the right search mode" (given (:search first-entry) - fhir-spec/fhir-type := :fhir.Bundle.entry/search + :fhir/type := :fhir.Bundle.entry/search :mode := #fhir/code "match"))))) (testing "with param _summary equal to true" @@ -815,13 +812,13 @@ :fhir/type := :fhir/Patient :id := "0" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z" [:meta :tag (coding v3-ObservationValue) 0 :code] := #fhir/code "SUBSETTED" :multipleBirth := nil)) (testing "the entry has the right search mode" (given (:search first-entry) - fhir-spec/fhir-type := :fhir.Bundle.entry/search + :fhir/type := :fhir.Bundle.entry/search :mode := #fhir/code "match")))) (testing "with param _summary equal to count" @@ -1045,7 +1042,7 @@ {:params {"active" "false"}})] (testing "the total is zero" - (is (zero? (type/value (:total body))))) + (is (zero? (:value (:total body))))) (testing "has a self link" (is (= (str base-url context-path "/Patient?active=false&_count=50") @@ -1117,7 +1114,7 @@ :params {"active" "false"}})] (testing "the total is zero" - (is (zero? (type/value (:total body))))) + (is (zero? (:value (:total body))))) (testing "has a self link" (is (= (str base-url context-path "/Patient?active=false&_count=50") @@ -3128,7 +3125,7 @@ (is (nil? (:value resource))))))) (testing "missing resource contents" - (with-redefs [rs/multi-get (fn [_ _] (ac/completed-future {}))] + (with-redefs [rc/multi-get (fn [_ _] (ac/completed-future {}))] (with-handler [handler] [[[:put {:fhir/type :fhir/Patient :id "0"}]]] @@ -3154,7 +3151,7 @@ {:system #fhir/uri "http://loinc.org" :code #fhir/code "94564-2"}]} :subject #fhir/Reference{:reference #fhir/string "Patient/0"} - :effective #fhir/dateTime "2025"}]]] + :effective #fhir/dateTime #system/date-time "2025"}]]] (testing "no search param" (let [{:keys [status] {[first-entry] :entry :as body} :body} diff --git a/modules/interaction/test/blaze/interaction/test_util.clj b/modules/interaction/test/blaze/interaction/test_util.clj index 7792e35c4..6c2e72af5 100644 --- a/modules/interaction/test/blaze/interaction/test_util.clj +++ b/modules/interaction/test/blaze/interaction/test_util.clj @@ -1,7 +1,6 @@ (ns blaze.interaction.test-util (:require [blaze.async.comp :as ac] - [blaze.fhir.spec.type :as type] [blaze.handler.util :as handler-util])) (def v3-ObservationValue @@ -15,6 +14,6 @@ (defn coding ([system] (fn [codings] - (filterv #(= system (type/value (:system %))) codings))) + (filterv #(= system (:value (:system %))) codings))) ([codings system] - (filterv #(= system (type/value (:system %))) codings))) + (filterv #(= system (:value (:system %))) codings))) diff --git a/modules/interaction/test/blaze/interaction/transaction/bundle/links_test.clj b/modules/interaction/test/blaze/interaction/transaction/bundle/links_test.clj index a905a0994..2ae9dfbd4 100644 --- a/modules/interaction/test/blaze/interaction/transaction/bundle/links_test.clj +++ b/modules/interaction/test/blaze/interaction/transaction/bundle/links_test.clj @@ -1,6 +1,5 @@ (ns blaze.interaction.transaction.bundle.links-test (:require - [blaze.fhir.spec :as fhir-spec] [blaze.fhir.spec.type :as type] [blaze.fhir.test-util] [blaze.interaction.transaction.bundle.links :as links] @@ -433,7 +432,7 @@ {:fhir/type :fhir/Observation :id "0" :code #fhir/CodeableConcept{}}}]] (given (links/resolve-entry-links entries) - [0 :resource :code fhir-spec/fhir-type] := :fhir/CodeableConcept))) + [0 :resource :code :fhir/type] := :fhir/CodeableConcept))) (testing "preserves references without reference" (let [entries diff --git a/modules/interaction/test/blaze/interaction/transaction/bundle_test.clj b/modules/interaction/test/blaze/interaction/transaction/bundle_test.clj index bbf347615..40f4158fa 100644 --- a/modules/interaction/test/blaze/interaction/transaction/bundle_test.clj +++ b/modules/interaction/test/blaze/interaction/transaction/bundle_test.clj @@ -10,9 +10,7 @@ [clojure.spec.test.alpha :as st] [clojure.test :as test :refer [deftest testing]] [cognitect.anomalies :as anom] - [juxt.iota :refer [given]]) - (:import - [java.time Instant])) + [juxt.iota :refer [given]])) (st/instrument) @@ -27,7 +25,7 @@ :resource {:fhir/type :fhir/Patient :id "id-220129" :meta (type/meta {:versionId #fhir/id "1" - :lastUpdated Instant/EPOCH})} + :lastUpdated #fhir/instant #system/date-time "1970-01-01T00:00:00Z"})} :request {:fhir/type :fhir.Bundle.entry/request :method #fhir/code "POST" @@ -42,7 +40,7 @@ :resource {:fhir/type :fhir/Patient :id "id-220200" :meta (type/meta {:versionId #fhir/id "1" - :lastUpdated Instant/EPOCH})} + :lastUpdated #fhir/instant #system/date-time "1970-01-01T00:00:00Z"})} :request {:fhir/type :fhir.Bundle.entry/request :method #fhir/code "POST" @@ -60,7 +58,7 @@ :resource {:fhir/type :fhir/Patient :id "id-220200" :meta (type/meta {:versionId #fhir/id "1" - :lastUpdated Instant/EPOCH})} + :lastUpdated #fhir/instant #system/date-time "1970-01-01T00:00:00Z"})} :request {:fhir/type :fhir.Bundle.entry/request :method #fhir/code "POST" @@ -76,7 +74,7 @@ :resource {:fhir/type :fhir/Patient :id "id-220200" :meta (type/meta {:versionId #fhir/id "1" - :lastUpdated Instant/EPOCH})} + :lastUpdated #fhir/instant #system/date-time "1970-01-01T00:00:00Z"})} :request {:fhir/type :fhir.Bundle.entry/request :method #fhir/code "POST" @@ -92,7 +90,7 @@ :resource {:fhir/type :fhir/Patient :id "id-214728" :meta (type/meta {:versionId #fhir/id "1" - :lastUpdated Instant/EPOCH})} + :lastUpdated #fhir/instant #system/date-time "1970-01-01T00:00:00Z"})} :request {:fhir/type :fhir.Bundle.entry/request :method #fhir/code "PUT" @@ -107,7 +105,7 @@ :resource {:fhir/type :fhir/Patient :id "id-214728" :meta (type/meta {:versionId #fhir/id "1" - :lastUpdated Instant/EPOCH})} + :lastUpdated #fhir/instant #system/date-time "1970-01-01T00:00:00Z"})} :request {:fhir/type :fhir.Bundle.entry/request :method #fhir/code "PUT" @@ -125,7 +123,7 @@ :resource {:fhir/type :fhir/Patient :id "id-214728" :meta (type/meta {:versionId #fhir/id "1" - :lastUpdated Instant/EPOCH})} + :lastUpdated #fhir/instant #system/date-time "1970-01-01T00:00:00Z"})} :request {:fhir/type :fhir.Bundle.entry/request :method #fhir/code "PUT" @@ -183,7 +181,7 @@ :resource {:fhir/type :fhir/Patient :id "0" :meta (type/meta {:versionId #fhir/id "1" - :lastUpdated Instant/EPOCH}) + :lastUpdated #fhir/instant #system/date-time "1970-01-01T00:00:00Z"}) :gender #fhir/code "male"} :request {:fhir/type :fhir.Bundle.entry/request diff --git a/modules/interaction/test/blaze/interaction/transaction_test.clj b/modules/interaction/test/blaze/interaction/transaction_test.clj index a2f09e927..850b81b39 100644 --- a/modules/interaction/test/blaze/interaction/transaction_test.clj +++ b/modules/interaction/test/blaze/interaction/transaction_test.clj @@ -44,9 +44,7 @@ [reitit.ring] [ring.middleware.params :refer [wrap-params]] [ring.util.response :as ring] - [taoensso.timbre :as log]) - (:import - [java.time Instant])) + [taoensso.timbre :as log])) (set! *warn-on-reflection* true) (st/instrument) @@ -359,7 +357,7 @@ :status := #fhir/string "201" :location := (type/uri (str base-url "/Patient/0/_history/1")) :etag := #fhir/string "W/\"1\"" - :lastModified := Instant/EPOCH))))) + :lastModified := #fhir/instant #system/date-time "1970-01-01T00:00:00Z"))))) (testing "with representation return preference" (with-handler [handler] @@ -386,14 +384,14 @@ :fhir/type := :fhir/Patient :id := "0" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH)) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")) (testing "entry response" (given response :status := #fhir/string "201" :location := (type/uri (str base-url "/Patient/0/_history/1")) :etag := #fhir/string "W/\"1\"" - :lastModified := Instant/EPOCH))))))) + :lastModified := #fhir/instant #system/date-time "1970-01-01T00:00:00Z"))))))) (testing "and updated resource" (let [entries @@ -435,7 +433,7 @@ (given response :status := #fhir/string "200" :etag := #fhir/string "W/\"2\"" - :lastModified := Instant/EPOCH))))) + :lastModified := #fhir/instant #system/date-time "1970-01-01T00:00:00Z"))))) (testing "with representation return preference" (with-handler [handler] @@ -466,13 +464,13 @@ :id := "0" :gender := #fhir/code "male" [:meta :versionId] := #fhir/id "2" - [:meta :lastUpdated] := Instant/EPOCH)) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")) (testing "entry response" (given response :status := #fhir/string "200" :etag := #fhir/string "W/\"2\"" - :lastModified := Instant/EPOCH))))))) + :lastModified := #fhir/instant #system/date-time "1970-01-01T00:00:00Z"))))))) (testing "with identical content" (let [entries @@ -480,7 +478,7 @@ :resource {:fhir/type :fhir/Patient :id "0" :meta (type/meta {:versionId #fhir/id "1" - :lastUpdated Instant/EPOCH}) + :lastUpdated #fhir/instant #system/date-time "1970-01-01T00:00:00Z"}) :gender #fhir/code "female"} :request {:fhir/type :fhir.Bundle.entry/request @@ -516,7 +514,7 @@ (given response :status := #fhir/string "200" :etag := #fhir/string "W/\"1\"" - :lastModified := Instant/EPOCH))))) + :lastModified := #fhir/instant #system/date-time "1970-01-01T00:00:00Z"))))) (testing "with representation return preference" (with-handler [handler] @@ -547,13 +545,13 @@ :id := "0" :gender := #fhir/code "female" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH)) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")) (testing "entry response" (given response :status := #fhir/string "200" :etag := #fhir/string "W/\"1\"" - :lastModified := Instant/EPOCH))))) + :lastModified := #fhir/instant #system/date-time "1970-01-01T00:00:00Z"))))) (testing "and content changing transaction in between" (with-redefs [kv/put! @@ -593,7 +591,7 @@ (given response :status := #fhir/string "200" :etag := #fhir/string "W/\"4\"" - :lastModified := Instant/EPOCH))))))))) + :lastModified := #fhir/instant #system/date-time "1970-01-01T00:00:00Z"))))))))) (testing "and create interaction" (let [entries @@ -632,7 +630,7 @@ :status := #fhir/string "201" :location := (type/uri (str base-url "/Patient/AAAAAAAAAAAAAAAA/_history/1")) :etag := #fhir/string "W/\"1\"" - :lastModified := Instant/EPOCH))))) + :lastModified := #fhir/instant #system/date-time "1970-01-01T00:00:00Z"))))) (testing "with representation return preference" (with-handler [handler] @@ -659,14 +657,14 @@ :fhir/type := :fhir/Patient :id := "AAAAAAAAAAAAAAAA" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH)) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")) (testing "entry response" (given response :status := #fhir/string "201" :location := (type/uri (str base-url "/Patient/AAAAAAAAAAAAAAAA/_history/1")) :etag := #fhir/string "W/\"1\"" - :lastModified := Instant/EPOCH))))))) + :lastModified := #fhir/instant #system/date-time "1970-01-01T00:00:00Z"))))))) (testing "and conditional create interaction" (testing "with empty property" @@ -750,7 +748,7 @@ :status := #fhir/string "201" :location := (type/uri (str base-url "/Patient/AAAAAAAAAAAAAAAA/_history/2")) :etag := #fhir/string "W/\"2\"" - :lastModified := Instant/EPOCH)))))) + :lastModified := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")))))) (testing "with representation return preference" (with-handler [handler] @@ -790,14 +788,14 @@ :fhir/type := :fhir/Patient :id := "AAAAAAAAAAAAAAAA" [:meta :versionId] := #fhir/id "2" - [:meta :lastUpdated] := Instant/EPOCH)) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")) (testing "entry response" (given response :status := #fhir/string "201" :location := (type/uri (str base-url "/Patient/AAAAAAAAAAAAAAAA/_history/2")) :etag := #fhir/string "W/\"2\"" - :lastModified := Instant/EPOCH))))))) + :lastModified := #fhir/instant #system/date-time "1970-01-01T00:00:00Z"))))))) (testing "with matching patient" (testing "without return preference" @@ -840,7 +838,7 @@ :status := #fhir/string "200" :location := nil :etag := #fhir/string "W/\"1\"" - :lastModified := Instant/EPOCH)))))) + :lastModified := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")))))) (testing "with representation return preference" (with-handler [handler] @@ -880,13 +878,13 @@ :fhir/type := :fhir/Patient :id := "0" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH)) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")) (testing "entry response" (given response :status := #fhir/string "200" :etag := #fhir/string "W/\"1\"" - :lastModified := Instant/EPOCH)))))))) + :lastModified := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")))))))) (testing "and delete interaction" (let [entries @@ -924,7 +922,7 @@ (given response :status := #fhir/string "204" :etag := #fhir/string "W/\"2\"" - :lastModified := Instant/EPOCH))))))) + :lastModified := #fhir/instant #system/date-time "1970-01-01T00:00:00Z"))))))) (testing "and read interaction" (testing "returns Not-Found on non-existing resource" @@ -994,13 +992,13 @@ :fhir/type := :fhir/Patient :id := "0" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH)) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")) (testing "entry response" (given response :status := #fhir/string "200" :etag := #fhir/string "W/\"1\"" - :lastModified := Instant/EPOCH)))))))) + :lastModified := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")))))))) (testing "On transaction bundle" (testing "on missing request" @@ -1612,9 +1610,9 @@ (testing "on multiple matching patients" (with-handler [handler] [[[:put {:fhir/type :fhir/Patient :id "0" - :birthDate #fhir/date "2020"}] + :birthDate #fhir/date #system/date "2020"}] [:put {:fhir/type :fhir/Patient :id "1" - :birthDate #fhir/date "2020"}]]] + :birthDate #fhir/date #system/date "2020"}]]] (let [{:keys [status body]} @(handler @@ -1676,7 +1674,7 @@ (given response :status := #fhir/string "201" :etag := #fhir/string "W/\"1\"" - :lastModified := Instant/EPOCH))))) + :lastModified := #fhir/instant #system/date-time "1970-01-01T00:00:00Z"))))) (testing "on existing resource" (with-handler [handler] @@ -1749,7 +1747,7 @@ (given response :status := #fhir/string "204" :etag := #fhir/string "W/\"2\"" - :lastModified := Instant/EPOCH))))))) + :lastModified := #fhir/instant #system/date-time "1970-01-01T00:00:00Z"))))))) (testing "and read interaction after update interaction" (let [entries @@ -1790,13 +1788,13 @@ :fhir/type := :fhir/Patient :id := "111718" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH)) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")) (testing "the first entry has the right response" (given (:response first-entry) :status := #fhir/string "200" :etag := #fhir/string "W/\"1\"" - :lastModified := Instant/EPOCH)) + :lastModified := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")) (testing "the second entry resource is nil" (is (nil? (:resource second-entry)))) @@ -1805,7 +1803,7 @@ (given (:response second-entry) :status := #fhir/string "201" :etag := #fhir/string "W/\"1\"" - :lastModified := Instant/EPOCH)))))))) + :lastModified := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")))))))) (testing "On batch bundle" (testing "on missing request" @@ -2186,12 +2184,12 @@ :status := #fhir/string "201" :location := (type/uri (str base-url "/Patient/0/_history/1")) :etag := #fhir/string "W/\"1\"" - :lastModified := Instant/EPOCH)))) + :lastModified := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")))) (testing "with identical content" (with-handler [handler] [[[:create {:fhir/type :fhir/Patient :id "0" - :birthDate #fhir/date "2020"}]]] + :birthDate #fhir/date #system/date "2020"}]]] (let [{:keys [status] {[{:keys [resource response]}] :entry} :body} @(handler @@ -2202,7 +2200,7 @@ [{:fhir/type :fhir.Bundle/entry :resource {:fhir/type :fhir/Patient :id "0" - :birthDate #fhir/date "2020"} + :birthDate #fhir/date #system/date "2020"} :request {:fhir/type :fhir.Bundle.entry/request :method #fhir/code "PUT" @@ -2218,7 +2216,7 @@ (given response :status := #fhir/string "200" :etag := #fhir/string "W/\"1\"" - :lastModified := Instant/EPOCH)))) + :lastModified := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")))) (testing "and content changing transaction in between" (with-redefs [kv/put! @@ -2227,12 +2225,12 @@ (kv-p/-put store entries))] (with-handler [handler node] [[[:create {:fhir/type :fhir/Patient :id "0" - :birthDate #fhir/date "2020"}]]] + :birthDate #fhir/date #system/date "2020"}]]] ;; don't wait for the transaction to be finished because the handler ;; call should see the first version of the patient @(node/submit-tx node [[:put {:fhir/type :fhir/Patient :id "0" - :birthDate #fhir/date "2021"}]]) + :birthDate #fhir/date #system/date "2021"}]]) (let [{:keys [status] {[{:keys [resource response]}] :entry} :body} @(handler @@ -2243,7 +2241,7 @@ [{:fhir/type :fhir.Bundle/entry :resource {:fhir/type :fhir/Patient :id "0" - :birthDate #fhir/date "2020"} + :birthDate #fhir/date #system/date "2020"} :request {:fhir/type :fhir.Bundle.entry/request :method #fhir/code "PUT" @@ -2259,7 +2257,7 @@ (given response :status := #fhir/string "200" :etag := #fhir/string "W/\"4\"" - :lastModified := Instant/EPOCH))))))) + :lastModified := #fhir/instant #system/date-time "1970-01-01T00:00:00Z"))))))) (testing "leading slash in URL is removed" (with-handler [handler] @@ -2288,7 +2286,7 @@ :status := #fhir/string "201" :location := (type/uri (str base-url "/Patient/0/_history/1")) :etag := #fhir/string "W/\"1\"" - :lastModified := Instant/EPOCH)))))) + :lastModified := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")))))) (testing "with representation return preference" (with-handler [handler] @@ -2315,14 +2313,14 @@ :fhir/type := :fhir/Patient :id := "0" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH)) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")) (testing "entry response" (given response :status := #fhir/string "201" :location := (type/uri (str base-url "/Patient/0/_history/1")) :etag := #fhir/string "W/\"1\"" - :lastModified := Instant/EPOCH)))))) + :lastModified := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")))))) (testing "and create interaction" (testing "on not-found type-level URL" @@ -2423,9 +2421,9 @@ (testing "on multiple matching patients" (with-handler [handler] [[[:put {:fhir/type :fhir/Patient :id "0" - :birthDate #fhir/date "2020"}] + :birthDate #fhir/date #system/date "2020"}] [:put {:fhir/type :fhir/Patient :id "1" - :birthDate #fhir/date "2020"}]]] + :birthDate #fhir/date #system/date "2020"}]]] (let [{:keys [status] {[{:keys [response]}] :entry} :body} @(handler @@ -2493,7 +2491,7 @@ (given response :status := #fhir/string "201" :etag := #fhir/string "W/\"1\"" - :lastModified := Instant/EPOCH))))) + :lastModified := #fhir/instant #system/date-time "1970-01-01T00:00:00Z"))))) (testing "on existing resource" (with-handler [handler] @@ -2593,7 +2591,7 @@ [:entry 0 :resource :fhir/type] := :fhir/Patient [:entry 0 :resource :id] := "0" [:entry 0 :resource :meta :versionId] := #fhir/id "1" - [:entry 0 :resource :meta :lastUpdated] := Instant/EPOCH)) + [:entry 0 :resource :meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")) (testing "entry response" (given response @@ -2634,10 +2632,10 @@ (with-handler [handler] [[[:create {:fhir/type :fhir/Observation :id "0" - :effective #fhir/dateTime "2021-12-08T00:00:00+01:00"}] + :effective #fhir/dateTime #system/date-time "2021-12-08T00:00:00+01:00"}] [:create {:fhir/type :fhir/Observation :id "1" - :effective #fhir/dateTime "2021-12-09T00:00:00+01:00"}]]] + :effective #fhir/dateTime #system/date-time "2021-12-09T00:00:00+01:00"}]]] (let [{:keys [status] {[{:keys [resource response]}] :entry} :body} @(handler diff --git a/modules/interaction/test/blaze/interaction/update_test.clj b/modules/interaction/test/blaze/interaction/update_test.clj index 578198388..c4828247d 100644 --- a/modules/interaction/test/blaze/interaction/update_test.clj +++ b/modules/interaction/test/blaze/interaction/update_test.clj @@ -28,9 +28,7 @@ [integrant.core :as ig] [juxt.iota :refer [given]] [reitit.core :as reitit] - [taoensso.timbre :as log]) - (:import - [java.time Instant])) + [taoensso.timbre :as log])) (set! *warn-on-reflection* true) (st/instrument) @@ -359,7 +357,7 @@ :fhir/type := :fhir/Patient :id := "0" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH))))) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z"))))) (testing "with return=minimal Prefer header" (with-handler [handler] @@ -451,7 +449,7 @@ :fhir/type := :fhir/Patient :id := "0" [:meta :versionId] := #fhir/id "3" - [:meta :lastUpdated] := Instant/EPOCH))))))) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z"))))))) (testing "on successful update of an existing resource" (testing "with no Prefer header" @@ -465,7 +463,7 @@ ::reitit/match patient-match :headers {"if-match" if-match} :body {:fhir/type :fhir/Patient :id "0" - :birthDate #fhir/date "2020"}})] + :birthDate #fhir/date #system/date "2020"}})] (testing "Returns 200" (is (= 200 status))) @@ -480,15 +478,15 @@ (given body :fhir/type := :fhir/Patient :id := "0" - :birthDate := #fhir/date "2020" + :birthDate := #fhir/date #system/date "2020" [:meta :versionId] := #fhir/id "2" - [:meta :lastUpdated] := Instant/EPOCH))))))) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z"))))))) (testing "on update of an existing resource with identical content" (doseq [if-match [nil "W/\"1\"" "W/\"1\",W/\"2\""]] (with-handler [handler] [[[:create {:fhir/type :fhir/Patient :id "0" - :birthDate #fhir/date "2020"}]]] + :birthDate #fhir/date #system/date "2020"}]]] (let [{:keys [status headers body]} @(handler @@ -497,8 +495,8 @@ :headers {"if-match" if-match} :body {:fhir/type :fhir/Patient :id "0" :meta (type/meta {:versionId #fhir/id "1" - :lastUpdated Instant/EPOCH}) - :birthDate #fhir/date "2020"}})] + :lastUpdated #fhir/instant #system/date-time "1970-01-01T00:00:00Z"}) + :birthDate #fhir/date #system/date "2020"}})] (testing "Returns 200" (is (= 200 status))) @@ -513,9 +511,9 @@ (given body :fhir/type := :fhir/Patient :id := "0" - :birthDate := #fhir/date "2020" + :birthDate := #fhir/date #system/date "2020" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH))))) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z"))))) (testing "and content changing transaction in between" (with-redefs [kv/put! @@ -525,12 +523,12 @@ (doseq [if-match [nil "W/\"1\",W/\"2\""]] (with-handler [handler node] [[[:create {:fhir/type :fhir/Patient :id "0" - :birthDate #fhir/date "2020"}]]] + :birthDate #fhir/date #system/date "2020"}]]] ;; don't wait for the transaction to be finished because the handler ;; call should see the first version of the patient @(node/submit-tx node [[:put {:fhir/type :fhir/Patient :id "0" - :birthDate #fhir/date "2021"}]]) + :birthDate #fhir/date #system/date "2021"}]]) (let [{:keys [status headers body]} @(handler @@ -539,8 +537,8 @@ :headers {"if-match" if-match} :body {:fhir/type :fhir/Patient :id "0" :meta (type/meta {:versionId #fhir/id "1" - :lastUpdated Instant/EPOCH}) - :birthDate #fhir/date "2020"}})] + :lastUpdated #fhir/instant #system/date-time "1970-01-01T00:00:00Z"}) + :birthDate #fhir/date #system/date "2020"}})] (testing "Returns 200" (is (= 200 status))) @@ -555,9 +553,9 @@ (given body :fhir/type := :fhir/Patient :id := "0" - :birthDate := #fhir/date "2020" + :birthDate := #fhir/date #system/date "2020" [:meta :versionId] := #fhir/id "4" - [:meta :lastUpdated] := Instant/EPOCH)))))))) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")))))))) (testing "with disabled referential integrity check" (with-handler [handler] @@ -590,7 +588,7 @@ :fhir/type := :fhir/Observation :id := "0" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH))))) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z"))))) (testing "conditional update" (testing "if-none-match" diff --git a/modules/interaction/test/blaze/interaction/vread_test.clj b/modules/interaction/test/blaze/interaction/vread_test.clj index 6195a6ffb..40ecfda23 100644 --- a/modules/interaction/test/blaze/interaction/vread_test.clj +++ b/modules/interaction/test/blaze/interaction/vread_test.clj @@ -17,9 +17,7 @@ [clojure.test :as test :refer [deftest is testing]] [juxt.iota :refer [given]] [reitit.core :as reitit] - [taoensso.timbre :as log]) - (:import - [java.time Instant])) + [taoensso.timbre :as log])) (st/instrument) (log/set-min-level! :trace) @@ -70,7 +68,7 @@ :fhir/type := :fhir/Patient :id := "0" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH))) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z"))) (testing "deleted version" (let [{:keys [status headers body]} diff --git a/modules/jepsen/src/blaze/jepsen/register.clj b/modules/jepsen/src/blaze/jepsen/register.clj index 16ce06f77..fc59073f1 100644 --- a/modules/jepsen/src/blaze/jepsen/register.clj +++ b/modules/jepsen/src/blaze/jepsen/register.clj @@ -27,7 +27,7 @@ @(-> (fhir-client/read base-uri "Patient" id context) (ac/then-apply (fn [resource] - {:type :ok :value (:multipleBirth resource)})) + {:type :ok :value (-> resource :multipleBirth :value)})) (ac/exceptionally (fn [e] {:type (if (ba/not-found? e) :ok :fail) :value nil})))) diff --git a/modules/jepsen/src/blaze/jepsen/resource_history.clj b/modules/jepsen/src/blaze/jepsen/resource_history.clj index 69ccd08dd..de2fc0231 100644 --- a/modules/jepsen/src/blaze/jepsen/resource_history.clj +++ b/modules/jepsen/src/blaze/jepsen/resource_history.clj @@ -50,7 +50,7 @@ @(-> (fhir-client/history-instance base-uri "Patient" id context) (ac/then-apply (fn [versions] - {:type :ok :value (map (comp :value first :identifier) versions)})) + {:type :ok :value (map (comp :value :value first :identifier) versions)})) (ac/exceptionally (fn [e] {:type (if (ba/not-found? e) :ok :fail) :value nil})))) diff --git a/modules/job-async-interaction/src/blaze/job/async_interaction.clj b/modules/job-async-interaction/src/blaze/job/async_interaction.clj index 7b36c21e9..15d279572 100644 --- a/modules/job-async-interaction/src/blaze/job/async_interaction.clj +++ b/modules/job-async-interaction/src/blaze/job/async_interaction.clj @@ -33,17 +33,17 @@ :code #fhir/CodeableConcept {:coding [#fhir/Coding - {:system #fhir/uri "https://samply.github.io/blaze/fhir/CodeSystem/JobType" + {:system #fhir/uri-interned "https://samply.github.io/blaze/fhir/CodeSystem/JobType" :code #fhir/code "async-interaction" - :display #fhir/string "Asynchronous Interaction Request"}]} - :authoredOn authored-on + :display #fhir/string-interned "Asynchronous Interaction Request"}]} + :authoredOn (type/dateTime authored-on) :input [(u/request-bundle-input (str "Bundle/" bundle-id)) {:fhir/type :fhir.Task/input :type (type/codeable-concept {:coding [(type/coding - {:system (type/uri u/parameter-uri) + {:system (type/uri-interned u/parameter-uri) :code #fhir/code "t"})]}) :value (type/unsignedInt t)}]}) @@ -79,12 +79,12 @@ :statusReason job-util/started-status-reason)) (defn t [job] - (type/value (job-util/input-value job u/parameter-uri "t"))) + (:value (job-util/input-value job u/parameter-uri "t"))) (defn response-bundle-ref "Returns the reference to the response bundle of `job` or nil if there is none." [job] - (-> (job-util/output-value job output-uri "bundle") :reference type/value)) + (-> (job-util/output-value job output-uri "bundle") :reference :value)) (defn- response-bundle [context entries] {:fhir/type :fhir/Bundle diff --git a/modules/job-async-interaction/src/blaze/job/async_interaction/util.clj b/modules/job-async-interaction/src/blaze/job/async_interaction/util.clj index deaa65664..fe380ee51 100644 --- a/modules/job-async-interaction/src/blaze/job/async_interaction/util.clj +++ b/modules/job-async-interaction/src/blaze/job/async_interaction/util.clj @@ -17,7 +17,7 @@ :type (type/codeable-concept {:coding [(type/coding - {:system (type/uri parameter-uri) + {:system (type/uri-interned parameter-uri) :code #fhir/code "bundle"})]}) :value (type/reference {:reference (type/string reference)})}) @@ -25,11 +25,11 @@ (type/quantity {:value (type/decimal (BigDecimal/valueOf (- (System/nanoTime) start) 9)) :unit #fhir/string "s" - :system #fhir/uri "http://unitsofmeasure.org" + :system #fhir/uri-interned "http://unitsofmeasure.org" :code #fhir/code "s"})) (defn- request-bundle-ref [job] - (if-let [reference (-> (job-util/input-value job parameter-uri "bundle") :reference type/value)] + (if-let [reference (-> (job-util/input-value job parameter-uri "bundle") :reference :value)] (or (fsr/split-literal-ref reference) (ba/incorrect (format "Invalid request bundle reference `%s`." reference))) (ba/incorrect "Missing request bundle reference."))) diff --git a/modules/job-async-interaction/test/blaze/job/async_interaction/util_test.clj b/modules/job-async-interaction/test/blaze/job/async_interaction/util_test.clj index 72c9b6991..f32005fec 100644 --- a/modules/job-async-interaction/test/blaze/job/async_interaction/util_test.clj +++ b/modules/job-async-interaction/test/blaze/job/async_interaction/util_test.clj @@ -8,7 +8,6 @@ [blaze.db.tx-cache] [blaze.db.tx-log.local] [blaze.fhir.spec.references-spec] - [blaze.fhir.spec.type :as type] [blaze.handler.fhir.util-spec] [blaze.job.async-interaction-spec] [blaze.job.async-interaction.util :as u] @@ -27,9 +26,9 @@ (deftest processing-duration-test (given (u/processing-duration (System/nanoTime)) - type/type := :fhir/Quantity - [:value type/type] := :fhir/decimal - [:value type/value] :? #(and (decimal? %) (pos? %)) + :fhir/type := :fhir/Quantity + [:value :fhir/type] := :fhir/decimal + [:value :value] :? #(and (decimal? %) (pos? %)) :unit := #fhir/string "s" :system := #fhir/uri "http://unitsofmeasure.org" :code := #fhir/code "s")) diff --git a/modules/job-async-interaction/test/blaze/job/async_interaction_spec.clj b/modules/job-async-interaction/test/blaze/job/async_interaction_spec.clj index e00c5ac80..ab62cd7eb 100644 --- a/modules/job-async-interaction/test/blaze/job/async_interaction_spec.clj +++ b/modules/job-async-interaction/test/blaze/job/async_interaction_spec.clj @@ -8,7 +8,7 @@ [clojure.spec.alpha :as s])) (s/fdef job-async/job - :args (s/cat :authored-on :fhir/dateTime + :args (s/cat :authored-on :system/date-time :bundle-id :blaze.resource/id :t :blaze.db/t) :ret :fhir/Task) diff --git a/modules/job-async-interaction/test/blaze/job/async_interaction_test.clj b/modules/job-async-interaction/test/blaze/job/async_interaction_test.clj index ed81d2ca4..ad7021630 100644 --- a/modules/job-async-interaction/test/blaze/job/async_interaction_test.clj +++ b/modules/job-async-interaction/test/blaze/job/async_interaction_test.clj @@ -15,7 +15,6 @@ [blaze.db.tx-log.local] [blaze.fhir.parsing-context] [blaze.fhir.spec.references-spec] - [blaze.fhir.spec.type :as type] [blaze.fhir.test-util :refer [structure-definition-repo]] [blaze.fhir.writing-context] [blaze.handler.fhir.util-spec] @@ -264,13 +263,13 @@ (defn- processing-duration [job] (-> (job-util/output-value job job-async/output-uri "processing-duration") - :value type/value)) + :value :value)) (deftest simple-job-execution-test (testing "success" (with-system [{:blaze/keys [job-scheduler] :as system} config] - @(js/create-job job-scheduler (job-async/job #fhir/dateTime "2024-05-30T10:26:00" "0" 0) + @(js/create-job job-scheduler (job-async/job #system/date-time "2024-05-30T10:26:00" "0" 0) (job-async/request-bundle "0" "GET" "Observation")) (testing "the job is completed" @@ -278,7 +277,7 @@ :fhir/type := :fhir/Task job-util/job-number := "1" jtu/combined-status := :completed - :authoredOn := #fhir/dateTime "2024-05-30T10:26:00" + :authoredOn := #fhir/dateTime #system/date-time "2024-05-30T10:26:00" job-async/response-bundle-ref := "Bundle/AAAAAAAAAAAAAAAA" processing-duration :? decimal?)) @@ -299,7 +298,7 @@ (testing "unknown FHIR type" (with-system [{:blaze/keys [job-scheduler] :as system} config] - @(js/create-job job-scheduler (job-async/job #fhir/dateTime "2024-05-30T10:26:00" "0" 0) + @(js/create-job job-scheduler (job-async/job #system/date-time "2024-05-30T10:26:00" "0" 0) (job-async/request-bundle "0" "GET" "Error")) (testing "the job is completed" @@ -307,7 +306,7 @@ :fhir/type := :fhir/Task job-util/job-number := "1" jtu/combined-status := :completed - :authoredOn := #fhir/dateTime "2024-05-30T10:26:00" + :authoredOn := #fhir/dateTime #system/date-time "2024-05-30T10:26:00" job-async/response-bundle-ref := "Bundle/AAAAAAAAAAAAAAAA" processing-duration :? decimal?)) @@ -350,7 +349,7 @@ (with-system [{:blaze/keys [job-scheduler] :as system} config] @(js/create-job job-scheduler - (-> (job-async/job #fhir/dateTime "2024-05-30T10:26:00" "0" 0) + (-> (job-async/job #system/date-time "2024-05-30T10:26:00" "0" 0) (update :input (partial take 1))) (job-async/request-bundle "0" "GET" "Error")) @@ -367,7 +366,7 @@ (deftest cancellation-test (with-system [{:blaze/keys [job-scheduler] :as system} config] - @(js/create-job job-scheduler (job-async/job #fhir/dateTime "2024-05-30T10:26:00" "0" 0) + @(js/create-job job-scheduler (job-async/job #system/date-time "2024-05-30T10:26:00" "0" 0) (job-async/request-bundle "0" "GET" "Observation")) @(jtu/pull-job system :in-progress/started) @@ -379,7 +378,7 @@ :fhir/type := :fhir/Task job-util/job-number := "1" jtu/combined-status := :cancelled/finished - :authoredOn := #fhir/dateTime "2024-05-30T10:26:00")) + :authoredOn := #fhir/dateTime #system/date-time "2024-05-30T10:26:00")) (testing "job history" (given @(jtu/pull-job-history system) diff --git a/modules/job-compact/src/blaze/job/compact.clj b/modules/job-compact/src/blaze/job/compact.clj index 98d3ea71e..1442c5e0e 100644 --- a/modules/job-compact/src/blaze/job/compact.clj +++ b/modules/job-compact/src/blaze/job/compact.clj @@ -30,7 +30,7 @@ #fhir/Quantity {:value #fhir/decimal 0M :unit #fhir/string "s" - :system #fhir/uri "http://unitsofmeasure.org" + :system #fhir/uri-interned "http://unitsofmeasure.org" :code #fhir/code "s"}) (defn job @@ -44,23 +44,23 @@ #fhir/CodeableConcept {:coding [#fhir/Coding - {:system #fhir/uri "https://samply.github.io/blaze/fhir/CodeSystem/JobType" + {:system #fhir/uri-interned "https://samply.github.io/blaze/fhir/CodeSystem/JobType" :code #fhir/code "compact" - :display #fhir/string "Compact a Database Column Family"}]} - :authoredOn authored-on + :display #fhir/string-interned "Compact a Database Column Family"}]} + :authoredOn (type/dateTime authored-on) :input [{:fhir/type :fhir.Task/input :type (type/codeable-concept {:coding [(type/coding - {:system (type/uri parameter-system) + {:system (type/uri-interned parameter-system) :code #fhir/code "database"})]}) :value (type/code database)} {:fhir/type :fhir.Task/input :type (type/codeable-concept {:coding [(type/coding - {:system (type/uri parameter-system) + {:system (type/uri-interned parameter-system) :code #fhir/code "column-family"})]}) :value (type/code column-family)}]}) @@ -73,7 +73,7 @@ [(task-output "processing-duration" initial-duration)])) (defn- increment-quantity-value [quantity x] - (update quantity :value #(type/decimal (+ (type/value %) x)))) + (update quantity :value #(type/decimal (+ (:value %) x)))) (defn- increment-duration [job duration] (job-util/update-output-value job output-system "processing-duration" @@ -85,7 +85,7 @@ (dissoc :statusReason))) (defn- database* [job] - (or (type/value (job-util/input-value job parameter-system "database")) + (or (:value (job-util/input-value job parameter-system "database")) (ba/incorrect "Missing `database` parameter."))) (defn- database [context job] @@ -94,8 +94,8 @@ (ba/incorrect (format "Unknown database `%s`." database))))) (defn- column-family [job] - (or (-> (job-util/input-value job parameter-system "column-family") - type/value keyword) + (or (some-> (job-util/input-value job parameter-system "column-family") + :value keyword) (ba/incorrect "Missing `column-family` parameter."))) (defn- assoc-duration [start result] diff --git a/modules/job-compact/src/blaze/job/compact_spec.clj b/modules/job-compact/src/blaze/job/compact_spec.clj index 74d567e16..d22d61a32 100644 --- a/modules/job-compact/src/blaze/job/compact_spec.clj +++ b/modules/job-compact/src/blaze/job/compact_spec.clj @@ -7,7 +7,7 @@ [clojure.spec.alpha :as s])) (s/fdef job-compact/job - :args (s/cat :authored-on :fhir/dateTime + :args (s/cat :authored-on :system/date-time :database string? :column-family string?) :ret :fhir/Task) diff --git a/modules/job-compact/test/blaze/job/compact_test.clj b/modules/job-compact/test/blaze/job/compact_test.clj index 6675f4fec..4944a9e04 100644 --- a/modules/job-compact/test/blaze/job/compact_test.clj +++ b/modules/job-compact/test/blaze/job/compact_test.clj @@ -13,7 +13,6 @@ [blaze.db.tx-log :as tx-log] [blaze.db.tx-log.local] [blaze.fhir.parsing-context] - [blaze.fhir.spec.type :as type] [blaze.fhir.test-util :refer [structure-definition-repo]] [blaze.fhir.writing-context] [blaze.job-scheduler :as js] @@ -216,8 +215,8 @@ :fhir/type := :fhir/Task job-util/job-number := "1" jtu/combined-status := :completed - [processing-duration :value type/type] := :fhir/decimal - [processing-duration :value type/value] :? #(and (decimal? %) (pos? %)) + [processing-duration :value :fhir/type] := :fhir/decimal + [processing-duration :value :value] :? #(and (decimal? %) (pos? %)) [processing-duration :unit] := #fhir/string "s" [processing-duration :system] := #fhir/uri "http://unitsofmeasure.org" [processing-duration :code] := #fhir/code "s")) diff --git a/modules/job-re-index/src/blaze/job/re_index.clj b/modules/job-re-index/src/blaze/job/re_index.clj index b2faa5c91..f3ec4175d 100644 --- a/modules/job-re-index/src/blaze/job/re_index.clj +++ b/modules/job-re-index/src/blaze/job/re_index.clj @@ -12,7 +12,9 @@ [clojure.string :as str] [integrant.core :as ig] [java-time.api :as time] - [taoensso.timbre :as log])) + [taoensso.timbre :as log]) + (:import + [java.time ZoneOffset])) (set! *warn-on-reflection* true) @@ -29,7 +31,7 @@ #fhir/Quantity {:value #fhir/decimal 0M :unit #fhir/string "s" - :system #fhir/uri "http://unitsofmeasure.org" + :system #fhir/uri-interned "http://unitsofmeasure.org" :code #fhir/code "s"}) (defn- start-job [job total-resources] @@ -46,14 +48,14 @@ (job-util/add-output job output-system code value)) (defn- increment-unsigned-int [value x] - (type/unsignedInt (+ (type/value value) x))) + (type/unsignedInt (+ (:value value) x))) (defn- increment-resources-processed [job num-resources] (job-util/update-output-value job output-system "resources-processed" increment-unsigned-int num-resources)) (defn- increment-quantity-value [quantity x] - (update quantity :value #(type/decimal (+ (type/value %) x)))) + (update quantity :value #(type/decimal (+ (:value %) x)))) (defn- increment-duration [job duration] (job-util/update-output-value job output-system "processing-duration" @@ -76,13 +78,16 @@ (dissoc :statusReason))) (defn- search-param-url [job] - (-> (job-util/input-value job parameter-system "search-param-url") type/value)) + (-> (job-util/input-value job parameter-system "search-param-url") :value)) (defn- next-resource [job] - (-> (job-util/output-value job output-system "next-resource") type/value)) + (-> (job-util/output-value job output-system "next-resource") :value)) + +(defn- instant [clock] + (.atOffset (time/instant clock) ZoneOffset/UTC)) (defn- elapsed [clock job] - (-> (time/duration (-> job :meta :lastUpdated) (time/instant clock)) + (-> (time/duration (-> job :meta :lastUpdated :value) (instant clock)) (time/as :seconds))) (defn- update-job [{:keys [admin-node clock]} job {:keys [next] :as result}] diff --git a/modules/job-re-index/test/blaze/job/re_index_test.clj b/modules/job-re-index/test/blaze/job/re_index_test.clj index 9e56ad69f..6727178a6 100644 --- a/modules/job-re-index/test/blaze/job/re_index_test.clj +++ b/modules/job-re-index/test/blaze/job/re_index_test.clj @@ -16,7 +16,6 @@ [blaze.db.tx-log :as tx-log] [blaze.db.tx-log.local] [blaze.fhir.parsing-context] - [blaze.fhir.spec.type :as type] [blaze.fhir.test-util :refer [structure-definition-repo]] [blaze.fhir.writing-context] [blaze.job-scheduler :as js] @@ -289,7 +288,7 @@ (output-value job "processing-duration")) (defn- next-resource [job] - (type/value (output-value job "next-resource"))) + (:value (output-value job "next-resource"))) (defn- job-id [{{:keys [clock rng-fn]} :context}] (luid/luid clock (rng-fn))) @@ -318,8 +317,8 @@ jtu/combined-status := :completed total-resources := #fhir/unsignedInt 20001 resources-processed := #fhir/unsignedInt 20001 - [processing-duration :value type/type] := :fhir/decimal - [processing-duration :value type/value] :? #(and (decimal? %) (pos? %)) + [processing-duration :value :fhir/type] := :fhir/decimal + [processing-duration :value :value] :? #(and (decimal? %) (pos? %)) [processing-duration :unit] := #fhir/string "s" [processing-duration :system] := #fhir/uri "http://unitsofmeasure.org" [processing-duration :code] := #fhir/code "s" @@ -366,8 +365,8 @@ jtu/combined-status := :completed total-resources := #fhir/unsignedInt 20001 resources-processed := #fhir/unsignedInt 20001 - [processing-duration :value type/type] := :fhir/decimal - [processing-duration :value type/value] :? #(and (decimal? %) (pos? %)) + [processing-duration :value :fhir/type] := :fhir/decimal + [processing-duration :value :value] :? #(and (decimal? %) (pos? %)) [processing-duration :unit] := #fhir/string "s" [processing-duration :system] := #fhir/uri "http://unitsofmeasure.org" [processing-duration :code] := #fhir/code "s")) @@ -514,8 +513,8 @@ jtu/combined-status := :completed total-resources := #fhir/unsignedInt 60001 resources-processed := #fhir/unsignedInt 60001 - [processing-duration :value type/type] := :fhir/decimal - [processing-duration :value type/value] :? #(and (decimal? %) (pos? %)) + [processing-duration :value :fhir/type] := :fhir/decimal + [processing-duration :value :value] :? #(and (decimal? %) (pos? %)) [processing-duration :unit] := #fhir/string "s" [processing-duration :system] := #fhir/uri "http://unitsofmeasure.org" [processing-duration :code] := #fhir/code "s")) @@ -567,8 +566,8 @@ jtu/combined-status := :completed total-resources := #fhir/unsignedInt 60001 resources-processed := #fhir/unsignedInt 60001 - [processing-duration :value type/type] := :fhir/decimal - [processing-duration :value type/value] :? #(and (decimal? %) (pos? %)) + [processing-duration :value :fhir/type] := :fhir/decimal + [processing-duration :value :value] :? #(and (decimal? %) (pos? %)) [processing-duration :unit] := #fhir/string "s" [processing-duration :system] := #fhir/uri "http://unitsofmeasure.org" [processing-duration :code] := #fhir/code "s")) diff --git a/modules/job-scheduler/src/blaze/job_scheduler.clj b/modules/job-scheduler/src/blaze/job_scheduler.clj index faf5d484c..9e67c0945 100644 --- a/modules/job-scheduler/src/blaze/job_scheduler.clj +++ b/modules/job-scheduler/src/blaze/job_scheduler.clj @@ -52,7 +52,7 @@ (log/debug "The job with id =" id "was unable to update itself. It may have been paused.") (fail-job-on-error node id e)) (log/debug "The execution of the job with id =" id "ended with status =" - (type/value status))))) + (:value status))))) (defn- wrap-error [f context job] (try @@ -101,17 +101,16 @@ (onNext [_ task-handles] (log/trace "Got" (count task-handles) "changed task(s)") (run! - (fn [{:keys [status] :as job}] + (fn [{{status-value :value} :status :as job}] (cond - (= #fhir/code "ready" status) + (= "ready" status-value) (on-start job-scheduler job) - (and (= #fhir/code "in-progress" status) + (and (= "in-progress" status-value) (= "resumed" (job-util/status-reason job))) - (on-resume job-scheduler job) - (and (= #fhir/code "cancelled" status) + (and (= "cancelled" status-value) (= "requested" (job-util/cancelled-sub-status job))) (on-cancel job-scheduler job))) @(d/pull-many node task-handles)) @@ -134,12 +133,12 @@ (ac/then-compose (partial current-job-number-observation context))))) (def ^:private inc-fhir-integer - (comp type/integer inc type/value)) + (comp type/integer inc :value)) (defn- job-number-identifier [job-number] (type/identifier {:use #fhir/code "official" - :system (type/uri job-util/job-number-url) + :system (type/uri-interned job-util/job-number-url) :value (type/string (str job-number))})) (defn- prepare-job [job id job-number] @@ -159,7 +158,7 @@ node (into [[:put (update obs :value inc-fhir-integer) [:if-match (:blaze.db/t (:blaze.db/tx (meta obs)))]] - [:create (prepare-job job id (inc (type/value job-number)))]] + [:create (prepare-job job id (inc (:value job-number)))]] (map (fn [resource] [:create resource])) other-resources)) (ac/then-compose @@ -175,8 +174,7 @@ (dissoc :statusReason))) (defn- cancel-conflict-msg [{:keys [id status]}] - (format "Can't cancel job `%s` because it's status is `%s`." - id (type/value status))) + (format "Can't cancel job `%s` because it's status is `%s`." id (:value status))) (defn cancel-job "Returns a CompletableFuture that will complete with a possibly cancelled job @@ -190,11 +188,11 @@ (log/debug "Try to cancel job with id =" id) (-> (job-util/pull-job node id) (ac/then-compose - (fn [{:keys [status] :as job}] - (if-not (#{#fhir/code "completed" #fhir/code "failed" #fhir/code "cancelled"} status) + (fn [{{status-value :value} :status :as job}] + (if-not (#{"completed" "failed" "cancelled"} status-value) (job-util/update-job node job cancel-job*) (ac/completed-future - (ba/conflict (cancel-conflict-msg job) :job/status (type/value status)))))))) + (ba/conflict (cancel-conflict-msg job) :job/status status-value))))))) (defn- hold-job** [job reason] (assoc @@ -206,16 +204,14 @@ (-> (job-util/pull-job node id) (ac/then-compose (fn [{:keys [status] :as job}] - (condp = status - #fhir/code "in-progress" - (job-util/update-job node job hold-job** reason) - #fhir/code "on-hold" - (ac/completed-future job) + (condp = (:value status) + "in-progress" (job-util/update-job node job hold-job** reason) + "on-hold" (ac/completed-future job) (ac/completed-future (ba/conflict (conflict-msg job)))))))) (defn- pause-conflict-msg [{:keys [id status]}] (format "Can't pause job `%s` because it isn't in-progress. It's status is `%s`." - id (type/value status))) + id (:value status))) (defn pause-job "Returns a CompletableFuture that will complete with a possibly paused job @@ -227,7 +223,7 @@ (defn- resume-conflict-msg [{:keys [id status]}] (format "Can't resume job `%s` because it isn't on-hold. It's status is `%s`." - id (type/value status))) + id (:value status))) (defn- resume-job** [job] (assoc @@ -239,9 +235,8 @@ (-> (job-util/pull-job node id) (ac/then-compose (fn [{:keys [status] :as job}] - (condp = status - #fhir/code "on-hold" - (job-util/update-job node job resume-job**) + (condp = (:value status) + "on-hold" (job-util/update-job node job resume-job**) (ac/completed-future (ba/conflict (resume-conflict-msg job)))))))) (defn resume-job @@ -254,7 +249,7 @@ (defn- shutdown-conflict-msg [{:keys [id status]}] (format "Can't put job `%s` on hold during shutdown because it isn't in-progress. It's status is `%s`." - id (type/value status))) + id (:value status))) (defn- shutdown [{:keys [context running-jobs]}] @(ac/all-of (mapv diff --git a/modules/job-test-util/src/blaze/job/test_util.clj b/modules/job-test-util/src/blaze/job/test_util.clj index b520aaa42..79f1140d3 100644 --- a/modules/job-test-util/src/blaze/job/test_util.clj +++ b/modules/job-test-util/src/blaze/job/test_util.clj @@ -3,21 +3,20 @@ (:require [blaze.async.comp :as ac] [blaze.db.api :as d] - [blaze.fhir.spec.type :as type] [blaze.job.util :as job-util] [blaze.luid :as luid] [blaze.util :refer [str]]) (:import [java.util.concurrent TimeUnit])) -(defn combined-status [{:keys [status] :as job}] +(defn combined-status [{{status :value} :status :as job}] (if-let [status-reason (job-util/status-reason job)] (if-let [cancelled-sub (job-util/cancelled-sub-status job)] - (keyword (str (type/value status) "." cancelled-sub) status-reason) - (keyword (type/value status) status-reason)) + (keyword (str status "." cancelled-sub) status-reason) + (keyword status status-reason)) (if-let [cancelled-sub (job-util/cancelled-sub-status job)] - (keyword (type/value status) cancelled-sub) - (keyword (type/value status))))) + (keyword status cancelled-sub) + (keyword status)))) (defn- job-id [{{:keys [clock rng-fn]} :context}] (luid/luid clock (rng-fn))) diff --git a/modules/job-util/src/blaze/job/util.clj b/modules/job-util/src/blaze/job/util.clj index 8aa052f3a..b2cc0d2cd 100644 --- a/modules/job-util/src/blaze/job/util.clj +++ b/modules/job-util/src/blaze/job/util.clj @@ -25,14 +25,14 @@ (type/codeable-concept {:coding [(type/coding - {:system (type/uri status-reason-url) + {:system (type/uri-interned status-reason-url) :code (type/code reason)})]})) (defn- mk-sub-status [system-url code] (type/codeable-concept {:coding [(type/coding - {:system (type/uri system-url) + {:system (type/uri-interned system-url) :code (type/code code)})]})) (def orderly-shut-down-status-reason (mk-status-reason "orderly-shutdown")) @@ -46,16 +46,14 @@ (defn job-number {:arglists '([job])} [{:keys [identifier]}] - (some - #(when (= job-number-url (type/value (:system %))) (type/value (:value %))) - identifier)) + (some #(when (= job-number-url (-> % :system :value)) (-> % :value :value)) identifier)) (defn code-value "Returns the value of the code of the coding with `system` or nil if not found." {:arglists '([system codeable-concept])} [system {:keys [coding]}] - (some #(when (= system (type/value (:system %))) (type/value (:code %))) coding)) + (some #(when (= system (-> % :system :value)) (-> % :code :value)) coding)) (defn job-type "Returns the type of `job` as keyword." @@ -99,13 +97,13 @@ "Returns the error category of `job` in case it failed and an error category is available." [job] - (some->> (type/value (output-value job "error-category")) + (some->> (:value (output-value job "error-category")) (keyword "cognitect.anomalies"))) (defn error-msg "Returns the error message of `job` in case it failed." [job] - (type/value (output-value job "error"))) + (:value (output-value job "error"))) (defn error "Returns the error as anomaly of `job` in case it failed." @@ -127,7 +125,7 @@ :type (type/codeable-concept {:coding [(type/coding - {:system (type/uri system) + {:system (type/uri-interned system) :code (type/code code)})]}) :value value}) @@ -148,7 +146,7 @@ (update job :output conj-output-value* system code value))) (defn- update-tx-op [{{version-id :versionId} :meta :as job}] - [:put job [:if-match (parse-long (type/value version-id))]]) + [:put job [:if-match (parse-long (:value version-id))]]) (defn- tx-ops [job other-resources] (into [(update-tx-op job)] (map (partial vector :create)) other-resources)) diff --git a/modules/job-util/test/blaze/job/util_test.clj b/modules/job-util/test/blaze/job/util_test.clj index 7da8b033d..ecc7d8f72 100644 --- a/modules/job-util/test/blaze/job/util_test.clj +++ b/modules/job-util/test/blaze/job/util_test.clj @@ -112,7 +112,7 @@ (job-util/task-output "foo" "bar" #fhir/integer 1)]} "foo" "bar" (fn [value x] - (type/integer (+ (type/value value) x))) + (type/integer (+ (:value value) x))) 1) {:fhir/type :fhir/Task :output diff --git a/modules/kv/build.clj b/modules/kv/build.clj index d1465ac5a..796754344 100644 --- a/modules/kv/build.clj +++ b/modules/kv/build.clj @@ -7,4 +7,4 @@ {:basis (b/create-basis {:project "deps.edn"}) :src-dirs ["java"] :class-dir "target/classes" - :javac-opts ["-Xlint:all" "-proc:none" "--release" "17"]})) + :javac-opts ["-Xlint:all" "-proc:none" "--release" "21"]})) diff --git a/modules/operation-code-system-validate-code/test/blaze/fhir/operation/code_system/validate_code_test.clj b/modules/operation-code-system-validate-code/test/blaze/fhir/operation/code_system/validate_code_test.clj index 4ad44029e..f42fa9d0d 100644 --- a/modules/operation-code-system-validate-code/test/blaze/fhir/operation/code_system/validate_code_test.clj +++ b/modules/operation-code-system-validate-code/test/blaze/fhir/operation/code_system/validate_code_test.clj @@ -161,7 +161,7 @@ (given body :fhir/type := :fhir/Parameters [:parameter 0 :name] := #fhir/string "result" - [:parameter 0 :value type/value] := result))) + [:parameter 0 :value :value] := result))) (testing "ignores unknown parameter" (let [{:keys [status body]} @@ -237,7 +237,7 @@ (given body :fhir/type := :fhir/Parameters [:parameter 0 :name] := #fhir/string "result" - [:parameter 0 :value type/value] := result)))) + [:parameter 0 :value :value] := result)))) (testing "and coding" (let [{:keys [status body]} diff --git a/modules/operation-compact/src/blaze/fhir/operation/compact.clj b/modules/operation-compact/src/blaze/fhir/operation/compact.clj index 7791c873c..dfc8710cb 100644 --- a/modules/operation-compact/src/blaze/fhir/operation/compact.clj +++ b/modules/operation-compact/src/blaze/fhir/operation/compact.clj @@ -4,7 +4,6 @@ (:require [blaze.anomaly :as ba :refer [if-ok]] [blaze.async.comp :as ac :refer [do-sync]] - [blaze.fhir.spec.type :as type] [blaze.job-scheduler :as js] [blaze.job.compact :as job-compact] [blaze.module :as m] @@ -22,7 +21,7 @@ (ba/incorrect (format "Expected Parameters resource but was `%s` resource." (name type))))) (defn- get-param [{:keys [parameter]} name] - (or (some #(when (= name (-> % :name type/value)) (-> % :value type/value)) parameter) + (or (some #(when (= name (-> % :name :value)) (-> % :value :value)) parameter) (ba/incorrect (format "Missing `%s` parameter." name)))) (defn- async-status-url diff --git a/modules/operation-graph/src/blaze/operation/graph.clj b/modules/operation-graph/src/blaze/operation/graph.clj index 72e96d37c..c66408960 100644 --- a/modules/operation-graph/src/blaze/operation/graph.clj +++ b/modules/operation-graph/src/blaze/operation/graph.clj @@ -37,7 +37,7 @@ (defn- compile [compiled-graph-cache {:keys [id] {version :versionId} :meta :as graph-def}] - (let [key [id (type/value version)]] + (let [key [id (:value version)]] (.get ^Cache compiled-graph-cache key (fn [_] (c/compile graph-def))))) (defn- find-node [{:keys [nodes]} id] diff --git a/modules/operation-graph/src/blaze/operation/graph/compiler.clj b/modules/operation-graph/src/blaze/operation/graph/compiler.clj index 4ff0cd27a..b06f66410 100644 --- a/modules/operation-graph/src/blaze/operation/graph/compiler.clj +++ b/modules/operation-graph/src/blaze/operation/graph/compiler.clj @@ -5,7 +5,6 @@ [blaze.db.api :as d] [blaze.fhir-path :as fhir-path] [blaze.fhir.spec.references :as fsr] - [blaze.fhir.spec.type :as type] [blaze.handler.fhir.util :as fhir-util] [blaze.operation.graph.spec] [blaze.util :refer [str]] @@ -21,7 +20,7 @@ "http://hl7.org/fhir/5.0/StructureDefinition/extension-GraphDefinition") (defn- extension-value [url] - #(when (= url (:url %)) (type/value (:value %)))) + #(when (= url (:url %)) (-> % :value :value))) (defn- camel->kebab [s] (.to CaseFormat/LOWER_CAMEL CaseFormat/LOWER_HYPHEN s)) @@ -64,8 +63,8 @@ (defn- link-path-resource-handles [path] (fn [db source-resource _target-node] (let [[res & more] (fhir-path/eval noop-resolver path source-resource)] - (when (and (nil? more) (= :fhir/Reference (type/type res))) - (when-let [ref (-> res :reference type/value)] + (when (and (nil? more) (= :fhir/Reference (:fhir/type res))) + (when-let [ref (-> res :reference :value)] (when-let [[type id] (fsr/split-literal-ref ref)] (ba/map (fhir-util/resource-handle db type id) vector))))))) @@ -85,7 +84,7 @@ (let [ref (str (name type) "/" id)] (d/type-query db (:type target-node) (mapv #(% ref) clauses))))) -(defn- link [{:keys [path] extensions :extension}] +(defn- link [{{path :value} :path extensions :extension}] (let [link (base-link extensions) params (some (extension-value (str extension-base ".link.params")) extensions)] (cond diff --git a/modules/operation-graph/test/blaze/operation/graph_test.clj b/modules/operation-graph/test/blaze/operation/graph_test.clj index 10faf48c2..8318356d2 100644 --- a/modules/operation-graph/test/blaze/operation/graph_test.clj +++ b/modules/operation-graph/test/blaze/operation/graph_test.clj @@ -2,7 +2,6 @@ (:require [blaze.async.comp :as ac] [blaze.db.api-stub :as api-stub :refer [with-system-data]] - [blaze.fhir.spec :as fhir-spec] [blaze.fhir.spec.type :as type] [blaze.fhir.util-spec] [blaze.handler.fhir.util-spec] @@ -239,7 +238,7 @@ (testing "the entry has the right search mode" (given (:search first-entry) - fhir-spec/fhir-type := :fhir.Bundle.entry/search + :fhir/type := :fhir.Bundle.entry/search :mode := #fhir/code "match"))))) (testing "returning the patient and one observation" @@ -310,12 +309,12 @@ (testing "the first entry has the right search mode" (given (:search first-entry) - fhir-spec/fhir-type := :fhir.Bundle.entry/search + :fhir/type := :fhir.Bundle.entry/search :mode := #fhir/code "match")) (testing "the second entry has the right search mode" (given (:search second-entry) - fhir-spec/fhir-type := :fhir.Bundle.entry/search + :fhir/type := :fhir.Bundle.entry/search :mode := #fhir/code "match"))))) (testing "returning the patient with one observation and one encounter" @@ -410,17 +409,17 @@ (testing "the first entry has the right search mode" (given (:search first-entry) - fhir-spec/fhir-type := :fhir.Bundle.entry/search + :fhir/type := :fhir.Bundle.entry/search :mode := #fhir/code "match")) (testing "the second entry has the right search mode" (given (:search second-entry) - fhir-spec/fhir-type := :fhir.Bundle.entry/search + :fhir/type := :fhir.Bundle.entry/search :mode := #fhir/code "match")) (testing "the third entry has the right search mode" (given (:search third-entry) - fhir-spec/fhir-type := :fhir.Bundle.entry/search + :fhir/type := :fhir.Bundle.entry/search :mode := #fhir/code "match"))))) (testing "returning the patient with two observations and only one encounter" @@ -528,22 +527,22 @@ (testing "the first entry has the right search mode" (given (:search first-entry) - fhir-spec/fhir-type := :fhir.Bundle.entry/search + :fhir/type := :fhir.Bundle.entry/search :mode := #fhir/code "match")) (testing "the second entry has the right search mode" (given (:search second-entry) - fhir-spec/fhir-type := :fhir.Bundle.entry/search + :fhir/type := :fhir.Bundle.entry/search :mode := #fhir/code "match")) (testing "the third entry has the right search mode" (given (:search third-entry) - fhir-spec/fhir-type := :fhir.Bundle.entry/search + :fhir/type := :fhir.Bundle.entry/search :mode := #fhir/code "match")) (testing "the fourth entry has the right search mode" (given (:search fourth-entry) - fhir-spec/fhir-type := :fhir.Bundle.entry/search + :fhir/type := :fhir.Bundle.entry/search :mode := #fhir/code "match"))))) (testing "circle between condition and encounter is not a problem" @@ -644,15 +643,15 @@ (testing "the first entry has the right search mode" (given (:search first-entry) - fhir-spec/fhir-type := :fhir.Bundle.entry/search + :fhir/type := :fhir.Bundle.entry/search :mode := #fhir/code "match")) (testing "the second entry has the right search mode" (given (:search second-entry) - fhir-spec/fhir-type := :fhir.Bundle.entry/search + :fhir/type := :fhir.Bundle.entry/search :mode := #fhir/code "match")) (testing "the third entry has the right search mode" (given (:search third-entry) - fhir-spec/fhir-type := :fhir.Bundle.entry/search + :fhir/type := :fhir.Bundle.entry/search :mode := #fhir/code "match")))))) diff --git a/modules/operation-graphql/test/blaze/fhir/operation/graphql_test.clj b/modules/operation-graphql/test/blaze/fhir/operation/graphql_test.clj index a0e69b428..ff710fa10 100644 --- a/modules/operation-graphql/test/blaze/fhir/operation/graphql_test.clj +++ b/modules/operation-graphql/test/blaze/fhir/operation/graphql_test.clj @@ -2,7 +2,7 @@ (:require [blaze.async.comp :as ac] [blaze.db.api-stub :as api-stub :refer [with-system-data]] - [blaze.db.resource-store :as rs] + [blaze.db.resource-cache :as rc] [blaze.db.spec] [blaze.executors :as ex] [blaze.fhir.operation.graphql :as graphql] @@ -235,7 +235,7 @@ [:errors] :? empty?))))))) (testing "missing resource contents" - (with-redefs [rs/multi-get (fn [_ _] (ac/completed-future {}))] + (with-redefs [rc/multi-get (fn [_ _] (ac/completed-future {}))] (with-handler [handler] [[[:put {:fhir/type :fhir/Patient :id "0"}]]] diff --git a/modules/operation-measure-evaluate-measure/src/blaze/fhir/operation/evaluate_measure/measure.clj b/modules/operation-measure-evaluate-measure/src/blaze/fhir/operation/evaluate_measure/measure.clj index 01997c34a..ef0b76db8 100644 --- a/modules/operation-measure-evaluate-measure/src/blaze/fhir/operation/evaluate_measure/measure.clj +++ b/modules/operation-measure-evaluate-measure/src/blaze/fhir/operation/evaluate_measure/measure.clj @@ -15,6 +15,7 @@ [blaze.fhir.operation.evaluate-measure.measure.stratifier :as strat] [blaze.fhir.operation.evaluate-measure.measure.util :as u] [blaze.fhir.spec.type :as type] + [blaze.fhir.spec.type.system :as system] [blaze.handler.fhir.util :as fhir-util] [blaze.luid :as luid] [blaze.module :as m] @@ -46,7 +47,7 @@ ;; ---- Compilation ----------------------------------------------------------- (def ^:private text-cql-content - #(when (-> % :contentType type/value #{"text/cql"}) %)) + #(when (-> % :contentType :value #{"text/cql"}) %)) (defn- extract-cql-code "Extracts the CQL code from the first attachment of `library`. @@ -54,15 +55,14 @@ Returns an anomaly on errors." {:arglists '([library])} [{:keys [id content]}] - (if-let [{:keys [data]} (some text-cql-content content)] - (let [data (type/value data)] - (if data - (String. ^bytes (.decode (Base64/getDecoder) ^String data) - StandardCharsets/UTF_8) - (ba/incorrect - (format "Missing embedded data of first attachment in library with id `%s`." id) - :fhir/issue "value" - :fhir.issue/expression "Library.content[0].data"))) + (if-let [{{data :value} :data} (some text-cql-content content)] + (if data + (String. ^bytes (.decode (Base64/getDecoder) ^String data) + StandardCharsets/UTF_8) + (ba/incorrect + (format "Missing embedded data of first attachment in library with id `%s`." id) + :fhir/issue "value" + :fhir.issue/expression "Library.content[0].data")) (ba/incorrect (format "No attachment with `text/cql` content type found in library with id `%s`." id) :fhir/issue "value" @@ -146,7 +146,7 @@ Returns an anomaly on errors." [db terminology-service measure opts] - (if-let [library-ref (-> measure :library first type/value)] + (if-let [library-ref (-> measure :library first :value)] (do-sync [library (find-library db library-ref)] (let [context (cond-> {:node (d/node db)} terminology-service @@ -200,7 +200,7 @@ (some (fn [{:keys [url value]}] (when (= "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-populationBasis" url) - (let [basis (type/value value)] + (let [basis (:value value)] (when-not (= "boolean" basis) basis)))) extension)) @@ -315,16 +315,13 @@ (do (log/debug (evaluate-groups-msg id subject-type duration)) [groups duration])))))))) -(defn- canonical [context {:keys [id url version]}] - (if-let [url (type/value url)] +(defn- canonical [context {:keys [id] {url :value} :url {version :value} :version}] + (if url (cond-> url version (str "|" version)) (fhir-util/instance-url context "Measure" id))) (defn- get-first-code [codings system] - (some - #(when (= system (-> % :system type/value)) - (-> % :code type/value)) - codings)) + (some #(when (= system (-> % :system :value)) (-> % :code :value)) codings)) (defn- subject-type [{{codings :coding} :subject}] (or (get-first-code codings "http://hl7.org/fhir/resource-types") "Patient")) @@ -335,7 +332,7 @@ :value (type/quantity {:code #fhir/code "s" - :system #fhir/uri "http://unitsofmeasure.org" + :system #fhir/uri-interned "http://unitsofmeasure.org" :unit #fhir/string "s" :value (type/decimal (bigdec duration))})})) @@ -370,11 +367,11 @@ "subject-list" #fhir/code "subject-list" "subject" #fhir/code "individual") :measure (type/canonical (canonical context measure)) - :date now + :date (type/dateTime now) :period (type/period - {:start (type/dateTime (str start)) - :end (type/dateTime (str end))})} + {:start (type/dateTime (system/parse-date-time (str start))) + :end (type/dateTime (system/parse-date-time (str end)))})} subject-handle (assoc :subject (type/reference {:reference (type/string (local-ref subject-handle))})) diff --git a/modules/operation-measure-evaluate-measure/src/blaze/fhir/operation/evaluate_measure/measure/stratifier.clj b/modules/operation-measure-evaluate-measure/src/blaze/fhir/operation/evaluate_measure/measure/stratifier.clj index a04b8349e..df6cad74d 100644 --- a/modules/operation-measure-evaluate-measure/src/blaze/fhir/operation/evaluate_measure/measure/stratifier.clj +++ b/modules/operation-measure-evaluate-measure/src/blaze/fhir/operation/evaluate_measure/measure/stratifier.clj @@ -10,30 +10,29 @@ [blaze.util :refer [conj-vec str]])) (defn- quantity-value [value] - (let [code (-> value :code type/value)] - (cond-> (str (-> value :value type/value)) code (str " " code)))) + (let [code (-> value :code :value)] + (cond-> (str (-> value :value :value)) code (str " " code)))) (defn- value-concept "Converts `value` into a CodeableConcept so that it can be used in a Stratifier." - [value] - (let [type (type/type value)] - (cond - (identical? :fhir/CodeableConcept type) - value + [{:fhir/keys [type] :as value}] + (cond + (identical? :fhir/CodeableConcept type) + value - (identical? :fhir/Quantity type) - (type/codeable-concept - {:text (type/string (quantity-value value))}) + (identical? :fhir/Quantity type) + (type/codeable-concept + {:text (type/string (quantity-value value))}) - (fhir-spec/primitive-val? value) - (type/codeable-concept {:text (type/string (str (type/value value)))}) + (fhir-spec/primitive-val? value) + (type/codeable-concept {:text (type/string (str (:value value)))}) - (nil? value) - (type/codeable-concept {:text #fhir/string "null"}) + (nil? value) + (type/codeable-concept {:text #fhir/string "null"}) - :else - (type/codeable-concept {:text (type/string (str value))})))) + :else + (type/codeable-concept {:text (type/string (str value))}))) (defn- stratum-value-extension [value] (type/extension @@ -51,7 +50,7 @@ :count (type/integer count)}) populations)} - (identical? :fhir/Quantity (type/type value)) + (identical? :fhir/Quantity (:fhir/type value)) (assoc :extension [(stratum-value-extension value)]))) (defn- stratum-subject-list-populations [context populations] @@ -90,7 +89,7 @@ :value (value-concept value) :population %} - (identical? :fhir/Quantity (type/type value)) + (identical? :fhir/Quantity (:fhir/type value)) (assoc :extension [(stratum-value-extension value)])))) (defn- stratifier-path [group-idx stratifier-idx] @@ -165,7 +164,7 @@ :code code :value (value-concept value)} - (identical? :fhir/Quantity (type/type value)) + (identical? :fhir/Quantity (:fhir/type value)) (assoc :extension [(stratum-component-value-extension value)]))) codes values)) @@ -199,7 +198,7 @@ :component (components codes values) :population %} - (identical? :fhir/Quantity (type/type values)) + (identical? :fhir/Quantity (:fhir/type values)) (assoc :extension [(stratum-value-extension values)])))) (defn- multi-component-reduce-op* [{:keys [report-type] :as context} expression-names] diff --git a/modules/operation-measure-evaluate-measure/src/blaze/fhir/operation/evaluate_measure/measure/util.clj b/modules/operation-measure-evaluate-measure/src/blaze/fhir/operation/evaluate_measure/measure/util.clj index c66658cc0..17d94fdbf 100644 --- a/modules/operation-measure-evaluate-measure/src/blaze/fhir/operation/evaluate_measure/measure/util.clj +++ b/modules/operation-measure-evaluate-measure/src/blaze/fhir/operation/evaluate_measure/measure/util.clj @@ -8,8 +8,8 @@ [blaze.util :refer [str]])) (defn expression-name [population-path-fn criteria] - (let [language (-> criteria :language type/value) - expression (-> criteria :expression type/value)] + (let [language (-> criteria :language :value) + expression (-> criteria :expression :value)] (cond (nil? criteria) (ba/incorrect @@ -71,8 +71,8 @@ (ba/exceptionally reduced))) (defn- expression-name-of-expression [{:keys [language expression]}] - (when (#{"text/cql" "text/cql-identifier"} (type/value language)) - (type/value expression))) + (when (#{"text/cql" "text/cql-identifier"} (:value language)) + (:value expression))) (defn- expression-name-of-population [{:keys [criteria]}] (expression-name-of-expression criteria)) diff --git a/modules/operation-measure-evaluate-measure/src/blaze/fhir/operation/evaluate_measure/middleware/params.clj b/modules/operation-measure-evaluate-measure/src/blaze/fhir/operation/evaluate_measure/middleware/params.clj index b0fb4e51c..75d69da26 100644 --- a/modules/operation-measure-evaluate-measure/src/blaze/fhir/operation/evaluate_measure/middleware/params.clj +++ b/modules/operation-measure-evaluate-measure/src/blaze/fhir/operation/evaluate_measure/middleware/params.clj @@ -4,7 +4,6 @@ [blaze.async.comp :as ac] [blaze.fhir.operation.evaluate-measure.measure :as-alias measure] [blaze.fhir.operation.evaluate-measure.measure.spec] - [blaze.fhir.spec.type :as type] [blaze.fhir.spec.type.system :as system] [clojure.spec.alpha :as s])) @@ -43,7 +42,7 @@ (defn- get-param-value-from-resource [body name] (when (identical? :fhir/Parameters (:fhir/type body)) - (some #(when (= name (-> % :name type/value)) (-> % :value type/value)) + (some #(when (= name (-> % :name :value)) (-> % :value :value)) (:parameter body)))) (defn- get-param-value* [{:keys [params body]} name coercer] @@ -132,18 +131,17 @@ report-type (get-param-value request "reportType" coerce-and-validate-report-type) subject-ref (coerce-subject-ref-param request)] - (let [report-type (some-> report-type type/value)] - (if (and (= :get request-method) (= "subject-list" report-type)) - (ba/unsupported no-subject-list-on-get-msg) - (assoc request - :blaze.fhir.operation.evaluate-measure/params - (cond-> {:period [period-start period-end] - :report-type (or report-type - (if subject-ref "subject" "population"))} - measure - (assoc :measure measure) - subject-ref - (assoc :subject-ref subject-ref))))))) + (if (and (= :get request-method) (= "subject-list" report-type)) + (ba/unsupported no-subject-list-on-get-msg) + (assoc request + :blaze.fhir.operation.evaluate-measure/params + (cond-> {:period [period-start period-end] + :report-type (or report-type + (if subject-ref "subject" "population"))} + measure + (assoc :measure measure) + subject-ref + (assoc :subject-ref subject-ref)))))) (defn wrap-coerce-params [handler] (fn [request] diff --git a/modules/operation-measure-evaluate-measure/test/blaze/fhir/operation/evaluate_measure/cql_test.clj b/modules/operation-measure-evaluate-measure/test/blaze/fhir/operation/evaluate_measure/cql_test.clj index 56579f7de..4ddba2c75 100644 --- a/modules/operation-measure-evaluate-measure/test/blaze/fhir/operation/evaluate_measure/cql_test.clj +++ b/modules/operation-measure-evaluate-measure/test/blaze/fhir/operation/evaluate_measure/cql_test.clj @@ -11,7 +11,6 @@ [blaze.fhir.operation.evaluate-measure.cql :as cql] [blaze.fhir.operation.evaluate-measure.cql-spec] [blaze.fhir.operation.evaluate-measure.test-util :as em-tu] - [blaze.fhir.spec.type] [blaze.module.test-util :refer [given-failed-future with-system]] [blaze.terminology-service :as-alias ts] [blaze.terminology-service-spec] diff --git a/modules/operation-measure-evaluate-measure/test/blaze/fhir/operation/evaluate_measure/measure/stratifier_test.clj b/modules/operation-measure-evaluate-measure/test/blaze/fhir/operation/evaluate_measure/measure/stratifier_test.clj index f94c859b3..e23284553 100644 --- a/modules/operation-measure-evaluate-measure/test/blaze/fhir/operation/evaluate_measure/measure/stratifier_test.clj +++ b/modules/operation-measure-evaluate-measure/test/blaze/fhir/operation/evaluate_measure/measure/stratifier_test.clj @@ -89,16 +89,13 @@ AgeInYearsAt(encounter.period.start)") (defn- cql-expression [expr] - {:fhir/type :fhir/Expression - :language #fhir/code "text/cql-identifier" - :expression (type/string expr)}) + (type/expression {:language #fhir/code "text/cql-identifier" + :expression (type/string expr)})) (def stratifier-with-missing-expression {:fhir/type :fhir.Measure.group/stratifier :code #fhir/CodeableConcept{:text #fhir/string "gender"} - :criteria - {:fhir/type :fhir/Expression - :language #fhir/code "text/cql-identifier"}}) + :criteria #fhir/Expression{:language #fhir/code "text/cql-identifier"}}) (def gender-stratifier {:fhir/type :fhir.Measure.group/stratifier @@ -120,9 +117,7 @@ :component [{:fhir/type :fhir.Measure.group.stratifier/component :code #fhir/CodeableConcept{:text #fhir/string "age"} - :criteria - {:fhir/type :fhir/Expression - :language #fhir/code "text/cql-identifier"}} + :criteria #fhir/Expression{:language #fhir/code "text/cql-identifier"}} {:fhir/type :fhir.Measure.group.stratifier/component :code #fhir/CodeableConcept{:text #fhir/string "gender"} :criteria (cql-expression "Gender")}]}) @@ -304,13 +299,13 @@ [[[:put {:fhir/type :fhir/Patient :id "0"}] [:put {:fhir/type :fhir/Patient :id "1" :gender #fhir/code "male" - :birthDate #fhir/date "1960"}] + :birthDate #fhir/date #system/date "1960"}] [:put {:fhir/type :fhir/Patient :id "2" :gender #fhir/code "female" - :birthDate #fhir/date "1960"}] + :birthDate #fhir/date #system/date "1960"}] [:put {:fhir/type :fhir/Patient :id "3" :gender #fhir/code "male" - :birthDate #fhir/date "1950"}]]] + :birthDate #fhir/date #system/date "1950"}]]] (let [{:keys [db] :as context} (context system library-age-gender) handles (into [] (em-tu/handle-mapper db) (d/type-list db "Patient"))] @@ -326,27 +321,27 @@ (testing "Encounter measure" (with-system-data [system config] - [[[:put {:fhir/type :fhir/Patient :id "0" :birthDate #fhir/date "2000"}] - [:put {:fhir/type :fhir/Patient :id "1" :birthDate #fhir/date "2001"}] - [:put {:fhir/type :fhir/Patient :id "2" :birthDate #fhir/date "2003"}] + [[[:put {:fhir/type :fhir/Patient :id "0" :birthDate #fhir/date #system/date "2000"}] + [:put {:fhir/type :fhir/Patient :id "1" :birthDate #fhir/date #system/date "2001"}] + [:put {:fhir/type :fhir/Patient :id "2" :birthDate #fhir/date #system/date "2003"}] [:put {:fhir/type :fhir/Encounter :id "0" :subject #fhir/Reference{:reference #fhir/string "Patient/0"}}] [:put {:fhir/type :fhir/Encounter :id "1" :status #fhir/code "finished" :subject #fhir/Reference{:reference #fhir/string "Patient/0"} - :period #fhir/Period{:start #fhir/dateTime "2020"}}] + :period #fhir/Period{:start #fhir/dateTime #system/date-time "2020"}}] [:put {:fhir/type :fhir/Encounter :id "2" :status #fhir/code "planned" :subject #fhir/Reference{:reference #fhir/string "Patient/1"} - :period #fhir/Period{:start #fhir/dateTime "2021"}}] + :period #fhir/Period{:start #fhir/dateTime #system/date-time "2021"}}] [:put {:fhir/type :fhir/Encounter :id "3" :status #fhir/code "finished" :subject #fhir/Reference{:reference #fhir/string "Patient/2"} - :period #fhir/Period{:start #fhir/dateTime "2022"}}] + :period #fhir/Period{:start #fhir/dateTime #system/date-time "2022"}}] [:put {:fhir/type :fhir/Encounter :id "4" :status #fhir/code "finished" :subject #fhir/Reference{:reference #fhir/string "Patient/2"} - :period #fhir/Period{:start #fhir/dateTime "2022"}}]]] + :period #fhir/Period{:start #fhir/dateTime #system/date-time "2022"}}]]] (let [{:keys [db] :as context} (context system library-encounter-status-age) handles [{:population-handle (em-tu/resource db "Encounter" "0") @@ -374,16 +369,16 @@ (testing "Quantity" (with-system-data [system config] - [[[:put {:fhir/type :fhir/Patient :id "0" :birthDate #fhir/date "2000"}] + [[[:put {:fhir/type :fhir/Patient :id "0" :birthDate #fhir/date #system/date "2000"}] [:put {:fhir/type :fhir/Observation :id "0" :subject #fhir/Reference{:reference #fhir/string "Patient/0"} - :effective #fhir/dateTime "2020" + :effective #fhir/dateTime #system/date-time "2020" :value #fhir/Quantity {:value #fhir/decimal 1M :code #fhir/code "kg"}}] [:put {:fhir/type :fhir/Observation :id "1" :subject #fhir/Reference{:reference #fhir/string "Patient/0"} - :effective #fhir/dateTime "2021" + :effective #fhir/dateTime #system/date-time "2021" :value #fhir/Quantity {:value #fhir/decimal 2M}}]]] diff --git a/modules/operation-measure-evaluate-measure/test/blaze/fhir/operation/evaluate_measure/measure/util_test.clj b/modules/operation-measure-evaluate-measure/test/blaze/fhir/operation/evaluate_measure/measure/util_test.clj index 2c8c628a3..5de48ebf5 100644 --- a/modules/operation-measure-evaluate-measure/test/blaze/fhir/operation/evaluate_measure/measure/util_test.clj +++ b/modules/operation-measure-evaluate-measure/test/blaze/fhir/operation/evaluate_measure/measure/util_test.clj @@ -26,8 +26,7 @@ (testing "unsupported language" (given (u/expression-name (constantly "path-184706") - {:fhir/type :fhir/Expression - :language #fhir/code "lang-184851"}) + #fhir/Expression{:language #fhir/code "lang-184851"}) ::anom/category := ::anom/unsupported ::anom/message := "Unsupported language `lang-184851`." :fhir/issue := "not-supported" @@ -35,8 +34,7 @@ (testing "missing expression" (given (u/expression-name (constantly "path-184642") - {:fhir/type :fhir/Expression - :language #fhir/code "text/cql-identifier"}) + #fhir/Expression{:language #fhir/code "text/cql-identifier"}) ::anom/category := ::anom/incorrect ::anom/message := "Missing expression." :fhir/issue := "required" @@ -46,22 +44,21 @@ (satisfies-prop 10 (prop/for-all [expression gen/string] (= expression (u/expression-name (constantly "foo") - {:fhir/type :fhir/Expression - :language #fhir/code "text/cql-identifier" - :expression (type/string expression)}))))) + (type/expression + {:language #fhir/code "text/cql-identifier" + :expression (type/string expression)})))))) (testing "works with `text/cql`" (satisfies-prop 10 (prop/for-all [expression gen/string] (= expression (u/expression-name (constantly "foo") - {:fhir/type :fhir/Expression - :language #fhir/code "text/cql" - :expression (type/string expression)})))))) + (type/expression + {:language #fhir/code "text/cql" + :expression (type/string expression)}))))))) (defn- cql-expression [expr] - {:fhir/type :fhir/Expression - :language #fhir/code "text/cql-identifier" - :expression (type/string expr)}) + (type/expression {:language #fhir/code "text/cql-identifier" + :expression (type/string expr)})) (deftest cql-definition-names-test (are [measure names] (= names (u/expression-names measure)) diff --git a/modules/operation-measure-evaluate-measure/test/blaze/fhir/operation/evaluate_measure/measure_test.clj b/modules/operation-measure-evaluate-measure/test/blaze/fhir/operation/evaluate_measure/measure_test.clj index 292ab7070..d816fb191 100644 --- a/modules/operation-measure-evaluate-measure/test/blaze/fhir/operation/evaluate_measure/measure_test.clj +++ b/modules/operation-measure-evaluate-measure/test/blaze/fhir/operation/evaluate_measure/measure_test.clj @@ -47,7 +47,7 @@ ["/MeasureReport/{id}/_history/{vid}" {:name :MeasureReport/versioned-instance}]] {:syntax :bracket})) -(defmulti entry-tx-op (fn [{{:keys [method]} :request}] (type/value method))) +(defmulti entry-tx-op (fn [{{:keys [method]} :request}] (:value method))) (defmethod entry-tx-op "PUT" [{:keys [resource]}] @@ -157,9 +157,8 @@ :code (type/code code)})]})) (defn- cql-expression [expr] - {:fhir/type :fhir/Expression - :language #fhir/code "text/cql-identifier" - :expression (type/string expr)}) + (type/expression {:language #fhir/code "text/cql-identifier" + :expression (type/string expr)})) (defn encode-base64 [^String s] (-> (Base64/getEncoder) @@ -629,7 +628,7 @@ :period := #fhir/Period{:start #fhir/dateTime #system/date-time "2000" :end #fhir/dateTime #system/date-time "2020"} [:group 0 :population 0 :code :coding 0 :code] := #fhir/code "initial-population" - [:group 0 :population 0 :count type/value] := count)))) + [:group 0 :population 0 :count :value] := count)))) (testing "with stratifiers" (doseq [[library count] [[(library-gender true) 1] [(library-gender false) 0]]] @@ -667,14 +666,14 @@ :type := #fhir/code "individual" :measure := #fhir/canonical "measure-155502" [:subject :reference] := #fhir/string "Patient/0" - :date := #system/date-time "1970-01-01T00:00Z" - :period := #fhir/Period{:start #fhir/dateTime "2000" - :end #fhir/dateTime "2020"} + :date := #fhir/dateTime #system/date-time "1970-01-01T00:00Z" + :period := #fhir/Period{:start #fhir/dateTime #system/date-time "2000" + :end #fhir/dateTime #system/date-time "2020"} [:group 0 :population 0 :code :coding 0 :code] := #fhir/code "initial-population" - [:group 0 :population 0 :count type/value] := count + [:group 0 :population 0 :count :value] := count [:group 0 :stratifier 0 :code 0 :text] := #fhir/string "gender" - [:group 0 :stratifier 0 :stratum 0 :value :text type/value] := (when (= 1 count) "male") - [:group 0 :stratifier 0 :stratum 0 :population 0 :count type/value] := (when (= 1 count) 1)))))) + [:group 0 :stratifier 0 :stratum 0 :value :text :value] := (when (= 1 count) "male") + [:group 0 :stratifier 0 :stratum 0 :population 0 :count :value] := (when (= 1 count) 1)))))) (testing "invalid subject" (with-system-data @@ -880,7 +879,7 @@ (defmacro testing-query [name count] `(testing ~name - (is (= ~count (-> (first-population (evaluate ~name)) :count type/value))))) + (is (= ~count (-> (first-population (evaluate ~name)) :count :value))))) (deftest integration-test (testing-query "q1" 2) @@ -1176,9 +1175,9 @@ (given (first-stratifier-strata (evaluate "q41-specimen-multi-stratifier")) count := 4 - [0 :component 0 :code :coding 0 :code type/value] := "sample-diagnosis" + [0 :component 0 :code :coding 0 :code :value] := "sample-diagnosis" [0 :component 0 :value :text] := #fhir/string "C34.9" - [0 :component 1 :code :coding 0 :code type/value] := "sample-type" + [0 :component 1 :code :coding 0 :code :value] := "sample-type" [0 :component 1 :value :text] := #fhir/string "blood-plasma" [0 :population 0 :count] := #fhir/integer 2 [1 :component 0 :value :text] := #fhir/string "C34.9" diff --git a/modules/operation-measure-evaluate-measure/test/blaze/fhir/operation/evaluate_measure/middleware/params_test.clj b/modules/operation-measure-evaluate-measure/test/blaze/fhir/operation/evaluate_measure/middleware/params_test.clj index c7c7727f1..09afa2a66 100644 --- a/modules/operation-measure-evaluate-measure/test/blaze/fhir/operation/evaluate_measure/middleware/params_test.clj +++ b/modules/operation-measure-evaluate-measure/test/blaze/fhir/operation/evaluate_measure/middleware/params_test.clj @@ -108,8 +108,8 @@ @(handler {:body (fu/parameters - "periodStart" #fhir/date "2020" - "periodEnd" #fhir/date "2021")})] + "periodStart" #fhir/date #system/date "2020" + "periodEnd" #fhir/date #system/date "2021")})] (given params [:period 0] := #system/date"2020" @@ -120,9 +120,9 @@ (let [request {:request-method :post :body (fu/parameters - "periodStart" #fhir/date "2014" - "periodEnd" #fhir/date "2015" - "measure" #fhir/date "2015")} + "periodStart" #fhir/date #system/date "2014" + "periodEnd" #fhir/date #system/date "2015" + "measure" #fhir/date #system/date "2015")} {:keys [status body]} @(handler request)] (is (= 400 status)) @@ -142,8 +142,8 @@ "measure" "measure-202606"}} {:body (fu/parameters - "periodStart" #fhir/date "2014" - "periodEnd" #fhir/date "2015" + "periodStart" #fhir/date #system/date "2014" + "periodEnd" #fhir/date #system/date "2015" "measure" #fhir/string "measure-202606")}]] (let [{:blaze.fhir.operation.evaluate-measure/keys [params]} @(handler request)] @@ -194,8 +194,8 @@ {:request-method :post :body (fu/parameters - "periodStart" #fhir/date "2014" - "periodEnd" #fhir/date "2015" + "periodStart" #fhir/date #system/date "2014" + "periodEnd" #fhir/date #system/date "2015" "reportType" #fhir/code "subject-list")})] (given params diff --git a/modules/operation-measure-evaluate-measure/test/blaze/fhir/operation/evaluate_measure_test.clj b/modules/operation-measure-evaluate-measure/test/blaze/fhir/operation/evaluate_measure_test.clj index 51e5c62d3..9e0ebbf2e 100644 --- a/modules/operation-measure-evaluate-measure/test/blaze/fhir/operation/evaluate_measure_test.clj +++ b/modules/operation-measure-evaluate-measure/test/blaze/fhir/operation/evaluate_measure_test.clj @@ -63,9 +63,8 @@ :code (type/code code)})]})) (defn- cql-expression [expr] - {:fhir/type :fhir/Expression - :language #fhir/code "text/cql-identifier" - :expression (type/string expr)}) + (type/expression {:language #fhir/code "text/cql-identifier" + :expression (type/string expr)})) (def cql-attachment #fhir/Attachment @@ -269,9 +268,9 @@ :status := #fhir/code "complete" :type := #fhir/code "summary" :measure := #fhir/canonical "url-181501" - :date := #fhir/dateTime "1970-01-01T00:00:00Z" - [:period :start] := #fhir/dateTime "2014" - [:period :end] := #fhir/dateTime "2015"))))) + :date := #fhir/dateTime #system/date-time "1970-01-01T00:00:00Z" + [:period :start] := #fhir/dateTime #system/date-time "2014" + [:period :end] := #fhir/dateTime #system/date-time "2015"))))) (testing "as POST request" (with-handler [handler] @@ -288,8 +287,8 @@ :path-params {:id "0"} :body (fu/parameters - "periodStart" #fhir/date "2014" - "periodEnd" #fhir/date "2015")})] + "periodStart" #fhir/date #system/date "2014" + "periodEnd" #fhir/date #system/date "2015")})] (is (= 201 status)) @@ -302,9 +301,9 @@ :status := #fhir/code "complete" :type := #fhir/code "summary" :measure := #fhir/canonical "url-181501" - :date := #fhir/dateTime "1970-01-01T00:00:00Z" - [:period :start] := #fhir/dateTime "2014" - [:period :end] := #fhir/dateTime "2015")))))) + :date := #fhir/dateTime #system/date-time "1970-01-01T00:00:00Z" + [:period :start] := #fhir/dateTime #system/date-time "2014" + [:period :end] := #fhir/dateTime #system/date-time "2015")))))) (testing "Returns Not Found on Non-Existing Measure" (with-handler [handler] @@ -401,7 +400,7 @@ :fhir/type := :fhir/OperationOutcome [:issue 0 :severity] := #fhir/code "error" [:issue 0 :code] := #fhir/code "value" - [:issue 0 :diagnostics] := (type/string (format "The Library resource with canonical URI `%s` was not found." (type/value library-ref)))))))) + [:issue 0 :diagnostics] := (type/string (format "The Library resource with canonical URI `%s` was not found." (:value library-ref)))))))) (testing "Returns Server Error on Missing Measure Content" (with-redefs [rs/get (fn [_ _] (ac/completed-future nil))] @@ -510,13 +509,13 @@ [:extension 0 :value :code] := #fhir/code "s" [:extension 0 :value :system] := #fhir/uri "http://unitsofmeasure.org" [:extension 0 :value :unit] := #fhir/string "s" - [:extension 0 :value :value type/value] :? decimal? + [:extension 0 :value :value :value] :? decimal? :status := #fhir/code "complete" :type := #fhir/code "summary" :measure := #fhir/canonical "url-181501" - :date := #fhir/dateTime "1970-01-01T00:00:00Z" - [:period :start] := #fhir/dateTime "2014" - [:period :end] := #fhir/dateTime "2015" + :date := #fhir/dateTime #system/date-time "1970-01-01T00:00:00Z" + [:period :start] := #fhir/dateTime #system/date-time "2014" + [:period :end] := #fhir/dateTime #system/date-time "2015" [:group 0 :population 0 :code :coding 0 :system] := measure-population-uri [:group 0 :population 0 :code :coding 0 :code] := #fhir/code "initial-population" [:group 0 :population 0 :count] := #fhir/integer 1))))) @@ -569,9 +568,9 @@ :status := #fhir/code "complete" :type := #fhir/code "summary" :measure := #fhir/canonical "url-181501" - :date := #fhir/dateTime "1970-01-01T00:00:00Z" - [:period :start] := #fhir/dateTime "2014" - [:period :end] := #fhir/dateTime "2015" + :date := #fhir/dateTime #system/date-time "1970-01-01T00:00:00Z" + [:period :start] := #fhir/dateTime #system/date-time "2014" + [:period :end] := #fhir/dateTime #system/date-time "2015" [:group 0 :population 0 :code :coding 0 :system] := measure-population-uri [:group 0 :population 0 :code :coding 0 :code] := #fhir/code "initial-population" [:group 0 :population 0 :count] := #fhir/integer 3 @@ -601,8 +600,8 @@ :body (fu/parameters "measure" #fhir/string "url-181501" - "periodStart" #fhir/date "2014" - "periodEnd" #fhir/date "2015")})] + "periodStart" #fhir/date #system/date "2014" + "periodEnd" #fhir/date #system/date "2015")})] (is (= 201 status)) @@ -615,9 +614,9 @@ :status := #fhir/code "complete" :type := #fhir/code "summary" :measure := #fhir/canonical "url-181501" - :date := #fhir/dateTime "1970-01-01T00:00:00Z" - [:period :start] := #fhir/dateTime "2014" - [:period :end] := #fhir/dateTime "2015") + :date := #fhir/dateTime #system/date-time "1970-01-01T00:00:00Z" + [:period :start] := #fhir/dateTime #system/date-time "2014" + [:period :end] := #fhir/dateTime #system/date-time "2015") (testing "with return=minimal Prefer header" (with-handler [handler] @@ -635,8 +634,8 @@ :body (fu/parameters "measure" #fhir/string "url-181501" - "periodStart" #fhir/date "2014" - "periodEnd" #fhir/date "2015")})] + "periodStart" #fhir/date #system/date "2014" + "periodEnd" #fhir/date #system/date "2015")})] (is (= 201 status)) @@ -669,8 +668,8 @@ {:request-method :post :body (fu/parameters - "periodStart" #fhir/date "2014" - "periodEnd" #fhir/date "2015")})] + "periodStart" #fhir/date #system/date "2014" + "periodEnd" #fhir/date #system/date "2015")})] (is (= 422 status)) @@ -704,8 +703,8 @@ {:request-method :post :body (fu/parameters - "periodStart" #fhir/date "2014" - "periodEnd" #fhir/date "2015" + "periodStart" #fhir/date #system/date "2014" + "periodEnd" #fhir/date #system/date "2015" "measure" #fhir/string "url-181501")})] (is (= 422 status)) diff --git a/modules/operation-patient-everything/test/blaze/operation/patient/everything_test.clj b/modules/operation-patient-everything/test/blaze/operation/patient/everything_test.clj index 7c7084955..8950e5190 100644 --- a/modules/operation-patient-everything/test/blaze/operation/patient/everything_test.clj +++ b/modules/operation-patient-everything/test/blaze/operation/patient/everything_test.clj @@ -4,7 +4,6 @@ [blaze.db.api :as d] [blaze.db.api-stub :as api-stub :refer [with-system-data]] [blaze.db.tx-log :as tx-log] - [blaze.fhir.spec :as fhir-spec] [blaze.fhir.test-util :refer [link-url]] [blaze.handler.fhir.util-spec] [blaze.handler.util :as handler-util] @@ -253,11 +252,11 @@ :fhir/type := :fhir/Patient :id := "0" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH)) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")) (testing "the entry has the right search mode" (given (:search first-entry) - fhir-spec/fhir-type := :fhir.Bundle.entry/search + :fhir/type := :fhir.Bundle.entry/search :mode := #fhir/code "match")))))) (deftest patient-with @@ -297,11 +296,11 @@ :fhir/type := :fhir/Patient :id := "0" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH)) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")) (testing "the first entry has the right search mode" (given (:search first-entry) - fhir-spec/fhir-type := :fhir.Bundle.entry/search + :fhir/type := :fhir.Bundle.entry/search :mode := #fhir/code "match")) (testing "the second entry has the right fullUrl" @@ -313,11 +312,11 @@ :fhir/type := (keyword "fhir" type) :id := "0" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH)) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")) (testing "the second entry has the right search mode" (given (:search second-entry) - fhir-spec/fhir-type := :fhir.Bundle.entry/search + :fhir/type := :fhir.Bundle.entry/search :mode := #fhir/code "match")))))) (testing "Patient with two Observations" @@ -358,11 +357,11 @@ :fhir/type := :fhir/Patient :id := "0" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH)) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")) (testing "the first entry has the right search mode" (given (:search first-entry) - fhir-spec/fhir-type := :fhir.Bundle.entry/search + :fhir/type := :fhir.Bundle.entry/search :mode := #fhir/code "match")) (testing "the second entry has the right fullUrl" @@ -374,11 +373,11 @@ :fhir/type := :fhir/Observation :id := "0" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH)) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")) (testing "the second entry has the right search mode" (given (:search second-entry) - fhir-spec/fhir-type := :fhir.Bundle.entry/search + :fhir/type := :fhir.Bundle.entry/search :mode := #fhir/code "match")) (testing "the third entry has the right fullUrl" @@ -390,11 +389,11 @@ :fhir/type := :fhir/Observation :id := "1" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH)) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")) (testing "the third entry has the right search mode" (given (:search third-entry) - fhir-spec/fhir-type := :fhir.Bundle.entry/search + :fhir/type := :fhir.Bundle.entry/search :mode := #fhir/code "match")))))) (deftest start-date @@ -405,7 +404,7 @@ :subject #fhir/Reference{:reference #fhir/string "Patient/0"}}] [:put {:fhir/type :fhir/Observation :id "1" :subject #fhir/Reference{:reference #fhir/string "Patient/0"} - :effective #fhir/dateTime "2024-01-04T23:45:50Z"}]]] + :effective #fhir/dateTime #system/date-time "2024-01-04T23:45:50Z"}]]] (let [{:keys [status] {[first-entry second-entry] :entry :as body} :body} @@ -438,11 +437,11 @@ :fhir/type := :fhir/Patient :id := "0" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH)) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")) (testing "the first entry has the right search mode" (given (:search first-entry) - fhir-spec/fhir-type := :fhir.Bundle.entry/search + :fhir/type := :fhir.Bundle.entry/search :mode := #fhir/code "match")) (testing "the second entry has the right fullUrl" @@ -454,11 +453,11 @@ :fhir/type := :fhir/Observation :id := "1" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH)) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")) (testing "the second entry has the right search mode" (given (:search second-entry) - fhir-spec/fhir-type := :fhir.Bundle.entry/search + :fhir/type := :fhir.Bundle.entry/search :mode := #fhir/code "match")))))) (deftest end-date @@ -469,7 +468,7 @@ :subject #fhir/Reference{:reference #fhir/string "Patient/0"}}] [:put {:fhir/type :fhir/Observation :id "1" :subject #fhir/Reference{:reference #fhir/string "Patient/0"} - :effective #fhir/dateTime "2024-01-04T23:45:50Z"}]]] + :effective #fhir/dateTime #system/date-time "2024-01-04T23:45:50Z"}]]] (let [{:keys [status] {[first-entry second-entry] :entry :as body} :body} @@ -502,11 +501,11 @@ :fhir/type := :fhir/Patient :id := "0" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH)) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")) (testing "the first entry has the right search mode" (given (:search first-entry) - fhir-spec/fhir-type := :fhir.Bundle.entry/search + :fhir/type := :fhir.Bundle.entry/search :mode := #fhir/code "match")) (testing "the second entry has the right fullUrl" @@ -518,11 +517,11 @@ :fhir/type := :fhir/Observation :id := "1" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH)) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")) (testing "the second entry has the right search mode" (given (:search second-entry) - fhir-spec/fhir-type := :fhir.Bundle.entry/search + :fhir/type := :fhir.Bundle.entry/search :mode := #fhir/code "match"))))) (testing "Patient with various resources" @@ -756,10 +755,10 @@ :subject #fhir/Reference{:reference #fhir/string "Patient/0"}}] [:put {:fhir/type :fhir/Observation :id "1" :subject #fhir/Reference{:reference #fhir/string "Patient/0"} - :effective #fhir/dateTime "2024-01-04T23:45:50Z"}] + :effective #fhir/dateTime #system/date-time "2024-01-04T23:45:50Z"}] [:put {:fhir/type :fhir/Observation :id "2" :subject #fhir/Reference{:reference #fhir/string "Patient/0"} - :effective #fhir/dateTime "2024-01-05T23:45:50Z"}]]] + :effective #fhir/dateTime #system/date-time "2024-01-05T23:45:50Z"}]]] (let [{:keys [status] {[first-entry second-entry] :entry :as body} :body} @@ -796,11 +795,11 @@ :fhir/type := :fhir/Patient :id := "0" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH)) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")) (testing "the first entry has the right search mode" (given (:search first-entry) - fhir-spec/fhir-type := :fhir.Bundle.entry/search + :fhir/type := :fhir.Bundle.entry/search :mode := #fhir/code "match")) (testing "the second entry has the right fullUrl" @@ -812,11 +811,11 @@ :fhir/type := :fhir/Observation :id := "1" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH)) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")) (testing "the second entry has the right search mode" (given (:search second-entry) - fhir-spec/fhir-type := :fhir.Bundle.entry/search + :fhir/type := :fhir.Bundle.entry/search :mode := #fhir/code "match"))) (testing "following the next link" @@ -859,13 +858,13 @@ :subject #fhir/Reference{:reference #fhir/string "Patient/0"}}] [:put {:fhir/type :fhir/Observation :id "1" :subject #fhir/Reference{:reference #fhir/string "Patient/0"} - :effective #fhir/dateTime "2024-01-04T23:45:50Z"}] + :effective #fhir/dateTime #system/date-time "2024-01-04T23:45:50Z"}] [:put {:fhir/type :fhir/Observation :id "2" :subject #fhir/Reference{:reference #fhir/string "Patient/0"} - :effective #fhir/dateTime "2024-01-05T23:45:50Z"}] + :effective #fhir/dateTime #system/date-time "2024-01-05T23:45:50Z"}] [:put {:fhir/type :fhir/Observation :id "3" :subject #fhir/Reference{:reference #fhir/string "Patient/0"} - :effective #fhir/dateTime "2026-01-05T23:45:50Z"}]]] + :effective #fhir/dateTime #system/date-time "2026-01-05T23:45:50Z"}]]] (let [{:keys [status] {[first-entry second-entry] :entry :as body} :body} @@ -903,11 +902,11 @@ :fhir/type := :fhir/Patient :id := "0" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH)) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")) (testing "the first entry has the right search mode" (given (:search first-entry) - fhir-spec/fhir-type := :fhir.Bundle.entry/search + :fhir/type := :fhir.Bundle.entry/search :mode := #fhir/code "match")) (testing "the second entry has the right fullUrl" @@ -919,11 +918,11 @@ :fhir/type := :fhir/Observation :id := "1" [:meta :versionId] := #fhir/id "1" - [:meta :lastUpdated] := Instant/EPOCH)) + [:meta :lastUpdated] := #fhir/instant #system/date-time "1970-01-01T00:00:00Z")) (testing "the second entry has the right search mode" (given (:search second-entry) - fhir-spec/fhir-type := :fhir.Bundle.entry/search + :fhir/type := :fhir.Bundle.entry/search :mode := #fhir/code "match"))) (testing "following the next link" @@ -1009,7 +1008,7 @@ system-clock :blaze.test/system-clock} system-clock-config] [[[:put {:fhir/type :fhir/Patient :id "0"}] [:put {:fhir/type :fhir/Observation :id "0" - :subject #fhir/Reference{:reference "Patient/0"}}]]] + :subject #fhir/Reference{:reference #fhir/string "Patient/0"}}]]] (Thread/sleep 2000) (let [handler (wrap-middleware handler node page-id-cipher) @@ -1017,7 +1016,7 @@ (Thread/sleep 2000) @(d/transact node [[:put {:fhir/type :fhir/Observation :id "1" - :subject #fhir/Reference{:reference "Patient/0"}}]]) + :subject #fhir/Reference{:reference #fhir/string "Patient/0"}}]]) (testing "since start of time" (let [{:keys [status] diff --git a/modules/operation-value-set-expand/test/blaze/fhir/operation/value_set/expand_test.clj b/modules/operation-value-set-expand/test/blaze/fhir/operation/value_set/expand_test.clj index d65e43ffb..69a2666c2 100644 --- a/modules/operation-value-set-expand/test/blaze/fhir/operation/value_set/expand_test.clj +++ b/modules/operation-value-set-expand/test/blaze/fhir/operation/value_set/expand_test.clj @@ -72,7 +72,7 @@ (defn- parameter [name] (fn [{:keys [parameter]}] - (some #(when (= name (type/value (:name %))) %) parameter))) + (some #(when (= name (:value (:name %))) %) parameter))) (deftest handler-test (testing "value set not found" diff --git a/modules/operation-value-set-validate-code/src/blaze/fhir/operation/value_set/validate_code.clj b/modules/operation-value-set-validate-code/src/blaze/fhir/operation/value_set/validate_code.clj index cfdf6d251..128867046 100644 --- a/modules/operation-value-set-validate-code/src/blaze/fhir/operation/value_set/validate_code.clj +++ b/modules/operation-value-set-validate-code/src/blaze/fhir/operation/value_set/validate_code.clj @@ -76,7 +76,7 @@ params)) (defn- contains-param? [name {:keys [parameter]}] - (some (comp #{name} type/value :name) parameter)) + (some (comp #{name} :value :name) parameter)) (defn- body-params [{:keys [body] {:strs [accept-language]} :headers}] (cond-> body diff --git a/modules/operation-value-set-validate-code/test/blaze/fhir/operation/value_set/validate_code_test.clj b/modules/operation-value-set-validate-code/test/blaze/fhir/operation/value_set/validate_code_test.clj index 06aedb3e7..e67c596e0 100644 --- a/modules/operation-value-set-validate-code/test/blaze/fhir/operation/value_set/validate_code_test.clj +++ b/modules/operation-value-set-validate-code/test/blaze/fhir/operation/value_set/validate_code_test.clj @@ -72,7 +72,7 @@ (defn- parameter [name] (fn [{:keys [parameter]}] - (filterv #(= name (type/value (:name %))) parameter))) + (filterv #(= name (:value (:name %))) parameter))) (deftest handler-test (testing "value set not found" @@ -189,7 +189,7 @@ (given body :fhir/type := :fhir/Parameters [:parameter 0 :name] := #fhir/string "result" - [:parameter 0 :value type/value] := result))) + [:parameter 0 :value :value] := result))) (testing "ignores unknown parameter" (let [{:keys [status body]} @@ -218,7 +218,7 @@ (given body :fhir/type := :fhir/Parameters [:parameter 0 :name] := #fhir/string "result" - [:parameter 0 :value type/value] := result)))) + [:parameter 0 :value :value] := result)))) (testing "and coding" (let [{:keys [status body]} @@ -292,7 +292,7 @@ (given body :fhir/type := :fhir/Parameters [:parameter 0 :name] := #fhir/string "result" - [:parameter 0 :value type/value] := result))) + [:parameter 0 :value :value] := result))) (testing "with display" (doseq [lang [nil "de" "en,de"]] @@ -461,7 +461,7 @@ (given body :fhir/type := :fhir/Parameters [:parameter 0 :name] := #fhir/string "result" - [:parameter 0 :value type/value] := true)) + [:parameter 0 :value :value] := true)) (testing "with system-version" (let [{:keys [status body]} @@ -475,7 +475,7 @@ (given body :fhir/type := :fhir/Parameters [:parameter 0 :name] := #fhir/string "result" - [:parameter 0 :value type/value] := true))))) + [:parameter 0 :value :value] := true))))) (testing "successful validation by valueSet" (testing "code only" @@ -506,7 +506,7 @@ (given body :fhir/type := :fhir/Parameters [:parameter 0 :name] := #fhir/string "result" - [:parameter 0 :value type/value] := result))))) + [:parameter 0 :value :value] := result))))) (testing "code and system" (with-handler [handler] @@ -536,7 +536,7 @@ (given body :fhir/type := :fhir/Parameters [:parameter 0 :name] := #fhir/string "result" - [:parameter 0 :value type/value] := result)))))) + [:parameter 0 :value :value] := result)))))) (testing "successful validation with externally supplied value set and code system" (with-handler [handler] diff --git a/modules/page-id-cipher/src/blaze/page_id_cipher.clj b/modules/page-id-cipher/src/blaze/page_id_cipher.clj index d9c7b14db..50ac9e549 100644 --- a/modules/page-id-cipher/src/blaze/page_id_cipher.clj +++ b/modules/page-id-cipher/src/blaze/page_id_cipher.clj @@ -77,7 +77,7 @@ (defn- decode-key-set-handle {:argLists '([key-set-resource])} [{[{{:keys [data]} :attachment}] :content}] - (parse-key-set (type/value data))) + (parse-key-set (:value data))) (defn- decode-state [key-set-resource] (let [key-set-handle (decode-key-set-handle key-set-resource)] @@ -93,7 +93,7 @@ (log/trace "Got" (count document-reference-handles) "changed document-reference(s)") (run! - (fn [{[{:keys [value]}] :identifier :as document-reference}] + (fn [{[{{value :value} :value}] :identifier :as document-reference}] (when (= identifier value) (log/debug "Refresh key set") (reset! state (decode-state document-reference)))) @@ -116,7 +116,7 @@ (assoc key-set-resource :content [(key-set-content (f handle))]))) (defn- update-tx-op [{{version-id :versionId} :meta :as resource}] - [:put resource [:if-match (parse-long (type/value version-id))]]) + [:put resource [:if-match (parse-long (:value version-id))]]) (defn- update-resource [node resource f] (d/transact node [(update-tx-op (f resource))])) diff --git a/modules/rest-api/src/blaze/rest_api/async_status_handler.clj b/modules/rest-api/src/blaze/rest_api/async_status_handler.clj index 9411b19e4..c4706439f 100644 --- a/modules/rest-api/src/blaze/rest_api/async_status_handler.clj +++ b/modules/rest-api/src/blaze/rest_api/async_status_handler.clj @@ -3,7 +3,6 @@ [blaze.anomaly :as ba] [blaze.async.comp :as ac :refer [do-sync]] [blaze.fhir.spec.references :as fsr] - [blaze.fhir.spec.type :as type] [blaze.handler.fhir.util :as fhir-util] [blaze.handler.util :as handler-util] [blaze.job.async-interaction :as job-async] @@ -19,7 +18,7 @@ (-> (fhir-util/pull db "Task" id) (ac/then-compose (fn [{:keys [status] :as job}] - (case (type/value status) + (case (:value status) "ready" (-> (ring/status 202) (ring/header "X-Progress" "ready") diff --git a/modules/rest-api/src/blaze/rest_api/capabilities_handler.clj b/modules/rest-api/src/blaze/rest_api/capabilities_handler.clj index 0f7c601e0..aeb5eb7e4 100644 --- a/modules/rest-api/src/blaze/rest_api/capabilities_handler.clj +++ b/modules/rest-api/src/blaze/rest_api/capabilities_handler.clj @@ -296,9 +296,8 @@ (assoc-in capability-statement-base [:implementation :url] (type/url (str base-url context-path)))) -(defn- profile-canonical [{:keys [url version]}] - (let [version (type/value version)] - (type/canonical (cond-> (type/value url) version (str "|" version))))) +(defn- profile-canonical [{{url :value} :url {version :value} :version}] + (type/canonical (cond-> url version (str "|" version)))) (defn- supported-profiles-query [db type] (d/type-query db "StructureDefinition" [["type" type] ["derivation" "constraint"]])) @@ -308,7 +307,7 @@ (mapv profile-canonical profiles))) (defn- assoc-supported-profiles** [db {:keys [type] :as resource}] - (do-sync [supported-profiles (supported-profiles db (type/value type))] + (do-sync [supported-profiles (supported-profiles db (:value type))] (cond-> resource (seq supported-profiles) (assoc :supportedProfile supported-profiles)))) diff --git a/modules/rest-api/src/blaze/rest_api/middleware/auth_guard.clj b/modules/rest-api/src/blaze/rest_api/middleware/auth_guard.clj index 0f6f72dc4..356512b06 100644 --- a/modules/rest-api/src/blaze/rest_api/middleware/auth_guard.clj +++ b/modules/rest-api/src/blaze/rest_api/middleware/auth_guard.clj @@ -9,7 +9,7 @@ (def ^:private ^:const msg-auth-required #fhir/Coding - {:system #fhir/uri "http://terminology.hl7.org/CodeSystem/operation-outcome" + {:system #fhir/uri-interned "http://terminology.hl7.org/CodeSystem/operation-outcome" :code #fhir/code "MSG_AUTH_REQUIRED"}) (defn not-authenticated-response [request] diff --git a/modules/rest-api/src/blaze/rest_api/structure_definitions.clj b/modules/rest-api/src/blaze/rest_api/structure_definitions.clj index d1b6e9906..78c9dcf1f 100644 --- a/modules/rest-api/src/blaze/rest_api/structure_definitions.clj +++ b/modules/rest-api/src/blaze/rest_api/structure_definitions.clj @@ -5,7 +5,6 @@ [blaze.async.comp :as ac :refer [do-sync]] [blaze.db.api :as d] [blaze.fhir.spec :as fhir-spec] - [blaze.fhir.spec.type :as type] [blaze.fhir.structure-definition-repo :as sdr] [blaze.luid :as luid] [blaze.module :as m] @@ -15,7 +14,7 @@ (def ^:private read-only-tag #fhir/Coding - {:system #fhir/uri "https://samply.github.io/blaze/fhir/CodeSystem/AccessControl" + {:system #fhir/uri-interned "https://samply.github.io/blaze/fhir/CodeSystem/AccessControl" :code #fhir/code "read-only"}) (defn- structure-definitions [db] @@ -26,15 +25,15 @@ (defn- structure-definition-urls [db] (do-sync [structure-definitions (structure-definitions db)] - (into #{} (comp (map (comp type/value :url)) url-filter) structure-definitions))) + (into #{} (comp (map (comp :value :url)) url-filter) structure-definitions))) (defn- tx-op [{:keys [url] :as structure-definition} luid-generator] [:create (assoc structure-definition :id (luid/head luid-generator)) - [["url" (type/value url)]]]) + [["url" (:value url)]]]) (defn- tx-ops [context existing-urls structure-definitions] (transduce - (remove (comp existing-urls type/value :url)) + (remove (comp existing-urls :value :url)) (fn ([{:keys [tx-ops]}] tx-ops) ([{:keys [luid-generator] :as ret} structure-definition] @@ -45,8 +44,8 @@ structure-definitions)) (defn- conform [parsing-context resource] - (fhir-spec/parse-json parsing-context "StructureDefinition" - (j/write-value-as-string resource))) + (->> (j/write-value-as-bytes resource) + (fhir-spec/parse-json parsing-context "StructureDefinition"))) (defn- append-read-only-tag [resource] (update-in resource [:meta :tag] (fnil conj []) read-only-tag)) diff --git a/modules/rest-api/test/blaze/rest_api/capabilities_handler_test.clj b/modules/rest-api/test/blaze/rest_api/capabilities_handler_test.clj index 5bef9811b..875807cb2 100644 --- a/modules/rest-api/test/blaze/rest_api/capabilities_handler_test.clj +++ b/modules/rest-api/test/blaze/rest_api/capabilities_handler_test.clj @@ -5,7 +5,7 @@ [blaze.db.impl.search-param] [blaze.fhir.parsing-context] [blaze.fhir.spec :as fhir-spec] - [blaze.fhir.spec.type :as type] + [blaze.fhir.spec.type.system :as system] [blaze.fhir.structure-definition-repo :as sdr] [blaze.fhir.test-util :refer [structure-definition-repo]] [blaze.middleware.fhir.db :refer [wrap-db]] @@ -41,7 +41,7 @@ mem-node-config ::rest-api/capabilities-handler {:version "version-131640" - :release-date "2024-01-07" + :release-date (system/parse-date-time "2024-01-07") :structure-definition-repo structure-definition-repo :search-param-registry (ig/ref :blaze.db/search-param-registry)})) @@ -115,7 +115,7 @@ (is (= 200 status)) (testing "ETag header" - (is (= "W/\"c3a1c2c6\"" (get headers "ETag")))) + (is (= "W/\"707af296\"" (get headers "ETag")))) (given body :fhir/type := :fhir/CapabilityStatement @@ -181,7 +181,7 @@ (= (set (conj ks :fhir/type)) (set (keys body)))))))) (testing "cache validation" - (doseq [if-none-match ["W/\"c3a1c2c6\"" "W/\"c3a1c2c6\", \"foo\""]] + (doseq [if-none-match ["W/\"707af296\"" "W/\"707af296\", \"foo\""]] (let [{:keys [status headers]} @(handler {:headers {"if-none-match" if-none-match} @@ -190,7 +190,7 @@ (is (= 304 status)) (testing "ETag header" - (is (= "W/\"c3a1c2c6\"" (get headers "ETag")))))))) + (is (= "W/\"707af296\"" (get headers "ETag")))))))) (testing "mode=terminology is ignored" (with-handler [handler minimal-config] @@ -360,7 +360,7 @@ {:handler (fn [_])}}}])) (defn- search-param [name] - (fn [params] (some #(when (= name (-> % :name type/value)) %) params))) + (fn [params] (some #(when (= name (-> % :name :value)) %) params))) (deftest observation-read-interaction-test (with-handler [handler observation-read-interaction-config] diff --git a/modules/rest-api/test/blaze/rest_api_test.clj b/modules/rest-api/test/blaze/rest_api_test.clj index a59389793..f3c0a434f 100644 --- a/modules/rest-api/test/blaze/rest_api_test.clj +++ b/modules/rest-api/test/blaze/rest_api_test.clj @@ -5,6 +5,7 @@ [blaze.db.api-stub :refer [mem-node-config]] [blaze.db.impl.search-param] [blaze.fhir.parsing-context] + [blaze.fhir.spec.type.system :as system] [blaze.fhir.structure-definition-repo.protocols :as sdrp] [blaze.fhir.test-util :refer [structure-definition-repo]] [blaze.fhir.writing-context] @@ -132,7 +133,7 @@ :rng-fn (ig/ref :blaze.test/fixed-rng-fn)} ::rest-api/capabilities-handler {:version "version-131640" - :release-date "2024-05-23" + :release-date (system/parse-date-time "2024-05-23") :structure-definition-repo structure-definition-repo :search-param-registry (ig/ref :blaze.db/search-param-registry) :terminology-service (ig/ref ::ts/local)} diff --git a/modules/rest-util/src/blaze/fhir/response/create.clj b/modules/rest-util/src/blaze/fhir/response/create.clj index e4cc7e2b5..6c15bea67 100644 --- a/modules/rest-util/src/blaze/fhir/response/create.clj +++ b/modules/rest-util/src/blaze/fhir/response/create.clj @@ -26,13 +26,13 @@ (identical? :keep op)) (defn build-response - [{:blaze/keys [db] :as context} tx-op old-handle {:keys [id] :as new-handle}] - (let [type (name (:fhir/type new-handle)) - tx (d/tx db (:t new-handle)) + [{:blaze/keys [db] :as context} tx-op old-handle + {:fhir/keys [type] :keys [id] :as new-handle}] + (let [tx (d/tx db (:t new-handle)) vid (str (:blaze.db/t tx)) created (and (not (keep? tx-op)) (or (nil? old-handle) (identical? :delete (:op old-handle))))] - (log/trace (format "build-response of %s/%s with vid = %s" type id vid)) + (log/trace (format "build-response of %s/%s with vid = %s" (name type) id vid)) (do-sync [body (body context new-handle)] (cond-> (-> (ring/response body) @@ -40,4 +40,4 @@ (ring/header "Last-Modified" (fhir-util/last-modified tx)) (ring/header "ETag" (str "W/\"" vid "\""))) created - (location-header context type id vid))))) + (location-header context (name type) id vid))))) diff --git a/modules/rest-util/src/blaze/handler/fhir/util.clj b/modules/rest-util/src/blaze/handler/fhir/util.clj index 18703fc9a..01684e0bb 100644 --- a/modules/rest-util/src/blaze/handler/fhir/util.clj +++ b/modules/rest-util/src/blaze/handler/fhir/util.clj @@ -254,7 +254,7 @@ (def ^:private return-preference-pred #(when (= return-preference-url (:url %)) - (keyword "blaze.preference" (type/value (:value %))))) + (keyword "blaze.preference" (-> % :value :value)))) (defn- find-return-preference [{extensions :extension}] (some return-preference-pred extensions)) @@ -266,14 +266,14 @@ return-preference :blaze.preference/return :or {context-path ""}} {{:keys [method url] - if-none-match :ifNoneMatch - if-match :ifMatch - if-none-exist :ifNoneExist + {if-none-match :value} :ifNoneMatch + {if-match :value} :ifMatch + {if-none-exist :value} :ifNoneExist :as request} :request :keys [resource]}] - (let [url (-> url type/value u/strip-leading-slashes) + (let [url (-> url :value u/strip-leading-slashes) [url query-string] (str/split url #"\?") - method (keyword (str/lower-case (type/value method))) + method (keyword (str/lower-case (:value method))) return-preference (or (find-return-preference request) return-preference (when (#{:post :put} method) @@ -310,7 +310,7 @@ (defn- convert-http-date "Converts string `s` representing an HTTP date into a FHIR instant." [s] - (Instant/from (.parse DateTimeFormatter/RFC_1123_DATE_TIME s))) + (type/instant (OffsetDateTime/from (.parse DateTimeFormatter/RFC_1123_DATE_TIME s)))) (defn- bundle-response {:arglists '([ring-response])} @@ -493,8 +493,8 @@ anomalies to indicate the position of the entry in the bundle." {:arglists '([idx entry])} [idx {:keys [request resource] :as entry}] - (let [method (some-> request :method type/value) - [url] (some-> request :url type/value u/strip-leading-slashes (str/split #"\?")) + (let [method (some-> request :method :value) + [url] (some-> request :url :value u/strip-leading-slashes (str/split #"\?")) {:keys [type id kind]} (some-> url match-url)] (cond (nil? request) diff --git a/modules/rest-util/src/blaze/handler/util.clj b/modules/rest-util/src/blaze/handler/util.clj index ae149381d..da40fd8be 100644 --- a/modules/rest-util/src/blaze/handler/util.clj +++ b/modules/rest-util/src/blaze/handler/util.clj @@ -14,8 +14,11 @@ [ring.util.response :as ring] [taoensso.timbre :as log]) (:import + [java.time Instant ZoneOffset] [java.util.concurrent CompletionException])) +(set! *warn-on-reflection* true) + (def ^:private valid-return-preferences #{"minimal" "representation" "OperationOutcome"}) @@ -72,7 +75,7 @@ (assoc :details {:coding - [{:system #fhir/uri "http://terminology.hl7.org/CodeSystem/operation-outcome" + [{:system #fhir/uri-interned "http://terminology.hl7.org/CodeSystem/operation-outcome" :code (type/code operation-outcome)}]}) message (assoc :diagnostics (type/string message)) @@ -237,3 +240,6 @@ "A handler returning failed futures." (reitit.ring/create-default-handler {:method-not-allowed method-not-allowed-batch-handler})) + +(defn instant [{:blaze.db.tx/keys [instant]}] + (type/instant (.atOffset ^Instant instant ZoneOffset/UTC))) diff --git a/modules/rest-util/src/blaze/handler/util_spec.clj b/modules/rest-util/src/blaze/handler/util_spec.clj index 9f481f48d..f7092b3be 100644 --- a/modules/rest-util/src/blaze/handler/util_spec.clj +++ b/modules/rest-util/src/blaze/handler/util_spec.clj @@ -11,3 +11,7 @@ (s/fdef handler-util/error-response :args (s/cat :error some?) :ret map?) + +(s/fdef handler-util/instant + :args (s/cat :tx :blaze.db/tx) + :ret :fhir/instant) diff --git a/modules/rest-util/src/blaze/interaction/search/util.clj b/modules/rest-util/src/blaze/interaction/search/util.clj index 6ef394571..dec559d54 100644 --- a/modules/rest-util/src/blaze/interaction/search/util.clj +++ b/modules/rest-util/src/blaze/interaction/search/util.clj @@ -9,13 +9,13 @@ [integrant.core :as ig])) (def ^:const match - #fhir/BundleEntrySearch{:mode #fhir/code "match"}) + #fhir.Bundle.entry/search{:mode #fhir/code "match"}) (def ^:const include - #fhir/BundleEntrySearch{:mode #fhir/code "include"}) + #fhir.Bundle.entry/search{:mode #fhir/code "include"}) (def ^:const outcome - #fhir/BundleEntrySearch{:mode #fhir/code "outcome"}) + #fhir.Bundle.entry/search{:mode #fhir/code "outcome"}) (defn- full-url [context {:fhir/keys [type] :keys [id]}] (type/uri (fhir-util/instance-url context (name type) id))) @@ -53,7 +53,7 @@ "4.0.1" (fn link [relation url] {:fhir/type :fhir.Bundle/link - :relation (type/string relation) + :relation (type/string-interned relation) :url (type/uri url)}) (fn link [relation url] {:fhir/type :fhir.Bundle/link diff --git a/modules/rest-util/src/blaze/middleware/fhir/output.clj b/modules/rest-util/src/blaze/middleware/fhir/output.clj index 494eba263..d59580375 100644 --- a/modules/rest-util/src/blaze/middleware/fhir/output.clj +++ b/modules/rest-util/src/blaze/middleware/fhir/output.clj @@ -7,7 +7,6 @@ (:require [blaze.anomaly :as ba] [blaze.fhir.spec :as fhir-spec] - [blaze.fhir.spec.type :as type] [blaze.handler.util :as handler-util] [clojure.data.xml :as xml] [clojure.java.io :as io] @@ -50,12 +49,12 @@ (with-open [writer (io/writer output-stream)] (xml/emit (fhir-spec/unform-xml body) writer)))))) -(defn- generate-binary** [{:keys [data]}] +(defn- generate-binary** [{{data :value} :data}] (when data - (.decode (Base64/getDecoder) ^String (type/value data)))) + (.decode (Base64/getDecoder) ^String data))) (defn- binary-content-type [body] - (or (-> body :contentType type/value) + (or (-> body :contentType :value) "application/octet-stream")) (defn- generate-binary* [writing-context {:keys [body] :as response}] diff --git a/modules/rest-util/src/blaze/middleware/link_headers.clj b/modules/rest-util/src/blaze/middleware/link_headers.clj index e80f8d845..dbe6a5fc0 100644 --- a/modules/rest-util/src/blaze/middleware/link_headers.clj +++ b/modules/rest-util/src/blaze/middleware/link_headers.clj @@ -4,12 +4,11 @@ (:refer-clojure :exclude [str]) (:require [blaze.async.comp :refer [do-sync]] - [blaze.fhir.spec.type :as type] [blaze.util :refer [str]] [clojure.string :as str])) -(defn- link-header-value [{:keys [relation url]}] - (str "<" (type/value url) ">;rel=\"" (type/value relation) "\"")) +(defn- link-header-value [{{relation :value} :relation {url :value} :url}] + (str "<" url ">;rel=\"" relation "\"")) (defn- add-link-header [response links] (let [value (str/join "," (map link-header-value links))] diff --git a/modules/rest-util/test/blaze/handler/fhir/util_test.clj b/modules/rest-util/test/blaze/handler/fhir/util_test.clj index ab5cc306e..d6cb067ec 100644 --- a/modules/rest-util/test/blaze/handler/fhir/util_test.clj +++ b/modules/rest-util/test/blaze/handler/fhir/util_test.clj @@ -6,7 +6,6 @@ [blaze.db.api-stub :refer [mem-node-config with-system-data]] [blaze.fhir.spec.generators :as fg] [blaze.fhir.spec.type :as type] - [blaze.fhir.spec.type.system :as system] [blaze.fhir.util :as fu] [blaze.handler.fhir.util :as fhir-util] [blaze.handler.fhir.util-spec] @@ -26,7 +25,7 @@ [reitit.core :as reitit] [ring.util.response :as ring]) (:import - [java.time Instant ZoneId ZonedDateTime] + [java.time Instant ZoneId ZoneOffset ZonedDateTime] [java.time.format DateTimeFormatter])) (set! *warn-on-reflection* true) @@ -209,8 +208,8 @@ (tu/satisfies-prop 1000 (prop/for-all [name gen/string-alphanumeric value fg/date-value] - (let [query-params {name value}] - (= (system/parse-date value) (fhir-util/date query-params name))))))) + (let [query-params {name (str value)}] + (= value (fhir-util/date query-params name))))))) (def router (reitit/router @@ -807,7 +806,7 @@ (= response {:fhir/type :fhir.Bundle.entry/response :status #fhir/string "200" - :lastModified (time/truncate-to (:blaze.db.tx/instant tx) :seconds) + :lastModified (type/instant (.atOffset ^Instant (time/truncate-to (:blaze.db.tx/instant tx) :seconds) ZoneOffset/UTC)) :etag (type/string (fhir-util/etag tx)) :location (type/uri location)}) (= resource diff --git a/modules/rest-util/test/blaze/interaction/search/util_test.clj b/modules/rest-util/test/blaze/interaction/search/util_test.clj index c577ff801..e32631af0 100644 --- a/modules/rest-util/test/blaze/interaction/search/util_test.clj +++ b/modules/rest-util/test/blaze/interaction/search/util_test.clj @@ -46,17 +46,17 @@ [:resource :fhir/type] := :fhir/Patient [:resource :id] := "0" [:search :mode] := #fhir/code "match" - [:search :extension] :? nil?) + [:search :extension] :? empty?) (let [resource (with-meta {:fhir/type :fhir/Patient :id "0"} - {::sp/match-extension [:extension-100623]})] + {::sp/match-extension [#fhir/Extension{:url "url-135131"}]})] (given (search-util/match-entry context resource) :fhir/type := :fhir.Bundle/entry :fullUrl := #fhir/uri "/Patient/0" [:resource :fhir/type] := :fhir/Patient [:resource :id] := "0" [:search :mode] := #fhir/code "match" - [:search :extension] := [:extension-100623]))) + [:search :extension] := [#fhir/Extension{:url "url-135131"}]))) (deftest include-entry-test (given-thrown (search-util/include-entry {} {:fhir/type :fhir/Patient :id "0"}) diff --git a/modules/rest-util/test/blaze/middleware/fhir/resource_test.clj b/modules/rest-util/test/blaze/middleware/fhir/resource_test.clj index c15a8f1f1..01e21141f 100644 --- a/modules/rest-util/test/blaze/middleware/fhir/resource_test.clj +++ b/modules/rest-util/test/blaze/middleware/fhir/resource_test.clj @@ -2,7 +2,6 @@ (:require [blaze.async.comp :as ac] [blaze.fhir.parsing-context] - [blaze.fhir.spec :as fhir-spec] [blaze.fhir.spec.type :as type] [blaze.fhir.test-util :refer [structure-definition-repo]] [blaze.handler.util :as handler-util] @@ -145,7 +144,7 @@ (given @((resource-body-handler "Binary") {:headers {"content-type" "application/fhir+json"} :body (string-input-stream (str "{\"data\" : \"" (apply str (repeat (* 8 1024 1024) \a)) "\", \"resourceType\" : \"Binary\"}"))}) - fhir-spec/fhir-type := :fhir/Binary))) + :fhir/type := :fhir/Binary))) (deftest xml-test (testing "possible content types" @@ -215,7 +214,7 @@ (given @((resource-body-handler "Binary") {:headers {"content-type" "application/fhir+xml"} :body (string-input-stream (str ""))}) - fhir-spec/fhir-type := :fhir/Binary))) + :fhir/type := :fhir/Binary))) (def ^:private whitespace (gen/fmap str/join (gen/vector (gen/elements [" " "\n" "\r" "\t"])))) @@ -276,8 +275,8 @@ {:headers {"content-type" content-type} :body (string-input-stream resource closed?)}) :fhir/type := :fhir/Binary - [:contentType type/value] := "text/plain" - [:data type/value] := "MTA1NjE0Cg==") + [:contentType :value] := "text/plain" + [:data :value] := "MTA1NjE0Cg==") (is (true? @closed?))))) (testing "raw binary data" @@ -288,8 +287,8 @@ {:headers {"content-type" content-type} :body (binary-input-stream raw-data closed?)}) :fhir/type := :fhir/Binary - [:contentType type/value] := content-type - [:data type/value] := (encode-binary-data raw-data)) + [:contentType :value] := content-type + [:data :value] := (encode-binary-data raw-data)) (is (true? @closed?)))) (testing "with missing content-type header" diff --git a/modules/server/build.clj b/modules/server/build.clj index d1465ac5a..796754344 100644 --- a/modules/server/build.clj +++ b/modules/server/build.clj @@ -7,4 +7,4 @@ {:basis (b/create-basis {:project "deps.edn"}) :src-dirs ["java"] :class-dir "target/classes" - :javac-opts ["-Xlint:all" "-proc:none" "--release" "17"]})) + :javac-opts ["-Xlint:all" "-proc:none" "--release" "21"]})) diff --git a/modules/spec/src/blaze/spec.clj b/modules/spec/src/blaze/spec.clj index 2135f7f61..449f99b87 100644 --- a/modules/spec/src/blaze/spec.clj +++ b/modules/spec/src/blaze/spec.clj @@ -5,6 +5,7 @@ [cognitect.anomalies :as anom] [java-time.api :as time]) (:import + [java.time.temporal Temporal] [java.util Random])) ;; The base URL of Blaze without :blaze/context-path @@ -15,7 +16,7 @@ string?) (s/def :blaze/release-date - string?) + #(instance? Temporal %)) (s/def :fhir/version #{"4.0.1" "6.0.0-ballot3"}) diff --git a/modules/terminology-service/src/blaze/terminology_service/local.clj b/modules/terminology-service/src/blaze/terminology_service/local.clj index e8bebf02d..e25f15470 100644 --- a/modules/terminology-service/src/blaze/terminology_service/local.clj +++ b/modules/terminology-service/src/blaze/terminology_service/local.clj @@ -5,7 +5,6 @@ [blaze.async.comp :as ac] [blaze.db.api :as d] [blaze.db.spec] - [blaze.fhir.spec.type :as type] [blaze.module :as m] [blaze.spec] [blaze.terminology-service :as ts] @@ -49,34 +48,33 @@ (defn- validate-params [specs {params :parameter}] (reduce - (fn [new-params {:keys [name] :as param}] - (let [name (type/value name)] - (if-let [{:keys [action] :as spec} (specs name)] - (case action - :copy - (assoc-via new-params spec name (type/value (:value param))) - - :parse-nat-long - (let [value (type/value (:value param))] - (if-not (neg? value) - (assoc-via new-params spec name value) - (reduced (ba/incorrect (format "Invalid value for parameter `%s`. Has to be a non-negative integer." name))))) - - :parse - (assoc-via new-params spec name ((:parse spec) (type/value (:value param)))) - - :parse-canonical - (assoc-via new-params spec name (:value param)) - - :copy-complex-type - (assoc-via new-params spec name (:value param)) - - :copy-resource - (assoc-via new-params spec name (:resource param)) - - (reduced (ba/unsupported (format "Unsupported parameter `%s`." name) - :http/status 400))) - new-params))) + (fn [new-params {{name :value} :name :as param}] + (if-let [{:keys [action] :as spec} (specs name)] + (case action + :copy + (assoc-via new-params spec name (:value (:value param))) + + :parse-nat-long + (let [value (:value (:value param))] + (if-not (neg? value) + (assoc-via new-params spec name value) + (reduced (ba/incorrect (format "Invalid value for parameter `%s`. Has to be a non-negative integer." name))))) + + :parse + (assoc-via new-params spec name ((:parse spec) (:value (:value param)))) + + :parse-canonical + (assoc-via new-params spec name (:value param)) + + :copy-complex-type + (assoc-via new-params spec name (:value param)) + + :copy-resource + (assoc-via new-params spec name (:resource param)) + + (reduced (ba/unsupported (format "Unsupported parameter `%s`." name) + :http/status 400))) + new-params)) {} params)) @@ -95,10 +93,9 @@ (ba/incorrect (format "Parameter `%s` differs from parameter `%s`." param-name-1 param-name-2)))) -(defn- cs-coding-clause [{:keys [code display]} origin] - (if-let [code (type/value code)] - (cond-> {:code code :origin origin} - (type/value display) (assoc :display (type/value display))) +(defn- cs-coding-clause [{{code :value} :code {display :value} :display} origin] + (if code + (cond-> {:code code :origin origin} display (assoc :display display)) (ba/incorrect "Missing required parameter `coding.code`."))) (defn- cs-validate-code-more @@ -112,8 +109,7 @@ (ba/incorrect "Missing both parameters `url` and `codeSystem`.")) coding - (let [system (-> coding :system type/value) - version (-> coding :version type/value)] + (let [{{system :value} :system {version :value} :version} coding] (if code-system (when-ok [clause (cs-coding-clause coding "coding")] (assoc params :clause clause)) @@ -128,8 +124,7 @@ codeable-concept (let [{[fist-coding :as codings] :coding} codeable-concept] (condp = (count codings) - 1 (let [system (-> fist-coding :system type/value) - version (-> fist-coding :version type/value)] + 1 (let [{{system :value} :system {version :value} :version} fist-coding] (if code-system (when-ok [clause (cs-coding-clause fist-coding "codeableConcept")] (assoc params :clause clause)) diff --git a/modules/terminology-service/src/blaze/terminology_service/local/code_system.clj b/modules/terminology-service/src/blaze/terminology_service/local/code_system.clj index 0432a8b51..97f4969fc 100644 --- a/modules/terminology-service/src/blaze/terminology_service/local/code_system.clj +++ b/modules/terminology-service/src/blaze/terminology_service/local/code_system.clj @@ -5,7 +5,6 @@ [blaze.anomaly :refer [if-ok when-ok]] [blaze.async.comp :as ac :refer [do-sync]] [blaze.db.api :as d] - [blaze.fhir.spec.type :as type] [blaze.fhir.util :as fu] [blaze.terminology-service.local.code-system :as-alias cs] [blaze.terminology-service.local.code-system.bcp-13] @@ -22,14 +21,14 @@ "Keeps code systems with complete or fragment content and the special ones." [code-systems] (filterv - (fn [{:keys [url content]}] - (or (#{"complete" "fragment"} (type/value content)) + (fn [{{url :value} :url {content :value} :content}] + (or (#{"complete" "fragment"} content) (#{"urn:ietf:bcp:13" "urn:ietf:bcp:47" "http://loinc.org" "http://snomed.info/sct" "http://unitsofmeasure.org"} - (type/value url)))) + url))) code-systems)) (defn- pull-code-systems [db] @@ -48,7 +47,7 @@ (map (fn [[url code-systems]] [url (fu/sort-by-priority code-systems)])) - (group-by (comp type/value :url) (filter-usable code-systems))))) + (group-by (comp :value :url) (filter-usable code-systems))))) (defn- assoc-graph [{concepts :concept :as code-system}] (assoc code-system :default/graph (graph/build-graph concepts))) @@ -59,8 +58,8 @@ (some (fn [{:fhir/keys [type] :as resource}] (when (identical? :fhir/CodeSystem type) - (when (= url (type/value (:url resource))) - (when (required-content (type/value (:content resource))) + (when (= url (:value (:url resource))) + (when (required-content (:value (:content resource))) (ac/completed-future (assoc-graph resource)))))) tx-resources)) ([{:keys [tx-resources] ::cs/keys [required-content] @@ -68,9 +67,9 @@ (some (fn [{:fhir/keys [type] :as resource}] (when (identical? :fhir/CodeSystem type) - (when (= url (type/value (:url resource))) - (when (= version (type/value (:version resource))) - (when (required-content (type/value (:content resource))) + (when (= url (:value (:url resource))) + (when (= version (:value (:version resource))) + (when (required-content (:value (:content resource))) (ac/completed-future (assoc-graph resource))))))) tx-resources))) @@ -106,10 +105,8 @@ (vc/parameters-from-concept concept params) #(vc/fail-parameters-from-anom % params))) -(defn- assoc-system-info [clause {:keys [url version]}] - (cond-> clause - (type/value url) (assoc :system (type/value url)) - (type/value version) (assoc :version (type/value version)))) +(defn- assoc-system-info [clause {{url :value} :url {version :value} :version}] + (cond-> clause url (assoc :system url) version (assoc :version version))) (defn validate-code "Returns a Parameters resource that contains the response of the validation diff --git a/modules/terminology-service/src/blaze/terminology_service/local/code_system/bcp_13.clj b/modules/terminology-service/src/blaze/terminology_service/local/code_system/bcp_13.clj index 1d22be232..8bdcdb5b8 100644 --- a/modules/terminology-service/src/blaze/terminology_service/local/code_system/bcp_13.clj +++ b/modules/terminology-service/src/blaze/terminology_service/local/code_system/bcp_13.clj @@ -20,9 +20,9 @@ #fhir/Meta {:tag [#fhir/Coding - {:system #fhir/uri "https://samply.github.io/blaze/fhir/CodeSystem/AccessControl" + {:system #fhir/uri-interned "https://samply.github.io/blaze/fhir/CodeSystem/AccessControl" :code #fhir/code "read-only"}]} - :url #fhir/uri "urn:ietf:bcp:13" + :url #fhir/uri-interned "urn:ietf:bcp:13" :version #fhir/string "1.0.0" :name #fhir/string "BCP-13" :title #fhir/string "BCP-13 Multipurpose Internet Mail Extensions (MIME) types" @@ -64,8 +64,8 @@ [] (keep (fn [{:keys [code]}] - (when (valid? (type/value code)) - {:system #fhir/uri "urn:ietf:bcp:13" + (when (valid? (:value code)) + {:system #fhir/uri-interned "urn:ietf:bcp:13" :code code}))) concepts)) diff --git a/modules/terminology-service/src/blaze/terminology_service/local/code_system/bcp_47.clj b/modules/terminology-service/src/blaze/terminology_service/local/code_system/bcp_47.clj index 3a9ad279a..a67ea43eb 100644 --- a/modules/terminology-service/src/blaze/terminology_service/local/code_system/bcp_47.clj +++ b/modules/terminology-service/src/blaze/terminology_service/local/code_system/bcp_47.clj @@ -26,9 +26,9 @@ #fhir/Meta {:tag [#fhir/Coding - {:system #fhir/uri "https://samply.github.io/blaze/fhir/CodeSystem/AccessControl" + {:system #fhir/uri-interned "https://samply.github.io/blaze/fhir/CodeSystem/AccessControl" :code #fhir/code "read-only"}]} - :url #fhir/uri "urn:ietf:bcp:47" + :url #fhir/uri-interned "urn:ietf:bcp:47" :version #fhir/string "1.0.0" :name #fhir/string "BCP-47" :title #fhir/string "BCP-47 Tags for Identifying Languages" @@ -75,8 +75,8 @@ [] (keep (fn [{:keys [code]}] - (when-let [display (display (type/value code))] - {:system #fhir/uri "urn:ietf:bcp:47" + (when-let [display (display (:value code))] + {:system #fhir/uri-interned "urn:ietf:bcp:47" :code code :display display}))) concepts)) diff --git a/modules/terminology-service/src/blaze/terminology_service/local/code_system/core.clj b/modules/terminology-service/src/blaze/terminology_service/local/code_system/core.clj index 491c1015d..c9e28aa9a 100644 --- a/modules/terminology-service/src/blaze/terminology_service/local/code_system/core.clj +++ b/modules/terminology-service/src/blaze/terminology_service/local/code_system/core.clj @@ -1,7 +1,5 @@ (ns blaze.terminology-service.local.code-system.core - (:refer-clojure :exclude [find]) - (:require - [blaze.fhir.spec.type :as type])) + (:refer-clojure :exclude [find])) (defmulti find {:arglists '([context url] [context url version])} @@ -17,7 +15,7 @@ (defmulti enhance {:arglists '([context code-system])} (fn [_ {:keys [url]}] - (condp = (type/value url) + (condp = (:value url) "http://loinc.org" :loinc "http://snomed.info/sct" :sct "urn:ietf:bcp:13" :bcp-13 @@ -28,7 +26,7 @@ (defmulti expand-complete {:arglists '([code-system active-only])} (fn [{:keys [url]} _] - (condp = (type/value url) + (condp = (:value url) "http://loinc.org" :loinc "http://snomed.info/sct" :sct "urn:ietf:bcp:13" :bcp-13 @@ -39,7 +37,7 @@ (defmulti expand-concept {:arglists '([code-system concepts params])} (fn [{:keys [url]} _ _] - (condp = (type/value url) + (condp = (:value url) "http://loinc.org" :loinc "http://snomed.info/sct" :sct "urn:ietf:bcp:13" :bcp-13 @@ -50,7 +48,7 @@ (defmulti expand-filter {:arglists '([code-system filter params])} (fn [{:keys [url]} _ _] - (condp = (type/value url) + (condp = (:value url) "http://loinc.org" :loinc "http://snomed.info/sct" :sct nil))) @@ -59,7 +57,7 @@ "Returns the concept according to `params` if it exists in `code-system`." {:arglists '([code-system params])} (fn [{:keys [url]} _] - (condp = (type/value url) + (condp = (:value url) "http://loinc.org" :loinc "http://snomed.info/sct" :sct "urn:ietf:bcp:13" :bcp-13 @@ -70,7 +68,7 @@ (defmulti find-filter {:arglists '([code-system filter params])} (fn [{:keys [url]} _ _] - (condp = (type/value url) + (condp = (:value url) "http://loinc.org" :loinc "http://snomed.info/sct" :sct nil))) diff --git a/modules/terminology-service/src/blaze/terminology_service/local/code_system/default.clj b/modules/terminology-service/src/blaze/terminology_service/local/code_system/default.clj index bc67c0bdb..185054dee 100644 --- a/modules/terminology-service/src/blaze/terminology_service/local/code_system/default.clj +++ b/modules/terminology-service/src/blaze/terminology_service/local/code_system/default.clj @@ -3,7 +3,6 @@ [blaze.anomaly :as ba :refer [when-ok]] [blaze.async.comp :refer [do-sync]] [blaze.db.api :as d] - [blaze.fhir.spec.type :as type] [blaze.fhir.util :as fu] [blaze.terminology-service.local.code-system :as-alias cs] [blaze.terminology-service.local.code-system.core :as c] @@ -28,7 +27,7 @@ (defn- code-system-not-required-content-msg [{:keys [url content]} required-content] (format "Can't use the code system `%s` because it's content is not one of %s. It's content is `%s`." - (type/value url) (str/join ", " required-content) (type/value content))) + (:value url) (str/join ", " required-content) (:value content))) (defn- code-system-not-required-content-anom [code-system required-content] (ba/conflict (code-system-not-required-content-msg code-system required-content))) @@ -40,7 +39,7 @@ (defn- get-graph [cache {:keys [id] {version :versionId} :meta :as code-system}] - (let [key [id (type/value version)]] + (let [key [id (:value version)]] (.get ^Cache cache key (fn [_] (graph/build-graph (:concept code-system)))))) (defmethod c/find :default @@ -49,7 +48,7 @@ url & [version]] (do-sync [code-systems (d/pull-many db (vec (code-system-query db url version)))] (if-let [{:keys [content] :as code-system} (first (fu/sort-by-priority code-systems))] - (if (required-content (type/value content)) + (if (required-content (:value content)) (assoc code-system :default/graph (get-graph graph-cache code-system)) (code-system-not-required-content-anom code-system required-content)) (ba/not-found (not-found-msg url version))))) @@ -61,17 +60,16 @@ (defn- inactive? [{properties :property}] (some (fn [{:keys [code value]}] - (condp = (type/value code) - "status" (when (= "retired" (type/value value)) true) - "inactive" (type/value value) + (condp = (:value code) + "status" (when (= "retired" (:value value)) true) + "inactive" (:value value) nil)) properties)) (defn- not-selectable? [{properties :property}] (some (fn [{:keys [code value]}] - (and (= "notSelectable" (type/value code)) - (type/value value))) + (and (= "notSelectable" (:value code)) (:value value))) properties)) (defn- definition-property [definition] @@ -89,7 +87,7 @@ (inactive? concept) (assoc :inactive #fhir/boolean true) (not-selectable? concept) (assoc :abstract #fhir/boolean true) include-designations (assoc :designation (:designation concept)) - (seq normal-properties) (assoc :property (filterv (comp (set normal-properties) type/value :code) (:property concept))) + (seq normal-properties) (assoc :property (filterv (comp (set normal-properties) :value :code) (:property concept))) (and definition (some #{"definition"} properties)) (update :property (fnil conj []) (definition-property definition))))) (defn- xf [params {:keys [url]}] @@ -114,14 +112,14 @@ (defn- concept-xf [params {:keys [url] {:keys [concepts]} :default/graph}] (keep (fn [{:keys [code display]}] - (when-let [concept (concepts (type/value code))] + (when-let [concept (concepts (:value code))] (cond-> (create-contains params url concept) display (assoc :display display)))))) (defn- concept-active-xf [params {:keys [url] {:keys [concepts]} :default/graph}] (keep (fn [{:keys [code display]}] - (when-let [concept (concepts (type/value code))] + (when-let [concept (concepts (:value code))] (when-not (inactive? concept) (cond-> (create-contains params url concept) display (assoc :display display))))))) diff --git a/modules/terminology-service/src/blaze/terminology_service/local/code_system/filter/core.clj b/modules/terminology-service/src/blaze/terminology_service/local/code_system/filter/core.clj index a62e48493..b1b4fa5ff 100644 --- a/modules/terminology-service/src/blaze/terminology_service/local/code_system/filter/core.clj +++ b/modules/terminology-service/src/blaze/terminology_service/local/code_system/filter/core.clj @@ -1,16 +1,14 @@ (ns blaze.terminology-service.local.code-system.filter.core (:require - [blaze.anomaly :as ba] - [blaze.fhir.spec.type :as type])) + [blaze.anomaly :as ba])) (defmulti filter-concepts "Returns all concepts that satisfy `filter` or an anomaly in case of errors." {:arglists '([code-system filter])} - (fn [_ {:keys [op]}] (-> op type/value keyword))) + (fn [_ {:keys [op]}] (-> op :value keyword))) (defn unsupported-filter-op-msg [{:keys [url]} {:keys [op]}] - (format "Unsupported filter operator `%s` in code system `%s`." - (type/value op) (type/value url))) + (format "Unsupported filter operator `%s` in code system `%s`." (:value op) (:value url))) (defmethod filter-concepts :default [code-system filter] @@ -20,7 +18,7 @@ "Returns the concept with `code` if it satisfies `filter` or an anomaly in case of errors." {:arglists '([code-system filter code])} - (fn [_ {:keys [op]} _] (-> op type/value keyword))) + (fn [_ {:keys [op]} _] (-> op :value keyword))) (defmethod find-concept :default [code-system filter _] diff --git a/modules/terminology-service/src/blaze/terminology_service/local/code_system/filter/descendent_of.clj b/modules/terminology-service/src/blaze/terminology_service/local/code_system/filter/descendent_of.clj index 4ef8f589a..c59d491cd 100644 --- a/modules/terminology-service/src/blaze/terminology_service/local/code_system/filter/descendent_of.clj +++ b/modules/terminology-service/src/blaze/terminology_service/local/code_system/filter/descendent_of.clj @@ -3,32 +3,31 @@ concept provided as the value (include descendant codes)." (:require [blaze.anomaly :as ba] - [blaze.fhir.spec.type :as type] [blaze.terminology-service.local.code-system.filter.core :as core] [blaze.terminology-service.local.graph :as graph])) (defn- expand-filter - [{:keys [url] :default/keys [graph]} value] + [{{url :value} :url :default/keys [graph]} value] (if (nil? value) - (ba/incorrect (format "Missing concept descendent-of filter value in code system `%s`." (type/value url))) - (graph/descendent-of graph (type/value value)))) + (ba/incorrect (format "Missing concept descendent-of filter value in code system `%s`." url)) + (graph/descendent-of graph value))) (defmethod core/filter-concepts :descendent-of - [{:keys [url] :as code-system} {:keys [property value]}] - (condp = (type/value property) - "concept" (expand-filter code-system (type/value value)) - nil (ba/incorrect (format "Missing descendent-of filter property in code system `%s`." (type/value url))) - (ba/unsupported (format "Unsupported descendent-of filter property `%s` in code system `%s`." (type/value property) (type/value url))))) + [{{url :value} :url :as code-system} {{property :value} :property {value :value} :value}] + (condp = property + "concept" (expand-filter code-system value) + nil (ba/incorrect (format "Missing descendent-of filter property in code system `%s`." url)) + (ba/unsupported (format "Unsupported descendent-of filter property `%s` in code system `%s`." property url)))) (defn- find-filter - [{:keys [url] :default/keys [graph]} value code] + [{{url :value} :url :default/keys [graph]} value code] (if (nil? value) - (ba/incorrect (format "Missing concept descendent-of filter value in code system `%s`." (type/value url))) + (ba/incorrect (format "Missing concept descendent-of filter value in code system `%s`." url)) (graph/find-descendent-of graph value code))) (defmethod core/find-concept :descendent-of - [{:keys [url] :as code-system} {:keys [property value]} code] - (condp = (type/value property) - "concept" (find-filter code-system (type/value value) code) - nil (ba/incorrect (format "Missing descendent-of filter property in code system `%s`." (type/value url))) - (ba/unsupported (format "Unsupported descendent-of filter property `%s` in code system `%s`." (type/value property) (type/value url))))) + [{{url :value} :url :as code-system} {{property :value} :property {value :value} :value} code] + (condp = property + "concept" (find-filter code-system value code) + nil (ba/incorrect (format "Missing descendent-of filter property in code system `%s`." url)) + (ba/unsupported (format "Unsupported descendent-of filter property `%s` in code system `%s`." property url)))) diff --git a/modules/terminology-service/src/blaze/terminology_service/local/code_system/filter/equals.clj b/modules/terminology-service/src/blaze/terminology_service/local/code_system/filter/equals.clj index 02c2112b3..f7dd9c968 100644 --- a/modules/terminology-service/src/blaze/terminology_service/local/code_system/filter/equals.clj +++ b/modules/terminology-service/src/blaze/terminology_service/local/code_system/filter/equals.clj @@ -1,20 +1,19 @@ (ns blaze.terminology-service.local.code-system.filter.equals (:require [blaze.anomaly :as ba :refer [when-ok]] - [blaze.fhir.spec.type :as type] [blaze.terminology-service.local.code-system.filter.core :as core])) (defn- pred [{:keys [url]} {:keys [property value]}] - (if-let [property (type/value property)] - (if-some [search-value (type/value value)] + (if-let [property (:value property)] + (if-some [search-value (:value value)] (fn [{properties :property}] (some (fn [{:keys [code value]}] - (and (= property (type/value code)) - (= search-value (type/value value)))) + (and (= property (:value code)) + (= search-value (:value value)))) properties)) - (ba/incorrect (format "Missing %s = filter value in code system `%s`." property (type/value url)))) - (ba/incorrect (format "Missing = filter property in code system `%s`." (type/value url))))) + (ba/incorrect (format "Missing %s = filter value in code system `%s`." property (:value url)))) + (ba/incorrect (format "Missing = filter property in code system `%s`." (:value url))))) (defmethod core/filter-concepts := [{{:keys [concepts]} :default/graph :as code-system} filter] diff --git a/modules/terminology-service/src/blaze/terminology_service/local/code_system/filter/exists.clj b/modules/terminology-service/src/blaze/terminology_service/local/code_system/filter/exists.clj index 7faa54cac..4d1004c74 100644 --- a/modules/terminology-service/src/blaze/terminology_service/local/code_system/filter/exists.clj +++ b/modules/terminology-service/src/blaze/terminology_service/local/code_system/filter/exists.clj @@ -3,21 +3,20 @@ When value is `false`, includes all codes that lack the specified property." (:require [blaze.anomaly :as ba :refer [when-ok]] - [blaze.fhir.spec.type :as type] [blaze.terminology-service.local.code-system.filter.core :as core])) (defn- pred "Creates either a filter or a remove xform, depending on the filter value." - [{:keys [url]} {:keys [property value]}] - (if-let [property (type/value property)] - (if-some [value (type/value value)] + [{{url :value} :url} {:keys [property value]}] + (if-let [property (:value property)] + (if-some [value (:value value)] (if-some [should-exist? (parse-boolean value)] ((if should-exist? identity complement) (fn [{properties :property}] - (some #(-> % :code type/value (= property)) properties))) - (ba/incorrect (format "Invalid %s exists filter value `%s` in code system `%s`. Should be one of `true` or `false`." property value (type/value url)))) - (ba/incorrect (format "Missing %s exists filter value in code system `%s`." property (type/value url)))) - (ba/incorrect (format "Missing exists filter property in code system `%s`." (type/value url))))) + (some #(-> % :code :value (= property)) properties))) + (ba/incorrect (format "Invalid %s exists filter value `%s` in code system `%s`. Should be one of `true` or `false`." property value url))) + (ba/incorrect (format "Missing %s exists filter value in code system `%s`." property url))) + (ba/incorrect (format "Missing exists filter property in code system `%s`." url)))) (defmethod core/filter-concepts :exists [{{:keys [concepts]} :default/graph :as code-system} filter] diff --git a/modules/terminology-service/src/blaze/terminology_service/local/code_system/filter/is_a.clj b/modules/terminology-service/src/blaze/terminology_service/local/code_system/filter/is_a.clj index b48ca5acd..533e58a10 100644 --- a/modules/terminology-service/src/blaze/terminology_service/local/code_system/filter/is_a.clj +++ b/modules/terminology-service/src/blaze/terminology_service/local/code_system/filter/is_a.clj @@ -4,32 +4,32 @@ (include descendant codes and self)." (:require [blaze.anomaly :as ba] - [blaze.fhir.spec.type :as type] [blaze.terminology-service.local.code-system.filter.core :as core] [blaze.terminology-service.local.graph :as graph])) (defn- expand-filter - [{:keys [url] :default/keys [graph]} value] + [{{url :value} :url :default/keys [graph]} value] (if (nil? value) - (ba/incorrect (format "Missing concept is-a filter value in code system `%s`." (type/value url))) + (ba/incorrect (format "Missing concept is-a filter value in code system `%s`." url)) (graph/is-a graph value))) (defmethod core/filter-concepts :is-a - [{:keys [url] :as code-system} {:keys [property value]}] - (condp = (type/value property) - "concept" (expand-filter code-system (type/value value)) - nil (ba/incorrect (format "Missing is-a filter property in code system `%s`." (type/value url))) - (ba/unsupported (format "Unsupported is-a filter property `%s` in code system `%s`." (type/value property) (type/value url))))) + [{{url :value} :url :as code-system} {{property :value} :property {value :value} :value}] + (condp = property + "concept" (expand-filter code-system value) + nil (ba/incorrect (format "Missing is-a filter property in code system `%s`." url)) + (ba/unsupported (format "Unsupported is-a filter property `%s` in code system `%s`." property url)))) (defn- find-filter - [{:keys [url] :default/keys [graph]} value code] + [{{url :value} :url :default/keys [graph]} value code] (if (nil? value) - (ba/incorrect (format "Missing concept is-a filter value in code system `%s`." (type/value url))) + (ba/incorrect (format "Missing concept is-a filter value in code system `%s`." url)) (graph/find-is-a graph value code))) (defmethod core/find-concept :is-a - [{:keys [url] :as code-system} {:keys [property value]} code] - (condp = (type/value property) - "concept" (find-filter code-system (type/value value) code) - nil (ba/incorrect (format "Missing is-a filter property in code system `%s`." (type/value url))) - (ba/unsupported (format "Unsupported is-a filter property `%s` in code system `%s`." (type/value property) (type/value url))))) + [{{url :value} :url :as code-system} + {{property :value} :property {value :value} :value} code] + (condp = property + "concept" (find-filter code-system value code) + nil (ba/incorrect (format "Missing is-a filter property in code system `%s`." url)) + (ba/unsupported (format "Unsupported is-a filter property `%s` in code system `%s`." property url)))) diff --git a/modules/terminology-service/src/blaze/terminology_service/local/code_system/filter/regex.clj b/modules/terminology-service/src/blaze/terminology_service/local/code_system/filter/regex.clj index 54a671837..b8d589f08 100644 --- a/modules/terminology-service/src/blaze/terminology_service/local/code_system/filter/regex.clj +++ b/modules/terminology-service/src/blaze/terminology_service/local/code_system/filter/regex.clj @@ -1,26 +1,25 @@ (ns blaze.terminology-service.local.code-system.filter.regex (:require [blaze.anomaly :as ba :refer [if-ok when-ok]] - [blaze.fhir.spec.type :as type] [blaze.terminology-service.local.code-system.filter.core :as core] [cognitect.anomalies :as anom])) -(defn- pred [{:keys [url]} {:keys [property value]}] - (if-let [property (type/value property)] - (if-some [pattern (type/value value)] +(defn- pred [{{url :value} :url} {:keys [property value]}] + (if-let [property (:value property)] + (if-some [pattern (:value value)] (if-ok [pattern (ba/try-all ::anom/incorrect (re-pattern pattern))] (if (= "code" property) (fn [{:keys [code]}] - (re-matches pattern (type/value code))) + (re-matches pattern (:value code))) (fn [{properties :property}] (some (fn [{:keys [code value]}] - (and (= property (type/value code)) - (re-matches pattern (type/value value)))) + (and (= property (:value code)) + (re-matches pattern (:value value)))) properties))) - #(assoc % ::anom/message (format "Invalid %s regex filter value `%s` in code system `%s`. Should be a valid regex pattern." property (type/value value) (type/value url)))) - (ba/incorrect (format "Missing %s regex filter value in code system `%s`." property (type/value url)))) - (ba/incorrect (format "Missing regex filter property in code system `%s`." (type/value url))))) + #(assoc % ::anom/message (format "Invalid %s regex filter value `%s` in code system `%s`. Should be a valid regex pattern." property (:value value) url))) + (ba/incorrect (format "Missing %s regex filter value in code system `%s`." property url))) + (ba/incorrect (format "Missing regex filter property in code system `%s`." url)))) (defmethod core/filter-concepts :regex [{{:keys [concepts]} :default/graph :as code-system} filter] diff --git a/modules/terminology-service/src/blaze/terminology_service/local/code_system/loinc.clj b/modules/terminology-service/src/blaze/terminology_service/local/code_system/loinc.clj index 47f043cbf..6b0f5fa75 100644 --- a/modules/terminology-service/src/blaze/terminology_service/local/code_system/loinc.clj +++ b/modules/terminology-service/src/blaze/terminology_service/local/code_system/loinc.clj @@ -3,7 +3,6 @@ [blaze.anomaly :as ba :refer [when-ok]] [blaze.async.comp :as ac] [blaze.db.api :as d] - [blaze.fhir.spec.type :as type] [blaze.terminology-service.local.code-system :as-alias cs] [blaze.terminology-service.local.code-system.core :as c] [blaze.terminology-service.local.code-system.loinc.context :as context :refer [url]] @@ -35,13 +34,13 @@ (defn- concept-xf [{{:keys [concept-index]} :loinc/context}] (keep (fn [{:keys [code]}] - (concept-index (type/value code))))) + (concept-index (:value code))))) (defn- active-concept-xf [{{:keys [concept-index]} :loinc/context}] (keep (fn [{:keys [code]}] - (when-let [concept (concept-index (type/value code))] - (when-not (-> concept :inactive type/value) + (when-let [concept (concept-index (:value code))] + (when-not (-> concept :inactive :value) concept))))) (defn- remove-properties [concept] @@ -62,7 +61,7 @@ (into #{} (comp - (if active-only (filter (comp not type/value :inactive)) identity) + (if active-only (filter (comp not :value :inactive)) identity) (map remove-properties)) concepts))) diff --git a/modules/terminology-service/src/blaze/terminology_service/local/code_system/loinc/context.clj b/modules/terminology-service/src/blaze/terminology_service/local/code_system/loinc/context.clj index 5c4717ecb..fc8789d77 100644 --- a/modules/terminology-service/src/blaze/terminology_service/local/code_system/loinc/context.clj +++ b/modules/terminology-service/src/blaze/terminology_service/local/code_system/loinc/context.clj @@ -25,9 +25,9 @@ #fhir/Meta {:tag [#fhir/Coding - {:system #fhir/uri "https://samply.github.io/blaze/fhir/CodeSystem/AccessControl" + {:system #fhir/uri-interned "https://samply.github.io/blaze/fhir/CodeSystem/AccessControl" :code #fhir/code "read-only"}]} - :url (type/uri url) + :url (type/uri-interned url) :version (type/string version) :name #fhir/string "LOINC" :title #fhir/string "LOINC Code System" @@ -45,52 +45,52 @@ :property [{:fhir/type :fhir.CodeSystem/property :code #fhir/code "COMPONENT" - :uri #fhir/uri "http://loinc.org/property/COMPONENT" + :uri #fhir/uri-interned "http://loinc.org/property/COMPONENT" :description #fhir/string "First major axis-component or analyte: Analyte Name, Analyte sub-class, Challenge" :type #fhir/code "Coding"} {:fhir/type :fhir.CodeSystem/property :code #fhir/code "PROPERTY" - :uri #fhir/uri "http://loinc.org/property/PROPERTY" + :uri #fhir/uri-interned "http://loinc.org/property/PROPERTY" :description #fhir/string "Second major axis-property observed: Kind of Property (also called kind of quantity)" :type #fhir/code "Coding"} {:fhir/type :fhir.CodeSystem/property :code #fhir/code "TIME_ASPCT" - :uri #fhir/uri "http://loinc.org/property/TIME_ASPCT" + :uri #fhir/uri-interned "http://loinc.org/property/TIME_ASPCT" :description #fhir/string "Third major axis-timing of the measurement: Time Aspect (Point or moment in time vs. time interval)" :type #fhir/code "Coding"} {:fhir/type :fhir.CodeSystem/property :code #fhir/code "SYSTEM" - :uri #fhir/uri "http://loinc.org/property/SYSTEM" + :uri #fhir/uri-interned "http://loinc.org/property/SYSTEM" :description #fhir/string "Fourth major axis-type of specimen or system: System (Sample) Type" :type #fhir/code "Coding"} {:fhir/type :fhir.CodeSystem/property :code #fhir/code "SCALE_TYP" - :uri #fhir/uri "http://loinc.org/property/SCALE_TYP" + :uri #fhir/uri-interned "http://loinc.org/property/SCALE_TYP" :description #fhir/string "Fifth major axis-scale of measurement: Type of Scale" :type #fhir/code "Coding"} {:fhir/type :fhir.CodeSystem/property :code #fhir/code "METHOD_TYP" - :uri #fhir/uri "http://loinc.org/property/METHOD_TYP" + :uri #fhir/uri-interned "http://loinc.org/property/METHOD_TYP" :description #fhir/string "Sixth major axis-method of measurement: Type of Method" :type #fhir/code "Coding"} {:fhir/type :fhir.CodeSystem/property :code #fhir/code "CLASS" - :uri #fhir/uri "http://loinc.org/property/CLASS" + :uri #fhir/uri-interned "http://loinc.org/property/CLASS" :description #fhir/string "An arbitrary classification of terms for grouping related observations together" :type #fhir/code "Coding"} {:fhir/type :fhir.CodeSystem/property :code #fhir/code "STATUS" - :uri #fhir/uri "http://loinc.org/property/STATUS" + :uri #fhir/uri-interned "http://loinc.org/property/STATUS" :description #fhir/string "Status of the term. Within LOINC, codes with STATUS=DEPRECATED are considered inactive. Current values: ACTIVE, TRIAL, DISCOURAGED, and DEPRECATED" :type #fhir/code "string"} {:fhir/type :fhir.CodeSystem/property :code #fhir/code "CLASSTYPE" - :uri #fhir/uri "http://loinc.org/property/CLASSTYPE" + :uri #fhir/uri-interned "http://loinc.org/property/CLASSTYPE" :description #fhir/string "1=Laboratory class; 2=Clinical class; 3=Claims attachments; 4=Surveys" :type #fhir/code "string"} {:fhir/type :fhir.CodeSystem/property :code #fhir/code "ORDER_OBS" - :uri #fhir/uri "http://loinc.org/property/ORDER_OBS" + :uri #fhir/uri-interned "http://loinc.org/property/ORDER_OBS" :description #fhir/string "Provides users with an idea of the intended use of the term by categorizing it as an order only, observation only, or both" :type #fhir/code "string"}]}) @@ -98,7 +98,7 @@ [code long-common-name component-pair property-pair time-pair system-pair scale-pair method-pair class-pair status class-type order-obs] (cond-> - {:system #fhir/uri "http://loinc.org" + {:system #fhir/uri-interned "http://loinc.org" :code (type/code code) :display (type/string long-common-name) :loinc/properties @@ -195,7 +195,7 @@ (rest (csv/read-csv reader)))))) (defn- answer-concept [list-code code display] - {:system #fhir/uri "http://loinc.org" + {:system #fhir/uri-interned "http://loinc.org" :code (type/code code) :display (type/string display) :loinc/properties diff --git a/modules/terminology-service/src/blaze/terminology_service/local/code_system/loinc/filter/core.clj b/modules/terminology-service/src/blaze/terminology_service/local/code_system/loinc/filter/core.clj index 059d0fa8c..dafa0821d 100644 --- a/modules/terminology-service/src/blaze/terminology_service/local/code_system/loinc/filter/core.clj +++ b/modules/terminology-service/src/blaze/terminology_service/local/code_system/loinc/filter/core.clj @@ -1,13 +1,12 @@ (ns blaze.terminology-service.local.code-system.loinc.filter.core (:require [blaze.anomaly :as ba] - [blaze.fhir.spec.type :as type] [blaze.terminology-service.local.code-system.filter.core :refer [unsupported-filter-op-msg]])) (defmulti expand-filter "Returns all codes that satisfy `filter` or an anomaly in case of errors." {:arglists '([code-system filter])} - (fn [_ {:keys [op]}] (-> op type/value keyword))) + (fn [_ {:keys [op]}] (-> op :value keyword))) (defmethod expand-filter :default [code-system filter] @@ -17,7 +16,7 @@ "Returns true if the concept with `code` satisfies `filter` or an anomaly in case of errors." {:arglists '([code-system filter code])} - (fn [_ {:keys [op]} _] (-> op type/value keyword))) + (fn [_ {:keys [op]} _] (-> op :value keyword))) (defmethod satisfies-filter :default [code-system filter _] diff --git a/modules/terminology-service/src/blaze/terminology_service/local/code_system/loinc/filter/equals.clj b/modules/terminology-service/src/blaze/terminology_service/local/code_system/loinc/filter/equals.clj index d4d32df8f..c834275a4 100644 --- a/modules/terminology-service/src/blaze/terminology_service/local/code_system/loinc/filter/equals.clj +++ b/modules/terminology-service/src/blaze/terminology_service/local/code_system/loinc/filter/equals.clj @@ -1,7 +1,6 @@ (ns blaze.terminology-service.local.code-system.loinc.filter.equals (:require [blaze.anomaly :as ba :refer [if-ok]] - [blaze.fhir.spec.type :as type] [blaze.terminology-service.local.code-system.loinc.context :as context :refer [url]] [blaze.terminology-service.local.code-system.loinc.filter.core :as core] [clojure.string :as str])) @@ -35,20 +34,20 @@ (fn [_] (ba/incorrect (format "Invalid ORDER_OBS = filter value `%s` in code system `%s`." value url)))))) (defmethod core/expand-filter := - [code-system {:keys [property value]}] - (condp = (type/value property) - "COMPONENT" (expand-filter code-system :component-index (type/value value)) - "PROPERTY" (expand-filter code-system :property-index (type/value value)) - "TIME_ASPCT" (expand-filter code-system :time-index (type/value value)) - "SYSTEM" (expand-filter code-system :system-index (type/value value)) - "SCALE_TYP" (expand-filter code-system :scale-index (type/value value)) - "METHOD_TYP" (expand-filter code-system :method-index (type/value value)) - "CLASS" (expand-filter code-system :class-index (type/value value)) - "STATUS" (expand-filter-status code-system (type/value value)) - "CLASSTYPE" (expand-filter-class-type code-system (type/value value)) - "ORDER_OBS" (expand-filter-order-obs code-system (type/value value)) + [code-system {{property :value} :property {:keys [value]} :value}] + (condp = property + "COMPONENT" (expand-filter code-system :component-index value) + "PROPERTY" (expand-filter code-system :property-index value) + "TIME_ASPCT" (expand-filter code-system :time-index value) + "SYSTEM" (expand-filter code-system :system-index value) + "SCALE_TYP" (expand-filter code-system :scale-index value) + "METHOD_TYP" (expand-filter code-system :method-index value) + "CLASS" (expand-filter code-system :class-index value) + "STATUS" (expand-filter-status code-system value) + "CLASSTYPE" (expand-filter-class-type code-system value) + "ORDER_OBS" (expand-filter-order-obs code-system value) nil (ba/incorrect (format "Missing = filter property in code system `%s`." url)) - (ba/unsupported (format "Unsupported = filter property `%s` in code system `%s`." (type/value property) url)))) + (ba/unsupported (format "Unsupported = filter property `%s` in code system `%s`." property url)))) (defn- satisfies-filter [key value {:loinc/keys [properties] :as concept}] (if (nil? value) @@ -95,19 +94,19 @@ concept))) (defmethod core/satisfies-filter := - [_ {:keys [property value]} concept] - (condp = (type/value property) - "COMPONENT" (satisfies-filter :component (type/value value) concept) - "PROPERTY" (satisfies-filter :property (type/value value) concept) - "TIME_ASPCT" (satisfies-filter :time (type/value value) concept) - "SYSTEM" (satisfies-filter :system (type/value value) concept) - "SCALE_TYP" (satisfies-filter :scale (type/value value) concept) - "METHOD_TYP" (satisfies-filter :method (type/value value) concept) - "CLASS" (satisfies-filter :class (type/value value) concept) - "STATUS" (satisfies-filter-status (type/value value) concept) - "CLASSTYPE" (satisfies-filter-class-type (type/value value) concept) - "ORDER_OBS" (satisfies-filter-order-obs (type/value value) concept) - "LIST" (satisfies-filter-list (type/value value) concept) - "answer-list" (satisfies-filter-answer-list (type/value value) concept) + [_ {{property :value} :property {:keys [value]} :value} concept] + (condp = property + "COMPONENT" (satisfies-filter :component value concept) + "PROPERTY" (satisfies-filter :property value concept) + "TIME_ASPCT" (satisfies-filter :time value concept) + "SYSTEM" (satisfies-filter :system value concept) + "SCALE_TYP" (satisfies-filter :scale value concept) + "METHOD_TYP" (satisfies-filter :method value concept) + "CLASS" (satisfies-filter :class value concept) + "STATUS" (satisfies-filter-status value concept) + "CLASSTYPE" (satisfies-filter-class-type value concept) + "ORDER_OBS" (satisfies-filter-order-obs value concept) + "LIST" (satisfies-filter-list value concept) + "answer-list" (satisfies-filter-answer-list value concept) nil (ba/incorrect (format "Missing = filter property in code system `%s`." url)) - (ba/unsupported (format "Unsupported = filter property `%s` in code system `%s`." (type/value property) url)))) + (ba/unsupported (format "Unsupported = filter property `%s` in code system `%s`." property url)))) diff --git a/modules/terminology-service/src/blaze/terminology_service/local/code_system/loinc/filter/regex.clj b/modules/terminology-service/src/blaze/terminology_service/local/code_system/loinc/filter/regex.clj index 8b815a2ce..94db294e2 100644 --- a/modules/terminology-service/src/blaze/terminology_service/local/code_system/loinc/filter/regex.clj +++ b/modules/terminology-service/src/blaze/terminology_service/local/code_system/loinc/filter/regex.clj @@ -2,7 +2,6 @@ (:refer-clojure :exclude [str]) (:require [blaze.anomaly :as ba] - [blaze.fhir.spec.type :as type] [blaze.terminology-service.local.code-system.loinc.context :as context :refer [url]] [blaze.terminology-service.local.code-system.loinc.filter.core :as core] [blaze.util :refer [str]])) @@ -31,19 +30,19 @@ (into [] (comp (matches name value) (mapcat index)) (keys index))))) (defmethod core/expand-filter :regex - [code-system {:keys [property value]}] - (condp = (type/value property) - "COMPONENT" (expand-filter-regex code-system :component-index (type/value value)) - "PROPERTY" (expand-filter-regex code-system :property-index (type/value value)) - "TIME_ASPCT" (expand-filter-regex code-system :time-index (type/value value)) - "SYSTEM" (expand-filter-regex code-system :system-index (type/value value)) - "SCALE_TYP" (expand-filter-regex code-system :scale-index (type/value value)) - "METHOD_TYP" (expand-filter-regex code-system :method-index (type/value value)) - "CLASS" (expand-filter-regex code-system :class-index (type/value value)) - "STATUS" (expand-filter-regex-keyword code-system :status-index (type/value value)) - "ORDER_OBS" (expand-filter-regex-keyword code-system :order-obs-index (type/value value)) + [code-system {{property :value} :property {:keys [value]} :value}] + (condp = property + "COMPONENT" (expand-filter-regex code-system :component-index value) + "PROPERTY" (expand-filter-regex code-system :property-index value) + "TIME_ASPCT" (expand-filter-regex code-system :time-index value) + "SYSTEM" (expand-filter-regex code-system :system-index value) + "SCALE_TYP" (expand-filter-regex code-system :scale-index value) + "METHOD_TYP" (expand-filter-regex code-system :method-index value) + "CLASS" (expand-filter-regex code-system :class-index value) + "STATUS" (expand-filter-regex-keyword code-system :status-index value) + "ORDER_OBS" (expand-filter-regex-keyword code-system :order-obs-index value) nil (ba/incorrect (format "Missing regex filter property in code system `%s`." url)) - (ba/unsupported (format "Unsupported regex filter property `%s` in code system `%s`." (type/value property) url)))) + (ba/unsupported (format "Unsupported regex filter property `%s` in code system `%s`." property url)))) (defn- satisfies-filter-regex [key value {:loinc/keys [properties] :as concept}] (if (nil? value) @@ -53,14 +52,14 @@ concept))) (defmethod core/satisfies-filter :regex - [_ {:keys [property value]} concept] - (condp = (type/value property) - "COMPONENT" (satisfies-filter-regex :component (type/value value) concept) - "PROPERTY" (satisfies-filter-regex :property (type/value value) concept) - "TIME_ASPCT" (satisfies-filter-regex :time (type/value value) concept) - "SCALE_TYP" (satisfies-filter-regex :scale (type/value value) concept) - "METHOD_TYP" (satisfies-filter-regex :method (type/value value) concept) - "SYSTEM" (satisfies-filter-regex :system (type/value value) concept) - "CLASS" (satisfies-filter-regex :class (type/value value) concept) + [_ {{property :value} :property {:keys [value]} :value} concept] + (condp = property + "COMPONENT" (satisfies-filter-regex :component value concept) + "PROPERTY" (satisfies-filter-regex :property value concept) + "TIME_ASPCT" (satisfies-filter-regex :time value concept) + "SCALE_TYP" (satisfies-filter-regex :scale value concept) + "METHOD_TYP" (satisfies-filter-regex :method value concept) + "SYSTEM" (satisfies-filter-regex :system value concept) + "CLASS" (satisfies-filter-regex :class value concept) nil (ba/incorrect (format "Missing regex filter property in code system `%s`." url)) - (ba/unsupported (format "Unsupported regex filter property `%s` in code system `%s`." (type/value property) url)))) + (ba/unsupported (format "Unsupported regex filter property `%s` in code system `%s`." property url)))) diff --git a/modules/terminology-service/src/blaze/terminology_service/local/code_system/sct.clj b/modules/terminology-service/src/blaze/terminology_service/local/code_system/sct.clj index f3f0585d1..239b1d5ff 100644 --- a/modules/terminology-service/src/blaze/terminology_service/local/code_system/sct.clj +++ b/modules/terminology-service/src/blaze/terminology_service/local/code_system/sct.clj @@ -33,14 +33,14 @@ (re-matches sct-u/module-only-version-pattern version) (do-sync [code-systems (cs-u/code-systems db url)] (filterv - #(str/starts-with? (type/value (:version %)) version) + #(str/starts-with? (:value (:version %)) version) code-systems)) :else (d/pull-many db (vec (code-system-query db url version))))) (defn- assoc-context [{:keys [version] :as code-system} context] - (when-ok [[module-id version] (sct-u/module-version (type/value version))] + (when-ok [[module-id version] (sct-u/module-version (:value version))] (assoc code-system :sct/context context :sct/module-id module-id :sct/version version))) @@ -94,16 +94,16 @@ (defn- fully-specified-name-designation [term] {:language #fhir/code "en" - :use #fhir/Coding{:system #fhir/uri "http://snomed.info/sct" + :use #fhir/Coding{:system #fhir/uri-interned "http://snomed.info/sct" :code #fhir/code "900000000000003001" - :display #fhir/string "Fully specified name"} + :display #fhir/string-interned "Fully specified name"} :value (type/string term)}) (defn- synonym-designation [[_id [language-code term]]] {:language (type/code language-code) - :use #fhir/Coding{:system #fhir/uri "http://snomed.info/sct" + :use #fhir/Coding{:system #fhir/uri-interned "http://snomed.info/sct" :code #fhir/code "900000000000013009" - :display #fhir/string "Synonym"} + :display #fhir/string-interned "Synonym"} :value (type/string term)}) (defn- assoc-designations [concept code-system find-synonyms code] @@ -116,7 +116,7 @@ [code-system find-synonyms code {:keys [include-version include-designations] :as params}] (cond-> - {:system #fhir/uri "http://snomed.info/sct" + {:system #fhir/uri-interned "http://snomed.info/sct" :code (type/code (str code)) :display (type/string (display code-system find-synonyms code params))} include-version (assoc :version (:version code-system)) @@ -153,7 +153,7 @@ (into [] (comp - (keep (comp parse-sctid type/value :code)) + (keep (comp parse-sctid :value :code)) ((if active-only active-concept-xf concept-xf) code-system params)) concepts)) diff --git a/modules/terminology-service/src/blaze/terminology_service/local/code_system/sct/context.clj b/modules/terminology-service/src/blaze/terminology_service/local/code_system/sct/context.clj index 8cbe198c5..8225920ea 100644 --- a/modules/terminology-service/src/blaze/terminology_service/local/code_system/sct/context.clj +++ b/modules/terminology-service/src/blaze/terminology_service/local/code_system/sct/context.clj @@ -3,6 +3,7 @@ (:require [blaze.anomaly :as ba :refer [when-ok]] [blaze.fhir.spec.type :as type] + [blaze.fhir.spec.type.system :as system] [blaze.util :refer [str]] [clojure.string :as str]) (:import @@ -443,9 +444,9 @@ #fhir/Meta {:tag [#fhir/Coding - {:system #fhir/uri "https://samply.github.io/blaze/fhir/CodeSystem/AccessControl" + {:system #fhir/uri-interned "https://samply.github.io/blaze/fhir/CodeSystem/AccessControl" :code #fhir/code "read-only"}]} - :url (type/uri url) + :url (type/uri-interned url) :version (type/string (version-url module-id version)) :title (type/string (find-fully-specified-name module-dependency-index fully-specified-name-index @@ -453,7 +454,7 @@ module-id)) :status #fhir/code "active" :experimental #fhir/boolean false - :date (type/dateTime (str (LocalDate/parse (str version) DateTimeFormatter/BASIC_ISO_DATE))) + :date (type/dateTime (system/parse-date-time (str (LocalDate/parse (str version) DateTimeFormatter/BASIC_ISO_DATE)))) :caseSensitive #fhir/boolean true :hierarchyMeaning #fhir/code "is-a" :versionNeeded #fhir/boolean false @@ -491,7 +492,7 @@ (apply f lines args))) (defn- has-core-version? [{:keys [version]}] - (str/starts-with? (type/value version) core-version-prefix)) + (str/starts-with? (:value version) core-version-prefix)) (defn- find-current-int-system [code-systems] (last (sort-by (comp :value :date) (filter has-core-version? code-systems)))) diff --git a/modules/terminology-service/src/blaze/terminology_service/local/code_system/sct/filter/core.clj b/modules/terminology-service/src/blaze/terminology_service/local/code_system/sct/filter/core.clj index 435cb4a37..db9072659 100644 --- a/modules/terminology-service/src/blaze/terminology_service/local/code_system/sct/filter/core.clj +++ b/modules/terminology-service/src/blaze/terminology_service/local/code_system/sct/filter/core.clj @@ -1,13 +1,12 @@ (ns blaze.terminology-service.local.code-system.sct.filter.core (:require [blaze.anomaly :as ba] - [blaze.fhir.spec.type :as type] [blaze.terminology-service.local.code-system.filter.core :refer [unsupported-filter-op-msg]])) (defmulti expand-filter "Returns all codes that satisfy `filter` or an anomaly in case of errors." {:arglists '([code-system filter])} - (fn [_ {:keys [op]}] (-> op type/value keyword))) + (fn [_ {:keys [op]}] (-> op :value keyword))) (defmethod expand-filter :default [code-system filter] @@ -17,7 +16,7 @@ "Returns true if the concept with `code` satisfies `filter` or an anomaly in case of errors." {:arglists '([code-system filter code])} - (fn [_ {:keys [op]} _] (-> op type/value keyword))) + (fn [_ {:keys [op]} _] (-> op :value keyword))) (defmethod satisfies-filter :default [code-system filter _] diff --git a/modules/terminology-service/src/blaze/terminology_service/local/code_system/sct/filter/descendent_of.clj b/modules/terminology-service/src/blaze/terminology_service/local/code_system/sct/filter/descendent_of.clj index 1a741d8f7..075fb3cd7 100644 --- a/modules/terminology-service/src/blaze/terminology_service/local/code_system/sct/filter/descendent_of.clj +++ b/modules/terminology-service/src/blaze/terminology_service/local/code_system/sct/filter/descendent_of.clj @@ -3,7 +3,6 @@ concept provided as the value (include descendant codes)." (:require [blaze.anomaly :as ba] - [blaze.fhir.spec.type :as type] [blaze.terminology-service.local.code-system.sct.context :as context :refer [url]] [blaze.terminology-service.local.code-system.sct.filter.core :as core] [blaze.terminology-service.local.code-system.sct.type :refer [parse-sctid]])) @@ -30,12 +29,12 @@ (defn- unsupported-property-msg [property] (format "Unsupported descendent-of filter property `%s` in code system `%s`." - (type/value property) url)) + property url)) (defmethod core/expand-filter :descendent-of - [code-system {:keys [property value]}] - (condp = (type/value property) - "concept" (expand-filter code-system (type/value value)) + [code-system {{property :value} :property {:keys [value]} :value}] + (condp = property + "concept" (expand-filter code-system value) nil (ba/incorrect missing-property-msg) (ba/unsupported (unsupported-property-msg property)))) @@ -50,8 +49,8 @@ (ba/incorrect (invalid-value-msg value))))) (defmethod core/satisfies-filter :descendent-of - [code-system {:keys [property value]} code] - (condp = (type/value property) - "concept" (satisfies-filter code-system (type/value value) code) + [code-system {{property :value} :property {:keys [value]} :value} code] + (condp = property + "concept" (satisfies-filter code-system value code) nil (ba/incorrect missing-property-msg) (ba/unsupported (unsupported-property-msg property)))) diff --git a/modules/terminology-service/src/blaze/terminology_service/local/code_system/sct/filter/equals.clj b/modules/terminology-service/src/blaze/terminology_service/local/code_system/sct/filter/equals.clj index 2360f7517..ae3b20bad 100644 --- a/modules/terminology-service/src/blaze/terminology_service/local/code_system/sct/filter/equals.clj +++ b/modules/terminology-service/src/blaze/terminology_service/local/code_system/sct/filter/equals.clj @@ -1,7 +1,6 @@ (ns blaze.terminology-service.local.code-system.sct.filter.equals (:require [blaze.anomaly :as ba] - [blaze.fhir.spec.type :as type] [blaze.terminology-service.local.code-system.sct.context :as context :refer [url]] [blaze.terminology-service.local.code-system.sct.filter.core :as core] [blaze.terminology-service.local.code-system.sct.type :refer [parse-sctid]])) @@ -23,12 +22,12 @@ (ba/incorrect (format "Invalid child = filter value `%s` in code system `%s`." value url))))) (defmethod core/expand-filter := - [code-system {:keys [property value]}] - (condp = (type/value property) - "parent" (expand-filter-parent code-system (type/value value)) - "child" (expand-filter-child code-system (type/value value)) + [code-system {{property :value} :property {:keys [value]} :value}] + (condp = property + "parent" (expand-filter-parent code-system value) + "child" (expand-filter-child code-system value) nil (ba/incorrect (format "Missing = filter property in code system `%s`." url)) - (ba/unsupported (format "Unsupported = filter property `%s` in code system `%s`." (type/value property) url)))) + (ba/unsupported (format "Unsupported = filter property `%s` in code system `%s`." property url)))) (defn- satisfies-filter-parent [{{:keys [module-dependency-index child-index]} :sct/context :sct/keys [module-id version]} value code] @@ -47,9 +46,9 @@ (ba/incorrect (format "Invalid child = filter value `%s` in code system `%s`." value url))))) (defmethod core/satisfies-filter := - [code-system {:keys [property value]} code] - (condp = (type/value property) - "parent" (satisfies-filter-parent code-system (type/value value) code) - "child" (satisfies-filter-child code-system (type/value value) code) + [code-system {{property :value} :property {:keys [value]} :value} code] + (condp = property + "parent" (satisfies-filter-parent code-system value code) + "child" (satisfies-filter-child code-system value code) nil (ba/incorrect (format "Missing = filter property in code system `%s`." url)) - (ba/unsupported (format "Unsupported = filter property `%s` in code system `%s`." (type/value property) url)))) + (ba/unsupported (format "Unsupported = filter property `%s` in code system `%s`." property url)))) diff --git a/modules/terminology-service/src/blaze/terminology_service/local/code_system/sct/filter/is_a.clj b/modules/terminology-service/src/blaze/terminology_service/local/code_system/sct/filter/is_a.clj index 468115327..bbd0d1337 100644 --- a/modules/terminology-service/src/blaze/terminology_service/local/code_system/sct/filter/is_a.clj +++ b/modules/terminology-service/src/blaze/terminology_service/local/code_system/sct/filter/is_a.clj @@ -4,7 +4,6 @@ (include descendant codes and self)." (:require [blaze.anomaly :as ba] - [blaze.fhir.spec.type :as type] [blaze.terminology-service.local.code-system.sct.context :as context :refer [url]] [blaze.terminology-service.local.code-system.sct.filter.core :as core] [blaze.terminology-service.local.code-system.sct.type :refer [parse-sctid]])) @@ -31,12 +30,12 @@ (defn- unsupported-property-msg [property] (format "Unsupported is-a filter property `%s` in code system `%s`." - (type/value property) url)) + property url)) (defmethod core/expand-filter :is-a - [code-system {:keys [property value]}] - (condp = (type/value property) - "concept" (expand-filter code-system (type/value value)) + [code-system {{property :value} :property {:keys [value]} :value}] + (condp = property + "concept" (expand-filter code-system value) nil (ba/incorrect missing-property-msg) (ba/unsupported (unsupported-property-msg property)))) @@ -52,8 +51,8 @@ (ba/incorrect (invalid-value-msg value))))) (defmethod core/satisfies-filter :is-a - [code-system {:keys [property value]} code] - (condp = (type/value property) - "concept" (satisfies-filter code-system (type/value value) code) + [code-system {{property :value} :property {:keys [value]} :value} code] + (condp = property + "concept" (satisfies-filter code-system value code) nil (ba/incorrect missing-property-msg) (ba/unsupported (unsupported-property-msg property)))) diff --git a/modules/terminology-service/src/blaze/terminology_service/local/code_system/ucum.clj b/modules/terminology-service/src/blaze/terminology_service/local/code_system/ucum.clj index 58c89edd1..94b4075bb 100644 --- a/modules/terminology-service/src/blaze/terminology_service/local/code_system/ucum.clj +++ b/modules/terminology-service/src/blaze/terminology_service/local/code_system/ucum.clj @@ -25,15 +25,15 @@ #fhir/Meta {:tag [#fhir/Coding - {:system #fhir/uri "https://samply.github.io/blaze/fhir/CodeSystem/AccessControl" + {:system #fhir/uri-interned "https://samply.github.io/blaze/fhir/CodeSystem/AccessControl" :code #fhir/code "read-only"}]} - :url #fhir/uri "http://unitsofmeasure.org" + :url #fhir/uri-interned "http://unitsofmeasure.org" :version #fhir/string "2013.10.21" :name #fhir/string "UCUM" :title #fhir/string "Unified Code for Units of Measure (UCUM)" :status #fhir/code "active" :experimental #fhir/boolean false - :date #fhir/dateTime "2013-10-21" + :date #fhir/dateTime #system/date-time "2013-10-21" :caseSensitive #fhir/boolean true :content #fhir/code "not-present"}) @@ -58,8 +58,8 @@ [] (keep (fn [{:keys [code]}] - (when (valid? (type/value code)) - {:system #fhir/uri "http://unitsofmeasure.org" + (when (valid? (:value code)) + {:system #fhir/uri-interned "http://unitsofmeasure.org" :code code}))) concepts)) diff --git a/modules/terminology-service/src/blaze/terminology_service/local/code_system/util.clj b/modules/terminology-service/src/blaze/terminology_service/local/code_system/util.clj index aa965185d..da0be9d67 100644 --- a/modules/terminology-service/src/blaze/terminology_service/local/code_system/util.clj +++ b/modules/terminology-service/src/blaze/terminology_service/local/code_system/util.clj @@ -2,7 +2,6 @@ (:require [blaze.async.comp :refer [do-sync]] [blaze.db.api :as d] - [blaze.fhir.spec.type :as type] [blaze.luid :as luid] [blaze.module :as m])) @@ -11,16 +10,16 @@ (defn code-system-versions [db url] (do-sync [code-systems (code-systems db url)] - (into #{} (map (comp type/value :version)) code-systems))) + (into #{} (map (comp :value :version)) code-systems))) (defn tx-op [{:keys [url version] :as code-system} id] [:create (assoc code-system :id id) - [["url" (type/value url)] - ["version" (type/value version)]]]) + [["url" (:value url)] + ["version" (:value version)]]]) (defn tx-ops [context existing-versions code-systems] (transduce - (remove (comp existing-versions type/value :version)) + (remove (comp existing-versions :value :version)) (fn ([{:keys [tx-ops]}] tx-ops) ([{:keys [luid-generator] :as ret} code-system] diff --git a/modules/terminology-service/src/blaze/terminology_service/local/graph.clj b/modules/terminology-service/src/blaze/terminology_service/local/graph.clj index faa898b84..0835af0b6 100644 --- a/modules/terminology-service/src/blaze/terminology_service/local/graph.clj +++ b/modules/terminology-service/src/blaze/terminology_service/local/graph.clj @@ -3,11 +3,11 @@ [blaze.fhir.spec.type :as type])) (defn- property-pred [code value] - #(and (= code (type/value (:code %))) (= value (type/value (:value %))))) + #(and (= code (:value (:code %))) (= value (:value (:value %))))) (defn- conj-property [props {:keys [code value] :as prop}] (cond-> props - (not (some (property-pred (type/value code) (type/value value)) props)) + (not (some (property-pred (:value code) (:value value)) props)) (conj prop))) (defn- merge-properties @@ -19,7 +19,7 @@ "Returns all parent codes of `concept` as strings." {:arglists '([direction concept])} [direction {properties :property}] - (keep #(when (= direction (type/value (:code %))) (type/value (:value %))) properties)) + (keep #(when (= direction (:value (:code %))) (:value (:value %))) properties)) (defn- child-property [code] {:fhir/type :fhir.CodeSystem.concept/property @@ -98,7 +98,7 @@ [concepts] (reduce (fn [graph {:keys [code] :as concept}] - (let [code (type/value code) + (let [code (:value code) child-concepts (:concept concept) concept (dissoc concept :concept) parent-codes (hierarchy-codes "parent" concept) diff --git a/modules/terminology-service/src/blaze/terminology_service/local/validate_code.clj b/modules/terminology-service/src/blaze/terminology_service/local/validate_code.clj index 12beacde2..4c9953cd1 100644 --- a/modules/terminology-service/src/blaze/terminology_service/local/validate_code.clj +++ b/modules/terminology-service/src/blaze/terminology_service/local/validate_code.clj @@ -8,30 +8,29 @@ (defn issue-anom-clause [{:keys [code system version]} issue] (let [{{:keys [text]} :details :as issue} issue] - (cond-> (ba/not-found (type/value text) :code code :issues [issue]) + (cond-> (ba/not-found (:value text) :code code :issues [issue]) system (assoc :system system) version (assoc :version version)))) (defn issue-anom-concept [{:keys [code system version display inactive]} issue] (let [{{:keys [text]} :details :as issue} issue] (cond-> (ba/not-found - (type/value text) - :code (type/value code) - :system (type/value system) + (:value text) + :code (:value code) + :system (:value system) :issues [issue]) - version (assoc :version (type/value version)) - display (assoc :display (type/value display)) - inactive (assoc :inactive (type/value inactive))))) + version (assoc :version (:value version)) + display (assoc :display (:value display)) + inactive (assoc :inactive (:value inactive))))) (defn- designation-pred ([display] (fn [{:keys [value]}] - (when (= display (type/value value)) + (when (= display (:value value)) value))) ([display languages] (fn [{:keys [value language]}] - (when (and (= display (type/value value)) - (languages (type/value language))) + (when (and (= display (:value value)) (languages (:value language))) value)))) (defn- check-display* @@ -39,7 +38,7 @@ [{{:keys [display] :as clause} :clause languages :display-languages :keys [lenient-display-validation]} {designations :designation :as concept}] - (if (= display (type/value (:display concept))) + (if (= display (:value (:display concept))) (assoc concept ::found-display (:display concept)) (let [pred (if languages (designation-pred display (set languages)) @@ -56,7 +55,7 @@ (defn- enhance-concept [supplements concept] (reduce (fn [{:keys [code] :as concept} {{:keys [concepts]} :default/graph}] - (let [concept-supplement (concepts (type/value code))] + (let [concept-supplement (concepts (:value code))] (cond-> concept concept-supplement (merge-concept concept-supplement)))) concept supplements)) @@ -88,7 +87,7 @@ {:coding [(type/coding (cond-> - {:system (type/uri (:system clause)) + {:system (type/uri-interned (:system clause)) :code (type/code (:code clause))} (:version clause) (assoc :version (type/string (:version clause))) (:display clause) (assoc :display (type/string (:display clause)))))]})))) @@ -101,7 +100,7 @@ "result" (type/boolean (or result-override false)) "message" (type/string message) "code" (some-> code type/code) - "system" (some-> system type/uri) + "system" (some-> system type/uri-interned) "version" (some-> version type/string) "display" (some-> display type/string) "inactive" (some-> inactive type/boolean) @@ -112,7 +111,7 @@ {:coding [(type/coding (cond-> - {:system (type/uri (:system clause)) + {:system (type/uri-interned (:system clause)) :code (type/code (:code clause))} (:version clause) (assoc :version (type/string (:version clause))) (:display clause) (assoc :display (type/string (:display clause)))))]})))) diff --git a/modules/terminology-service/src/blaze/terminology_service/local/value_set.clj b/modules/terminology-service/src/blaze/terminology_service/local/value_set.clj index b0c70a422..810d1914f 100644 --- a/modules/terminology-service/src/blaze/terminology_service/local/value_set.clj +++ b/modules/terminology-service/src/blaze/terminology_service/local/value_set.clj @@ -12,15 +12,15 @@ (some (fn [{:fhir/keys [type] :as resource}] (when (identical? :fhir/ValueSet type) - (when (= url (type/value (:url resource))) + (when (= url (:value (:url resource))) (ac/completed-future resource)))) tx-resources)) ([tx-resources url version] (some (fn [{:fhir/keys [type] :as resource}] (when (identical? :fhir/ValueSet type) - (when (= url (type/value (:url resource))) - (when (= version (type/value (:version resource))) + (when (= url (:value (:url resource))) + (when (= version (:value (:version resource))) (ac/completed-future resource))))) tx-resources))) @@ -53,7 +53,7 @@ (reduce (fn [param {:keys [url value]}] (condp = url - "name" (assoc param :name (type/string (type/value value))) + "name" (assoc param :name (type/string (:value value))) "value" (assoc param :value value) param)) {:fhir/type :fhir.Parameters/parameter} @@ -64,4 +64,4 @@ {:arglists '([value-set])} [{:keys [language]}] (when language - {:display-languages [(type/value language)]})) + {:display-languages [(:value language)]})) diff --git a/modules/terminology-service/src/blaze/terminology_service/local/value_set/expand.clj b/modules/terminology-service/src/blaze/terminology_service/local/value_set/expand.clj index c874239b3..3d841b78e 100644 --- a/modules/terminology-service/src/blaze/terminology_service/local/value_set/expand.clj +++ b/modules/terminology-service/src/blaze/terminology_service/local/value_set/expand.clj @@ -20,13 +20,14 @@ (defn- all-version-expansion-anom [url] (ba/unsupported (all-version-expansion-msg url))) -(defn- find-code-system [{:keys [params] :as context} {:keys [system version]}] - (condp = (type/value version) - "*" (ac/completed-future (all-version-expansion-anom (type/value system))) - nil (if-let [version (vs-u/find-version params (type/value system))] - (cs/find context (type/value system) version) - (cs/find context (type/value system))) - (cs/find context (type/value system) (type/value version)))) +(defn- find-code-system + [{:keys [params] :as context} {{system :value} :system {version :value} :version}] + (condp = version + "*" (ac/completed-future (all-version-expansion-anom system)) + nil (if-let [version (vs-u/find-version params system)] + (cs/find context system version) + (cs/find context system)) + (cs/find context system version))) (defn- expand-filters [code-system filters params] (->> (map #(cs/expand-filter code-system % params) filters) @@ -41,18 +42,16 @@ (defn- used-codesystem-parameter [url version] {:fhir/type :fhir.ValueSet.expansion/parameter :name #fhir/string "used-codesystem" - :value (type/uri (cond-> url version (str "|" version)))}) + :value (type/uri-interned (cond-> url version (str "|" version)))}) (defn- version-parameter [url version] {:fhir/type :fhir.ValueSet.expansion/parameter :name #fhir/string "version" - :value (type/uri (str url "|" version))}) + :value (type/uri-interned (str url "|" version))}) -(defn- code-system-parameters [{:keys [url version]}] - (let [url (type/value url) - version (type/value version)] - (cond-> #{(used-codesystem-parameter url version)} - version (conj (version-parameter url version))))) +(defn- code-system-parameters [{{url :value} :url {version :value} :version}] + (cond-> #{(used-codesystem-parameter url version)} + version (conj (version-parameter url version)))) (defn- include-system [{:keys [params] :as context} {concepts :concept filters :filter :as include}] @@ -70,16 +69,16 @@ (ac/then-compose (partial expand-value-set context)))) (defn- include-value-sets [context value-sets] - (let [futures (mapv #(expand-value-set-by-canonical context (type/value %)) value-sets)] + (let [futures (mapv #(expand-value-set-by-canonical context (:value %)) value-sets)] (do-sync [_ (ac/all-of futures)] (transduce (map (comp #(select-keys % [:parameter :contains]) :expansion ac/join)) (partial merge-with into) futures)))) (defn- include [context {:keys [system] value-sets :valueSet :as include}] (cond - (and (type/value system) value-sets) + (and system value-sets) (ac/completed-future (ba/incorrect "Incorrect combination of system and valueSet.")) - (type/value system) (include-system context include) + system (include-system context include) value-sets (include-value-sets context value-sets) :else (ac/completed-future (ba/incorrect "Missing system or valueSet.")))) @@ -124,7 +123,7 @@ (cond-> {:fhir/type :fhir.ValueSet.expansion/property :code (type/code property)} (#{"status" "definition"} property) - (assoc :uri (type/uri (str "http://hl7.org/fhir/concept-properties#" property))))) + (assoc :uri (type/uri-interned (str "http://hl7.org/fhir/concept-properties#" property))))) (defn- append-properties [properties] (mapv append-property properties)) @@ -135,8 +134,8 @@ (cond-> {:fhir/type :fhir.ValueSet/expansion :identifier (type/uri (str "urn:uuid:" (random-uuid))) - :timestamp (time/offset-date-time clock) - :total (clojure.core/count concepts) + :timestamp (type/dateTime (time/offset-date-time clock)) + :total (type/integer (clojure.core/count concepts)) :parameter (append-params parameters params)} (seq properties) (assoc :property (append-properties properties)) (nil? count) (assoc :contains concepts) @@ -145,8 +144,8 @@ (defn- expand-value-set** [{{:keys [include-definition] :or {include-definition false}} :params :as context} - {{:keys [inactive] includes :include excludes :exclude} :compose :as value-set}] - (let [new-context (update-in context [:params :active-only] #(or % (false? (type/value inactive)))) + {{{inactive :value} :inactive includes :include excludes :exclude} :compose :as value-set}] + (let [new-context (update-in context [:params :active-only] #(or % (false? inactive))) includes (expand-includes new-context includes) excludes (expand-includes new-context excludes)] (do-sync [_ (ac/all-of [includes excludes])] @@ -160,8 +159,8 @@ (expansion context (vec (:parameter includes)) concepts)) (not include-definition) (dissoc :compose)))))) -(defn- expand-value-set-msg [{:keys [url]}] - (if-let [url (type/value url)] +(defn- expand-value-set-msg [{{url :value} :url}] + (if url (format "Error while expanding the value set `%s`. " url) "Error while expanding the provided value set. ")) diff --git a/modules/terminology-service/src/blaze/terminology_service/local/value_set/loinc.clj b/modules/terminology-service/src/blaze/terminology_service/local/value_set/loinc.clj index e7748e63e..d6511a275 100644 --- a/modules/terminology-service/src/blaze/terminology_service/local/value_set/loinc.clj +++ b/modules/terminology-service/src/blaze/terminology_service/local/value_set/loinc.clj @@ -11,17 +11,17 @@ (def ^:const ^long value-set-prefix-length (count lc/value-set-prefix)) -(defn- build-value-set [id {:keys [title]} value-set-concepts] +(defn- build-value-set [id {{title :value} :title} value-set-concepts] {:fhir/type :fhir/ValueSet :name (type/string (str "LOINC_AnswerList_" (str/replace id "-" "_"))) - :title (type/string (format "LOINC AnswerList %s (%s)" id (type/value title))) + :title (type/string (format "LOINC AnswerList %s (%s)" id title)) :status #fhir/code "active" :copyright lc/copyright :compose {:fhir/type :fhir.ValueSet/compose :include [{:fhir/type :fhir.ValueSet.compose/include - :system #fhir/uri "http://loinc.org" + :system #fhir/uri-interned "http://loinc.org" :concept (mapv #(-> (assoc % :fhir/type :fhir.ValueSet.compose.include/concept) diff --git a/modules/terminology-service/src/blaze/terminology_service/local/value_set/util.clj b/modules/terminology-service/src/blaze/terminology_service/local/value_set/util.clj index a73dd7aff..7706712d1 100644 --- a/modules/terminology-service/src/blaze/terminology_service/local/value_set/util.clj +++ b/modules/terminology-service/src/blaze/terminology_service/local/value_set/util.clj @@ -1,10 +1,9 @@ (ns blaze.terminology-service.local.value-set.util (:require - [blaze.fhir.spec.type :as type] [clojure.string :as str])) (defn find-version [{:keys [system-versions]} system] (some - #(let [[s v] (str/split (type/value %) #"\|")] + #(let [[s v] (str/split (:value %) #"\|")] (when (= system s) v)) system-versions)) diff --git a/modules/terminology-service/src/blaze/terminology_service/local/value_set/validate_code.clj b/modules/terminology-service/src/blaze/terminology_service/local/value_set/validate_code.clj index ec90bb1af..8e0e14d47 100644 --- a/modules/terminology-service/src/blaze/terminology_service/local/value_set/validate_code.clj +++ b/modules/terminology-service/src/blaze/terminology_service/local/value_set/validate_code.clj @@ -2,7 +2,6 @@ (:require [blaze.anomaly :as ba :refer [if-ok when-ok]] [blaze.async.comp :as ac :refer [do-sync]] - [blaze.fhir.spec.type :as type] [blaze.terminology-service.local.code-system :as cs] [blaze.terminology-service.local.validate-code :as vc] [blaze.terminology-service.local.value-set :as vs] @@ -22,17 +21,17 @@ display (assoc :display display))) (defn- coding-clause [value-set coding origin] - (if-let [code (-> coding :code type/value)] - (if-let [system (-> coding :system type/value)] - (let [version (-> coding :version type/value) - display (-> coding :display type/value)] + (if-let [code (-> coding :code :value)] + (if-let [system (-> coding :system :value)] + (let [version (-> coding :version :value) + display (-> coding :display :value)] (cond-> {:code code :system system :origin origin} version (assoc :version version) display (assoc :display display))) (let [clause {:code code :origin origin} {{:keys [text]} :details :as not-in-vs} (issue/not-in-vs value-set clause)] (ba/incorrect - (type/value text) + (:value text) :code code :issues [not-in-vs (issue/missing-system clause)]))) (ba/incorrect "Missing required parameter `coding.code`."))) @@ -86,9 +85,9 @@ (cond-> (ba/not-found (format "Code `%s` not found." code) :code code) system (assoc :system system) version (assoc :version version))) - ([{code-system-version :version} clause] + ([{{code-system-version :value} :version} clause] (cond-> (anom-clause clause) - (type/value code-system-version) (assoc :version (type/value code-system-version))))) + code-system-version (assoc :version code-system-version)))) (defn- match-clause [system version clause] (cond @@ -131,7 +130,7 @@ (defn- find-concepts [code-system concepts {:keys [clause] :as params} matched-clause] (or (when-let [{:keys [code] :as concept} (cs/find-complete code-system (assoc params :clause matched-clause))] - (when (some (comp #{(type/value code)} type/value :code) concepts) + (when (some (comp #{(:value code)} :value :code) concepts) concept)) (if (:system clause) (vc/issue-anom-clause matched-clause (issue/invalid-code matched-clause)) @@ -147,9 +146,9 @@ Only the code, system and version is compared here." [context - {:keys [system version] concepts :concept filters :filter} + {{system :value} :system {version :value} :version concepts :concept filters :filter} {:keys [clause] :as params}] - (if-let [matched-clause (match-clause (type/value system) (ignore-star-version (type/value version)) clause)] + (if-let [matched-clause (match-clause system (ignore-star-version version) clause)] (if (and (seq concepts) (seq filters)) (ac/completed-future (ba/incorrect "Incorrect combination of concept and filter.")) (do-sync [code-system (find-code-system context matched-clause)] @@ -177,7 +176,7 @@ (ac/then-compose #(validate-code*** context % params)))) (defn- find-concept-in-value-sets [context [value-set & more] params] - (-> (find-concept-in-value-set context (type/value value-set) params) + (-> (find-concept-in-value-set context (:value value-set) params) (ac/handle (fn [concept e] (cond @@ -188,12 +187,12 @@ (ac/then-compose identity))) (defn- find-concept-include - [context {:keys [system] value-sets :valueSet :as include} params] + [context {{system :value} :system value-sets :valueSet :as include} params] (cond - (and (type/value system) (seq value-sets)) + (and system (seq value-sets)) (ac/completed-future (ba/incorrect "Incorrect combination of system and valueSet.")) - (type/value system) (find-concept-include-system context include params) + system (find-concept-include-system context include params) (seq value-sets) (find-concept-in-value-sets context value-sets params) :else (ac/completed-future (ba/incorrect "Missing system or valueSet.")))) @@ -212,7 +211,7 @@ (defn- state-validator [inactive {:keys [clause active-only]}] (if (or active-only (false? inactive)) (fn [concept] - (if (type/value (:inactive concept)) + (if (:value (:inactive concept)) (vc/issue-anom-concept concept (issue/inactive-code clause)) concept)) identity)) @@ -225,7 +224,7 @@ (defn- validate-code*** {:arglists '([context value-set params])} [context - {{:keys [inactive] includes :include excludes :exclude} :compose :as value-set} + {{{inactive :value} :inactive includes :include excludes :exclude} :compose :as value-set} {:keys [clause] :as params}] (if (seq includes) (-> (find-concept-includes (assoc context :value-set value-set) includes params) @@ -237,7 +236,7 @@ (if e concept (anom-clause clause))))))) - (ac/then-apply (state-validator (type/value inactive) params))) + (ac/then-apply (state-validator inactive params))) (anom-clause clause))) (defn- validate-code** @@ -248,9 +247,9 @@ (fn [e] (cond-> e (not (:terminal e)) - (as-> e (let [{{:keys [text]} :details :as issue} (issue/not-in-vs value-set clause)] + (as-> e (let [{{{text :value} :text} :details :as issue} (issue/not-in-vs value-set clause)] (cond-> (update e :issues (partial into [issue])) - (not (::message-important e)) (assoc ::anom/message (type/value text)))))))) + (not (::message-important e)) (assoc ::anom/message text))))))) (ac/then-apply (display-validator context params)) (ac/then-apply #(vc/parameters-from-concept % params)) (ac/exceptionally #(vc/fail-parameters-from-anom % params)))) @@ -258,7 +257,7 @@ (defn- supplement-xf [context] (keep #(when (= "http://hl7.org/fhir/StructureDefinition/valueset-supplement" (:url %)) - (let [[url version] (str/split (type/value (:value %)) #"\|") + (let [[url version] (str/split (-> % :value :value) #"\|") context (assoc context ::cs/required-content #{"supplement"})] (if version (cs/find context url version) diff --git a/modules/terminology-service/src/blaze/terminology_service/local/value_set/validate_code/issue.clj b/modules/terminology-service/src/blaze/terminology_service/local/value_set/validate_code/issue.clj index ae71c7a21..ae1969338 100644 --- a/modules/terminology-service/src/blaze/terminology_service/local/value_set/validate_code/issue.clj +++ b/modules/terminology-service/src/blaze/terminology_service/local/value_set/validate_code/issue.clj @@ -7,7 +7,7 @@ (defn- tx-issue-type-coding [code] (type/coding - {:system #fhir/uri "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type" + {:system #fhir/uri-interned "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type" :code (type/code code)})) (def ^:private not-found-coding @@ -37,10 +37,9 @@ (defn- code [{:keys [code system]}] (cond->> code system (str system "#"))) -(defn- value-set-canonical [{:keys [url version]}] - (when-let [url (type/value url)] - (let [version (type/value version)] - (cond-> url version (str "|" version))))) +(defn- value-set-canonical [{{url :value} :url {version :value} :version}] + (when url + (cond-> url version (str "|" version)))) (defn- value-set-msg [value-set] (if-let [c (value-set-canonical value-set)] @@ -94,7 +93,8 @@ (defn invalid-display {:arglists '([clause concept lenient-display-validation])} - [{:keys [code system origin] expected-display :display} {actual-display :display} lenient-display-validation] + [{:keys [code system origin] expected-display :display} + {{actual-display :value} :display} lenient-display-validation] {:fhir/type :fhir.OperationOutcome/issue :severity (if lenient-display-validation #fhir/code "warning" #fhir/code "error") :code #fhir/code "invalid" @@ -104,19 +104,19 @@ :text (type/string (cond-> (format "Invalid display `%s` for code `%s`." expected-display (str system "#" code)) - actual-display (str (format " A valid display is `%s`." (type/value actual-display)))))}) + actual-display (str (format " A valid display is `%s`." actual-display))))}) :expression [(type/string (cond->> "display" origin (str origin ".")))]}) (defn cannot-infer {:arglists '([code-system clause])} - [{:keys [url]} {:keys [code]}] + [{{url :value} :url} {:keys [code]}] {:fhir/type :fhir.OperationOutcome/issue :severity #fhir/code "error" :code #fhir/code "not-found" :details (type/codeable-concept {:coding [cannot-infer-coding] - :text (type/string (format "The provided code `%s` is not known to belong to the inferred code system `%s`." code (type/value url)))}) + :text (type/string (format "The provided code `%s` is not known to belong to the inferred code system `%s`." code url))}) :expression [#fhir/string "code"]}) (defn inactive-code diff --git a/modules/terminology-service/test/blaze/terminology_service/local/code_system/sct/context_test.clj b/modules/terminology-service/test/blaze/terminology_service/local/code_system/sct/context_test.clj index 4f1f0f0a1..16cd6af8c 100644 --- a/modules/terminology-service/test/blaze/terminology_service/local/code_system/sct/context_test.clj +++ b/modules/terminology-service/test/blaze/terminology_service/local/code_system/sct/context_test.clj @@ -459,7 +459,7 @@ [:code-systems 0 :title] := #fhir/string "Germany National Extension module (core metadata concept)" [:code-systems 0 :status] := #fhir/code "active" [:code-systems 0 :experimental] := #fhir/boolean false - [:code-systems 0 :date] := #fhir/dateTime "2023-11-15" + [:code-systems 0 :date] := #fhir/dateTime #system/date-time "2023-11-15" [:code-systems 0 :caseSensitive] := #fhir/boolean true [:code-systems 0 :hierarchyMeaning] := #fhir/code "is-a" [:code-systems 0 :versionNeeded] := #fhir/boolean false @@ -478,14 +478,14 @@ [:code-systems 1 :version] := #fhir/string "http://snomed.info/sct/11000274103/version/20240515" [:code-systems 1 :title] := #fhir/string "Germany National Extension module (core metadata concept)" - [:code-systems 1 :date] := #fhir/dateTime "2024-05-15" + [:code-systems 1 :date] := #fhir/dateTime #system/date-time "2024-05-15" [:code-systems 2 :version] := #fhir/string "http://snomed.info/sct/11000274103/version/20241115" [:code-systems 2 :title] := #fhir/string "Germany National Extension module (core metadata concept)" - [:code-systems 2 :date] := #fhir/dateTime "2024-11-15" + [:code-systems 2 :date] := #fhir/dateTime #system/date-time "2024-11-15" [:code-systems 3 :version] := #fhir/string "http://snomed.info/sct/900000000000207008/version/20220131" [:code-systems 3 :title] := #fhir/string "SNOMED CT core module (core metadata concept)" - [:code-systems 3 :date] := #fhir/dateTime "2022-01-31" + [:code-systems 3 :date] := #fhir/dateTime #system/date-time "2022-01-31" - [:current-int-system :date] := #fhir/dateTime "2024-10-01"))) + [:current-int-system :date] := #fhir/dateTime #system/date-time "2024-10-01"))) diff --git a/modules/terminology-service/test/blaze/terminology_service/local/value_set/validate_code/issue_test.clj b/modules/terminology-service/test/blaze/terminology_service/local/value_set/validate_code/issue_test.clj index e91d403c2..1b9bed4a5 100644 --- a/modules/terminology-service/test/blaze/terminology_service/local/value_set/validate_code/issue_test.clj +++ b/modules/terminology-service/test/blaze/terminology_service/local/value_set/validate_code/issue_test.clj @@ -1,6 +1,5 @@ (ns blaze.terminology-service.local.value-set.validate-code.issue-test (:require - [blaze.fhir.spec.type :as type] [blaze.terminology-service.local.value-set.validate-code.issue :as issue] [blaze.terminology-service.local.value-set.validate-code.issue-spec] [blaze.test-util :as tu] @@ -16,8 +15,8 @@ (fn [codings] (some (fn [coding] - (and (= "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type" (type/value (:system coding))) - (= code (type/value (:code coding))))) + (and (= "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type" (:value (:system coding))) + (= code (:value (:code coding))))) codings))) (deftest not-in-vs-test diff --git a/modules/terminology-service/test/blaze/terminology_service/local_test.clj b/modules/terminology-service/test/blaze/terminology_service/local_test.clj index 1c567f3d8..0c3f7b3b0 100644 --- a/modules/terminology-service/test/blaze/terminology_service/local_test.clj +++ b/modules/terminology-service/test/blaze/terminology_service/local_test.clj @@ -262,15 +262,15 @@ (some? (re-matches #"urn:uuid:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}" s))) (defn- sort-expansion [value-set] - (update-in value-set [:expansion :contains] (partial sort-by (comp type/value :code)))) + (update-in value-set [:expansion :contains] (partial sort-by (comp :value :code)))) (defn- parameter [name] (fn [{:keys [parameter]}] - (filterv #(= name (type/value (:name %))) parameter))) + (filterv #(= name (:value (:name %))) parameter))) (defn- concept [code] (fn [concepts] - (filterv #(= code (type/value (:code %))) concepts))) + (filterv #(= code (:value (:code %))) concepts))) (deftest code-system-test (testing "with no code system" @@ -478,7 +478,7 @@ (testing "unsupported param" (given-failed-future (code-system-validate-code ts - "date" #fhir/dateTime "2025") + "date" #fhir/dateTime #system/date-time "2025") ::anom/category := ::anom/unsupported ::anom/message := "Unsupported parameter `date`.")) @@ -1111,7 +1111,7 @@ [(parameter "result") 0 :value] := #fhir/boolean true [(parameter "code") 0 :value] := #fhir/code "441510007" [(parameter "system") 0 :value] := #fhir/uri "http://snomed.info/sct" - [(parameter "version") 0 :value type/value] := output-version + [(parameter "version") 0 :value :value] := output-version [(parameter "display") 0 :value] := #fhir/string "Blood specimen with anticoagulant")) (testing "fully specified name display" @@ -1343,7 +1343,7 @@ (testing "unsupported param" (given-failed-future (expand-value-set ts - "date" #fhir/dateTime "2025") + "date" #fhir/dateTime #system/date-time "2025") ::anom/category := ::anom/unsupported ::anom/message := "Unsupported parameter `date`.")) @@ -1702,7 +1702,7 @@ :expansion {:fhir/type :fhir.ValueSet/expansion :identifier #fhir/uri "urn:uuid:b01db38a-3ec8-4167-a279-0bb1200624a8" - :timestamp #fhir/dateTime "1970-01-01T00:00:00Z" + :timestamp #fhir/dateTime #system/date-time "1970-01-01T00:00:00Z" :contains [{:fhir/type :fhir.ValueSet.expansion/contains :system #fhir/uri "system-115910" @@ -2285,8 +2285,8 @@ (given @(expand-value-set ts "url" #fhir/uri "value-set-135750") :fhir/type := :fhir/ValueSet - [:expansion (parameter "used-codesystem") 0 :value] := #fhir/uri "system-180814" - [:expansion (parameter "used-codesystem") 1 :value] := #fhir/uri "system-115910" + [:expansion (parameter "used-codesystem") 0 :value] := #fhir/uri "system-115910" + [:expansion (parameter "used-codesystem") 1 :value] := #fhir/uri "system-180814" [:expansion :contains count] := 2 [:expansion :contains 0 :system] := #fhir/uri "system-115910" [:expansion :contains 0 :code] := #fhir/code "code-115927" @@ -2325,8 +2325,8 @@ (given @(expand-value-set ts "url" #fhir/uri "value-set-135750") :fhir/type := :fhir/ValueSet - [:expansion (parameter "used-codesystem") 0 :value] := #fhir/uri "system-180814" - [:expansion (parameter "used-codesystem") 1 :value] := #fhir/uri "system-115910" + [:expansion (parameter "used-codesystem") 0 :value] := #fhir/uri "system-115910" + [:expansion (parameter "used-codesystem") 1 :value] := #fhir/uri "system-180814" [:expansion :contains count] := 4 [:expansion :contains 0 :system] := #fhir/uri "system-115910" [:expansion :contains 0 :code] := #fhir/code "code-115927" @@ -2377,8 +2377,8 @@ (given @(expand-value-set ts "url" #fhir/uri "value-set-135750") :fhir/type := :fhir/ValueSet - [:expansion (parameter "used-codesystem") 0 :value] := #fhir/uri "system-180814" - [:expansion (parameter "used-codesystem") 1 :value] := #fhir/uri "system-115910" + [:expansion (parameter "used-codesystem") 0 :value] := #fhir/uri "system-115910" + [:expansion (parameter "used-codesystem") 1 :value] := #fhir/uri "system-180814" [:expansion :contains count] := 3 [:expansion :contains 0 :system] := #fhir/uri "system-115910" [:expansion :contains 0 :code] := #fhir/code "code-115927" @@ -2699,7 +2699,7 @@ :expansion {:fhir/type :fhir.ValueSet/expansion :identifier #fhir/uri "urn:uuid:78653bd7-1b0a-4c9c-afa7-0d5ccf8c6a71" - :timestamp #fhir/dateTime "1970-01-01T00:00:00Z" + :timestamp #fhir/dateTime #system/date-time "1970-01-01T00:00:00Z" :contains [{:fhir/type :fhir.ValueSet.expansion/contains :system #fhir/uri "system-180814" @@ -5117,8 +5117,8 @@ :meta := nil :url := #fhir/uri "value-set-135750" :compose := nil - [:expansion :timestamp] := #fhir/dateTime "1970-01-01T00:00:00Z" - [:expansion :identifier type/value] :? uuid-urn? + [:expansion :timestamp] := #fhir/dateTime #system/date-time "1970-01-01T00:00:00Z" + [:expansion :identifier :value] :? uuid-urn? [:expansion :total] := #fhir/integer 1 [:expansion :contains count] := 1 [:expansion :contains 0 :system] := #fhir/uri "system-115910" @@ -5149,8 +5149,8 @@ :meta := nil :url := #fhir/uri "value-set-135750" [:compose :include 0 :system] := #fhir/uri "system-115910" - [:expansion :timestamp] := #fhir/dateTime "1970-01-01T00:00:00Z" - [:expansion :identifier type/value] :? uuid-urn? + [:expansion :timestamp] := #fhir/dateTime #system/date-time "1970-01-01T00:00:00Z" + [:expansion :identifier :value] :? uuid-urn? [:expansion :total] := #fhir/integer 1 [:expansion :contains count] := 1 [:expansion :contains 0 :system] := #fhir/uri "system-115910" @@ -5337,7 +5337,7 @@ (testing "unsupported param" (given-failed-future (value-set-validate-code ts - "date" #fhir/dateTime "2025") + "date" #fhir/dateTime #system/date-time "2025") ::anom/category := ::anom/unsupported ::anom/message := "Unsupported parameter `date`.")) diff --git a/modules/test-util/src/blaze/test_util.clj b/modules/test-util/src/blaze/test_util.clj index 65f54bf10..c820b0c94 100644 --- a/modules/test-util/src/blaze/test_util.clj +++ b/modules/test-util/src/blaze/test_util.clj @@ -26,7 +26,7 @@ (defmacro satisfies-prop [num-tests prop] `(let [result# (tc/quick-check ~num-tests ~prop)] (if (instance? Throwable (:result result#)) - (throw (:result result#)) + (prn (:result result#)) (if (true? (:result result#)) (is :success) (is (clojure.pprint/pprint result#)))))) diff --git a/modules/util/build.clj b/modules/util/build.clj index d1465ac5a..796754344 100644 --- a/modules/util/build.clj +++ b/modules/util/build.clj @@ -7,4 +7,4 @@ {:basis (b/create-basis {:project "deps.edn"}) :src-dirs ["java"] :class-dir "target/classes" - :javac-opts ["-Xlint:all" "-proc:none" "--release" "17"]})) + :javac-opts ["-Xlint:all" "-proc:none" "--release" "21"]})) diff --git a/resources/blaze.edn b/resources/blaze.edn index d35f9df87..87875c05a 100644 --- a/resources/blaze.edn +++ b/resources/blaze.edn @@ -411,14 +411,13 @@ ;; content hashes. It speeds up access to resources which are stored in the ;; key-value store in encoded format. ;; - ;; The env var DB_RESOURCE_CACHE_SIZE can be used to specify the maximum - ;; number of resources, the cache can hold. Please note that the actual memory - ;; size used depends on resource sizes. Please choose a cache size depending - ;; on your use case while monitoring JVM heap size. + ;; The env var DB_RESOURCE_CACHE_SIZE_RATIO can be used to specify the maximum + ;; ratio of JVM heap size that is allocated to the resource cache. Please + ;; choose a ratio depending on your use case while monitoring JVM heap size. ;; :blaze.db/resource-cache {:resource-store #blaze/ref :blaze.db/resource-store - :max-size #blaze/cfg ["DB_RESOURCE_CACHE_SIZE" nat-int? 100000]} + :max-size-ratio #blaze/cfg ["DB_RESOURCE_CACHE_SIZE_RATIO" double? 0.25]} ;; ;; Search Param Registry diff --git a/src/blaze/core.clj b/src/blaze/core.clj index c15079369..66bd1fd93 100644 --- a/src/blaze/core.clj +++ b/src/blaze/core.clj @@ -8,7 +8,7 @@ (:gen-class)) (defn- max-memory [] - (quot (.maxMemory (Runtime/getRuntime)) (* 1024 1024))) + (bit-shift-right (.maxMemory (Runtime/getRuntime)) 20)) (defn- config-msg [config] (->> (sort-by key config) diff --git a/src/blaze/system.clj b/src/blaze/system.clj index 0dfc6e460..c9cd73b0b 100644 --- a/src/blaze/system.clj +++ b/src/blaze/system.clj @@ -8,6 +8,7 @@ (:require [blaze.anomaly :as ba :refer [if-ok]] [blaze.coerce-env :as ce] + [blaze.fhir.spec.type.system :as system] [blaze.log] [blaze.path :refer [dir? path]] [blaze.spec] @@ -240,7 +241,7 @@ (defmethod ig/init-key :blaze/release-date [_ release-date] - release-date) + (system/parse-date-time release-date)) (defmethod ig/init-key :blaze.db/storage [_ storage] diff --git a/test/blaze/system_test.clj b/test/blaze/system_test.clj index 9bb8ba9d1..a1417449d 100644 --- a/test/blaze/system_test.clj +++ b/test/blaze/system_test.clj @@ -3,6 +3,7 @@ [blaze.async.comp :as ac] [blaze.db.api-stub :refer [mem-node-config with-system-data]] [blaze.fhir.parsing-context] + [blaze.fhir.spec.type.system :refer [parse-date-time]] [blaze.fhir.test-util :refer [structure-definition-repo]] [blaze.fhir.writing-context] [blaze.interaction.conditional-delete-type] @@ -207,7 +208,7 @@ {:job-scheduler (ig/ref :blaze/job-scheduler)} ::rest-api/capabilities-handler {:version "0.1.0" - :release-date "2024-01-07" + :release-date (parse-date-time "2024-01-07") :structure-definition-repo structure-definition-repo :search-param-registry (ig/ref :blaze.db/search-param-registry) :terminology-service (ig/ref ::ts/local)