pysof 0.1.45__tar.gz → 0.1.47__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pysof-0.1.45 → pysof-0.1.47}/Cargo.toml +2 -2
- {pysof-0.1.45 → pysof-0.1.47}/PKG-INFO +1 -1
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhir/Cargo.toml +3 -3
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhir-macro/Cargo.toml +1 -1
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/Cargo.toml +2 -2
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/README.md +24 -14
- pysof-0.1.47/crates/fhirpath/src/aggregate_math_functions.rs +458 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/debug_trace.rs +1 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/evaluator.rs +273 -68
- pysof-0.1.47/crates/fhirpath/src/format_functions.rs +241 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/handlers.rs +3 -1
- pysof-0.1.47/crates/fhirpath/src/interval_functions.rs +453 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/lib.rs +4 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/parse_debug.rs +41 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/parser.rs +58 -0
- pysof-0.1.47/crates/fhirpath/src/repeat_all_function.rs +145 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/set_operations.rs +8 -7
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/type_inference.rs +1 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/evaluator_tests.rs +359 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/Cargo.toml +2 -2
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/Cargo.toml +3 -3
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/src/handlers.rs +2 -1
- {pysof-0.1.45 → pysof-0.1.47}/README.md +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhir/README.md +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhir/build.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhir/src/lib.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhir/src/parameters.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhir/src/r4.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhir/src/r4b.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhir/src/r5.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhir/src/r6.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhir/src/r6.rs.template +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhir/tests/integer_string_integration.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhir/tests/simple_null_test.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhir/tests/test_contained_stack.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhir-macro/README.md +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhir-macro/src/lib.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/PRECISION_LIMITATION.md +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/benches/cli_benches.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/benches/evaluator_benches.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/benches/parser_benches.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/benches/server_benches.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/examples/test_ucum.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/grammar/fhirpath.g4 +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/server-api.md +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/aggregate_function.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/bin/fhirpath-cli.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/bin/fhirpath-server.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/boolean_functions.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/boundary_functions.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/cli.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/collection_functions.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/collection_navigation.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/contains_function.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/conversion_functions.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/date_operation.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/datetime_impl.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/distinct_functions.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/error.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/extension_function.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/fhir_type_hierarchy.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/json_utils.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/long_conversion.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/models.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/not_function.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/polymorphic_access.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/reference_key_functions.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/repeat_function.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/resource_type.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/server.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/subset_functions.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/terminology_client.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/terminology_functions.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/trace_function.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/type_function.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/ucum.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/all_function_tests.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/boundary_debug_tests.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/boundary_function_integration_tests.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/comment_test.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/common/context.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/common/mod.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/common/parser.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/common/runner.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/context_trace_test.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r4/input/observation-example.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r4/input/observation-example.xml +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r4/input/patient-example.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r4/input/patient-example.xml +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r4/input/questionnaire-example.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r4/input/questionnaire-example.xml +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r4/input/valueset-example-expansion.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r4/input/valueset-example-expansion.xml +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r4/tests-fhir-r4.xml +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r5/input/appointment-examplereq.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r5/input/ccda.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r5/input/codesystem-example.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r5/input/conceptmap-example.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r5/input/explanationofbenefit-example.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r5/input/observation-example.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r5/input/observation-example.xml +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r5/input/parameters-example-types.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r5/input/patient-container-example.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r5/input/patient-example-name.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r5/input/patient-example-period.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r5/input/patient-example.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r5/input/patient-name-extensions.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r5/input/questionnaire-example.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r5/input/valueset-example-expansion.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r5/known-test-failures.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r5/tests-fhir-r5.xml +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/testSchema.xsd +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/date_operation_tests.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/datetime_boundary_tests.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/debug_datetime_boundary.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/debug_string_boundary_tests.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/define_variable_test.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/enhanced_variable_tests.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/extension_tests.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/fhir_boundary_tests.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/is_as_method_tests.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/join_function_test.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/oftype_datetime_tests.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/parse_debug_test.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/parser_tests.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/polymorphic_r4_tests.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/polymorphic_tests.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/precision_tests.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/r4_tests.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/r5_tests.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/reference_key_debug.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/test_boundary_zero.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/trace_api_test.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/trace_tests.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/tree_navigation_tests.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/truncate_tests.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/type_operation_tests.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/type_preservation_integration_test.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/type_reflection_tests.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/uri_type_test.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/uuid_type_preservation_test.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath-support/Cargo.toml +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath-support/README.md +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath-support/src/lib.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath-support/src/type_info.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/.gitignore +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/Cargo.lock +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/Makefile +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/PYPI_CHECKLIST.md +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/README.md +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/WHEEL_BUILDING.md +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/multithreading_example.py +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/python-tests/__init__.py +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/python-tests/test_content_types.py +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/python-tests/test_core_functions.py +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/python-tests/test_error_handling.py +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/python-tests/test_fhir_versions.py +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/python-tests/test_import.py +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/python-tests/test_package_metadata.py +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/python-tests/test_source_errors.py +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/scripts/build-wheels.py +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/src/lib.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/src/pysof/__init__.py +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/src/pysof/_pysof.pyi +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/src/pysof/py.typed +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/test_multithreading.py +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/tests/integration/content_types.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/tests/integration/error_handling.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/tests/integration/fhir_versions.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/tests/integration/mod.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/tests/integration.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/tests/lib_coverage_tests.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/tests/lib_tests.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/tests/multithreading_integration.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/tests/threading_test.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/uv.lock +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/serde-support/Cargo.toml +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/serde-support/src/lib.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/README.md +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/benches/parallel_processing_bench.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/src/cli.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/src/data_source.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/src/error.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/src/lib.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/src/mod.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/src/models.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/src/parquet_schema.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/src/server.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/src/streaming.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/src/traits.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/common/mod.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/debug_boolean_test.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/debug_column_ordering.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/debug_datetime_instant_constants.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/debug_datetime_type_info.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/debug_enum_conversion.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/debug_evaluation_result_conversion.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/debug_extension_function.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/debug_extension_macro_fix.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/debug_extension_simple.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/debug_fhir_resource_structure.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/debug_foreach_combinations.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/debug_inequality.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/debug_instant_constant.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/debug_instant_type_info.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/debug_raw_resource.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/debug_reference_key_types.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/extension_debug_test.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/foreach_test.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/multiselect_test.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/run_foreach_tests.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/server_tests.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/basic.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/collection.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/combinations.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/constant.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/constant_types.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/fhirpath.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/fhirpath_numbers.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/fn_boundary.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/fn_empty.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/fn_extension.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/fn_first.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/fn_join.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/fn_oftype.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/fn_reference_keys.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/foreach.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/logic.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/repeat.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/union.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/validate.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/view_resource.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/where.json +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql_on_fhir_tests.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_base64_binary_constant.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_boolean_constant_debug.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_boolean_constant_debug2.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_boolean_constant_debug3.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_boolean_validation.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_chunked_processing.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_cli_file_source.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_csv_quote_handling.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_extension_fix_verification.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_extension_value_access.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_format_parameter_body.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_header_parameter_body.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_limit_parameter_body.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_ndjson_input.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_parallel_working.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_parameter_validation.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_parquet_export.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_parquet_large_dataset.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_parquet_server_options.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_patient_reference_formats.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_query_parameter_combinations.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_run_operation_parameters.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_runner_integration.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_shortened_format_names.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_where_clause_validation.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_x_ndjson_support.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/pyproject.toml +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/src/lib.rs +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/src/pysof/__init__.py +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/src/pysof/_pysof.cpython-311-darwin.so +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/src/pysof/_pysof.pyi +0 -0
- {pysof-0.1.45 → pysof-0.1.47}/src/pysof/py.typed +0 -0
|
@@ -17,8 +17,8 @@ crate-type = ["cdylib"]
|
|
|
17
17
|
|
|
18
18
|
[dependencies]
|
|
19
19
|
pyo3 = { version = ">=0.27", features = ["extension-module", "generate-import-lib"] }
|
|
20
|
-
helios-sof = { path = "../sof", version = "0.1.
|
|
21
|
-
helios-fhir = { path = "../fhir", version = "0.1.
|
|
20
|
+
helios-sof = { path = "../sof", version = "0.1.47" }
|
|
21
|
+
helios-fhir = { path = "../fhir", version = "0.1.47" }
|
|
22
22
|
serde = { workspace = true }
|
|
23
23
|
serde_json = { workspace = true }
|
|
24
24
|
chrono = { version = "0.4", features = ["serde"] }
|
|
@@ -24,9 +24,9 @@ xml = ["helios-serde-support/xml"]
|
|
|
24
24
|
serde = { workspace = true }
|
|
25
25
|
serde_json = { workspace = true, features = ["raw_value"] }
|
|
26
26
|
clap = { version = "4.0", features = ["derive"] }
|
|
27
|
-
helios-fhir-macro = { path = "../fhir-macro", version = "0.1.
|
|
28
|
-
helios-serde-support = { path = "../serde-support", version = "0.1.
|
|
29
|
-
helios-fhirpath-support = { path = "../fhirpath-support", version = "0.1.
|
|
27
|
+
helios-fhir-macro = { path = "../fhir-macro", version = "0.1.47" }
|
|
28
|
+
helios-serde-support = { path = "../serde-support", version = "0.1.47" }
|
|
29
|
+
helios-fhirpath-support = { path = "../fhirpath-support", version = "0.1.47" }
|
|
30
30
|
time = "0.3"
|
|
31
31
|
chrono = { workspace = true }
|
|
32
32
|
# Re-add serde-with-arbitrary-precision, keep macros
|
|
@@ -20,4 +20,4 @@ proc-macro2 = "1.0"
|
|
|
20
20
|
serde = { workspace = true }
|
|
21
21
|
serde_json = { workspace = true }
|
|
22
22
|
heck = "0.5" # For case conversion
|
|
23
|
-
helios-fhirpath-support = { path = "../fhirpath-support", version = "0.1.
|
|
23
|
+
helios-fhirpath-support = { path = "../fhirpath-support", version = "0.1.47" }
|
|
@@ -18,8 +18,8 @@ R5 = ["helios-fhir/R5"]
|
|
|
18
18
|
R6 = ["helios-fhir/R6"]
|
|
19
19
|
|
|
20
20
|
[dependencies]
|
|
21
|
-
helios-fhir = { path = "../fhir", version = "0.1.
|
|
22
|
-
helios-fhirpath-support = { path = "../fhirpath-support", version = "0.1.
|
|
21
|
+
helios-fhir = { path = "../fhir", version = "0.1.47" }
|
|
22
|
+
helios-fhirpath-support = { path = "../fhirpath-support", version = "0.1.47" }
|
|
23
23
|
chumsky = "0.10"
|
|
24
24
|
roxmltree = "0.20"
|
|
25
25
|
chrono = { workspace = true } # For date/time parsing and functions
|
|
@@ -288,9 +288,9 @@ The SQL on FHIR specification leverages FHIRPath to define flattened tabular vie
|
|
|
288
288
|
* [select()](https://hl7.org/fhirpath/2025Jan/#selectprojection-expression--collection): ✅
|
|
289
289
|
* [sort()](https://hl7.org/fhirpath/2025Jan/#sortkeyselector-expression-asc--desc----collection) (STU): ✅ (Sort with optional key selector)
|
|
290
290
|
* [repeat()](https://hl7.org/fhirpath/2025Jan/#repeatprojection-expression--collection): ✅ (With cycle detection)
|
|
291
|
-
* [repeatAll()](https://hl7.org/fhirpath/2025Jan/#repeatallprojection-expression--collection) (STU):
|
|
291
|
+
* [repeatAll()](https://hl7.org/fhirpath/2025Jan/#repeatallprojection-expression--collection) (STU): ✅
|
|
292
292
|
* [ofType()](https://hl7.org/fhirpath/2025Jan/#oftypetype--type-specifier--collection): ✅ (Full namespace qualification support)
|
|
293
|
-
* [coalesce()](https://hl7.org/fhirpath/2025Jan/#coalescevalue--collection----collection) (STU):
|
|
293
|
+
* [coalesce()](https://hl7.org/fhirpath/2025Jan/#coalescevalue--collection----collection) (STU): ✅
|
|
294
294
|
* [Subsetting](https://hl7.org/fhirpath/2025Jan/#subsetting)
|
|
295
295
|
* [Indexer `[]`](https://hl7.org/fhirpath/2025Jan/#-index--integer---collection): ✅
|
|
296
296
|
* [single()](https://hl7.org/fhirpath/2025Jan/#single--collection): ✅
|
|
@@ -303,7 +303,7 @@ The SQL on FHIR specification leverages FHIRPath to define flattened tabular vie
|
|
|
303
303
|
* [exclude()](https://hl7.org/fhirpath/2025Jan/#excludeother-collection--collection): ✅
|
|
304
304
|
* [Combining](https://hl7.org/fhirpath/2025Jan/#combining)
|
|
305
305
|
* [union()](https://hl7.org/fhirpath/2025Jan/#unionother--collection): ✅
|
|
306
|
-
* [combine()](https://hl7.org/fhirpath/2025Jan/#combineother--collection--collection):
|
|
306
|
+
* [combine()](https://hl7.org/fhirpath/2025Jan/#combineother--collection--collection): ✅ (Including optional `preserveOrder` parameter)
|
|
307
307
|
* [Conversion](https://hl7.org/fhirpath/2025Jan/#conversion)
|
|
308
308
|
* [Implicit Conversions](https://hl7.org/fhirpath/2025Jan/#conversion): ✅ (Integer/Decimal)
|
|
309
309
|
* [iif()](https://hl7.org/fhirpath/2025Jan/#iifcriterion-expression-true-result-collection--otherwise-result-collection--collection): ✅
|
|
@@ -313,18 +313,20 @@ The SQL on FHIR specification leverages FHIRPath to define flattened tabular vie
|
|
|
313
313
|
* [convertsToInteger()](https://hl7.org/fhirpath/2025Jan/#convertstointeger--boolean): ✅
|
|
314
314
|
* [toLong()](https://hl7.org/fhirpath/2025Jan/#tolong--long) (STU): ✅
|
|
315
315
|
* [convertsToLong()](https://hl7.org/fhirpath/2025Jan/#convertstolong--boolean) (STU): ✅
|
|
316
|
-
* [toDate()](https://hl7.org/fhirpath/2025Jan/#todate--date): ✅
|
|
316
|
+
* [toDate()](https://hl7.org/fhirpath/2025Jan/#todate--date): ✅ (Including optional `format` parameter)
|
|
317
317
|
* [convertsToDate()](https://hl7.org/fhirpath/2025Jan/#convertstodate--boolean): ✅
|
|
318
|
-
* [toDateTime()](https://hl7.org/fhirpath/2025Jan/#todatetime--datetime): ✅
|
|
318
|
+
* [toDateTime()](https://hl7.org/fhirpath/2025Jan/#todatetime--datetime): ✅ (Including optional `format` parameter)
|
|
319
319
|
* [convertsToDateTime()](https://hl7.org/fhirpath/2025Jan/#convertstodatetime--boolean): ✅
|
|
320
320
|
* [toDecimal()](https://hl7.org/fhirpath/2025Jan/#todecimal--decimal): ✅
|
|
321
321
|
* [convertsToDecimal()](https://hl7.org/fhirpath/2025Jan/#convertstodecimal--boolean): ✅
|
|
322
322
|
* [toQuantity()](https://hl7.org/fhirpath/2025Jan/#toquantityunit--string--quantity): 🟡 (Basic types, no unit conversion)
|
|
323
323
|
* [convertsToQuantity()](https://hl7.org/fhirpath/2025Jan/#convertstoquantityunit--string--boolean): 🟡 (Basic types, no unit conversion)
|
|
324
|
-
* [toString()](https://hl7.org/fhirpath/2025Jan/#tostring--string): ✅
|
|
324
|
+
* [toString()](https://hl7.org/fhirpath/2025Jan/#tostring--string): ✅ (Including optional `format` parameter)
|
|
325
325
|
* [convertsToString()](https://hl7.org/fhirpath/2025Jan/#convertstostring--string): ✅
|
|
326
326
|
* [toTime()](https://hl7.org/fhirpath/2025Jan/#totime--time): ✅
|
|
327
327
|
* [convertsToTime()](https://hl7.org/fhirpath/2025Jan/#convertstotime--boolean): ✅
|
|
328
|
+
* [Date Conversion Functions](https://hl7.org/fhirpath/2025Jan/#date-conversion-functions) (STU)
|
|
329
|
+
* Date/DateTime/Time string format codes (`yyyy`, `MM`, `dd`, etc.): ✅
|
|
328
330
|
* [String Manipulation](https://hl7.org/fhirpath/2025Jan/#string-manipulation)
|
|
329
331
|
* [indexOf()](https://hl7.org/fhirpath/2025Jan/#indexofsubstring--string--integer): ✅
|
|
330
332
|
* [lastIndexOf()](https://hl7.org/fhirpath/2025Jan/#lastindexofsubstring--string--integer) (STU): ✅
|
|
@@ -335,9 +337,9 @@ The SQL on FHIR specification leverages FHIRPath to define flattened tabular vie
|
|
|
335
337
|
* [upper()](https://hl7.org/fhirpath/2025Jan/#upper--string): ✅
|
|
336
338
|
* [lower()](https://hl7.org/fhirpath/2025Jan/#lower--string): ✅
|
|
337
339
|
* [replace()](https://hl7.org/fhirpath/2025Jan/#replacepattern--string-substitution--string--string): ✅
|
|
338
|
-
* [matches()](https://hl7.org/fhirpath/2025Jan/#matchesregex--string--boolean):
|
|
339
|
-
* [matchesFull()](https://hl7.org/fhirpath/2025Jan/#matchesfullregex--string--boolean) (STU):
|
|
340
|
-
* [replaceMatches()](https://hl7.org/fhirpath/2025Jan/#replacematchesregex--string-substitution-string--string):
|
|
340
|
+
* [matches()](https://hl7.org/fhirpath/2025Jan/#matchesregex--string--boolean): ✅ (Including optional `flags` parameter: `s`, `m`, `i`, `x`)
|
|
341
|
+
* [matchesFull()](https://hl7.org/fhirpath/2025Jan/#matchesfullregex--string--boolean) (STU): ✅ (Including optional `flags` parameter)
|
|
342
|
+
* [replaceMatches()](https://hl7.org/fhirpath/2025Jan/#replacematchesregex--string-substitution-string--string): ✅ (Including optional `flags` parameter)
|
|
341
343
|
* [length()](https://hl7.org/fhirpath/2025Jan/#length--integer): ✅
|
|
342
344
|
* [toChars()](https://hl7.org/fhirpath/2025Jan/#tochars--collection): ✅
|
|
343
345
|
* [encode()](https://hl7.org/fhirpath/2025Jan/#encodeformat--string--string): ✅
|
|
@@ -373,7 +375,10 @@ The SQL on FHIR specification leverages FHIRPath to define flattened tabular vie
|
|
|
373
375
|
* [highBoundary()](https://hl7.org/fhirpath/2025Jan/#highboundaryprecision-integer-decimal--date--datetime--time) (STU): ✅ (Full support for Decimal, Date, DateTime, and Time)
|
|
374
376
|
* [precision()](https://hl7.org/fhirpath/2025Jan/#precision--integer) (STU): ✅ (See [limitation for decimal trailing zeros](PRECISION_LIMITATION.md))
|
|
375
377
|
* [Date/DateTime/Time Component Extraction](https://hl7.org/fhirpath/2025Jan/#extract-datedatetimetime-components) (STU): ✅ (All component functions implemented: yearOf, monthOf, dayOf, hourOf, minuteOf, secondOf, millisecondOf)
|
|
376
|
-
|
|
378
|
+
* [Date and Time Interval Functions](https://hl7.org/fhirpath/2025Jan/#date-and-time-interval-functions) (STU)
|
|
379
|
+
* [duration()](https://hl7.org/fhirpath/2025Jan/#durationvalue-date--datetime--time-precision-identifier-integer): ✅
|
|
380
|
+
* [difference()](https://hl7.org/fhirpath/2025Jan/#differencevalue-date--datetime--time-precision-identifier-integer): ✅
|
|
381
|
+
|
|
377
382
|
### [Operations](https://hl7.org/fhirpath/2025Jan/#operations)
|
|
378
383
|
|
|
379
384
|
* [Equality](https://hl7.org/fhirpath/2025Jan/#equality)
|
|
@@ -409,15 +414,20 @@ The SQL on FHIR specification leverages FHIRPath to define flattened tabular vie
|
|
|
409
414
|
* [`mod` (Modulo)](https://hl7.org/fhirpath/2025Jan/#mod): ✅ (Numeric)
|
|
410
415
|
* [`&` (String Concatenation)](https://hl7.org/fhirpath/2025Jan/#-string-concatenation): ✅
|
|
411
416
|
* [Date/Time Arithmetic](https://hl7.org/fhirpath/2025Jan/#datetime-arithmetic): ✅ (Full arithmetic support with timezone and precision handling)
|
|
417
|
+
* [Unary Operators (`+` and `-`)](https://hl7.org/fhirpath/2025Jan/#unary-operators--and--): ✅
|
|
412
418
|
* [Operator Precedence](https://hl7.org/fhirpath/2025Jan/#operator-precedence): ✅
|
|
413
419
|
|
|
420
|
+
### [Instance Selector](https://hl7.org/fhirpath/2025Jan/#instance-selector) (STU)
|
|
421
|
+
|
|
422
|
+
* Object creation syntax (`typename { element : value, ... }`): ✅
|
|
423
|
+
|
|
414
424
|
### [Aggregates](https://hl7.org/fhirpath/2025Jan/#aggregates)
|
|
415
425
|
|
|
416
426
|
* [aggregate()](https://hl7.org/fhirpath/2025Jan/#aggregateaggregator--expression--init--value--value) (STU): ✅ (Full accumulator support)
|
|
417
|
-
* [sum()](https://hl7.org/fhirpath/2025Jan/#sum--integer--long--decimal--quantity) (STU):
|
|
418
|
-
* [min()](https://hl7.org/fhirpath/2025Jan/#min--integer--long--decimal--quantity--date--datetime--time--string) (STU):
|
|
419
|
-
* [max()](https://hl7.org/fhirpath/2025Jan/#max--integer--long--decimal--quantity--date--datetime--time--string) (STU):
|
|
420
|
-
* [avg()](https://hl7.org/fhirpath/2025Jan/#avg--decimal--quantity) (STU):
|
|
427
|
+
* [sum()](https://hl7.org/fhirpath/2025Jan/#sum--integer--long--decimal--quantity) (STU): ✅
|
|
428
|
+
* [min()](https://hl7.org/fhirpath/2025Jan/#min--integer--long--decimal--quantity--date--datetime--time--string) (STU): ✅
|
|
429
|
+
* [max()](https://hl7.org/fhirpath/2025Jan/#max--integer--long--decimal--quantity--date--datetime--time--string) (STU): ✅
|
|
430
|
+
* [avg()](https://hl7.org/fhirpath/2025Jan/#avg--decimal--quantity) (STU): ✅
|
|
421
431
|
|
|
422
432
|
### [Lexical Elements](https://hl7.org/fhirpath/2025Jan/#lexical-elements)
|
|
423
433
|
|
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
//! # FHIRPath Aggregate Math Functions
|
|
2
|
+
//!
|
|
3
|
+
//! Implements the aggregate math functions: `sum()`, `min()`, `max()`, `avg()`.
|
|
4
|
+
|
|
5
|
+
use helios_fhirpath_support::{EvaluationError, EvaluationResult};
|
|
6
|
+
use rust_decimal::Decimal;
|
|
7
|
+
use std::cmp::Ordering;
|
|
8
|
+
|
|
9
|
+
/// Extracts items from an EvaluationResult into a Vec.
|
|
10
|
+
fn extract_items(input: &EvaluationResult) -> Vec<&EvaluationResult> {
|
|
11
|
+
match input {
|
|
12
|
+
EvaluationResult::Collection { items, .. } => items.iter().collect(),
|
|
13
|
+
EvaluationResult::Empty => vec![],
|
|
14
|
+
single_item => vec![single_item],
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/// Compares two EvaluationResults of compatible types.
|
|
19
|
+
/// Returns None if the types are incomparable.
|
|
20
|
+
fn compare_values(a: &EvaluationResult, b: &EvaluationResult) -> Option<Ordering> {
|
|
21
|
+
match (a, b) {
|
|
22
|
+
(EvaluationResult::Integer(a, _), EvaluationResult::Integer(b, _)) => Some(a.cmp(b)),
|
|
23
|
+
(EvaluationResult::Integer64(a, _), EvaluationResult::Integer64(b, _)) => Some(a.cmp(b)),
|
|
24
|
+
(EvaluationResult::Decimal(a, _), EvaluationResult::Decimal(b, _)) => Some(a.cmp(b)),
|
|
25
|
+
// Mixed numeric: promote to Decimal
|
|
26
|
+
(EvaluationResult::Integer(a, _), EvaluationResult::Decimal(b, _)) => {
|
|
27
|
+
Some(Decimal::from(*a).cmp(b))
|
|
28
|
+
}
|
|
29
|
+
(EvaluationResult::Decimal(a, _), EvaluationResult::Integer(b, _)) => {
|
|
30
|
+
Some(a.cmp(&Decimal::from(*b)))
|
|
31
|
+
}
|
|
32
|
+
(EvaluationResult::Integer(a, _), EvaluationResult::Integer64(b, _)) => Some(a.cmp(b)),
|
|
33
|
+
(EvaluationResult::Integer64(a, _), EvaluationResult::Integer(b, _)) => Some(a.cmp(b)),
|
|
34
|
+
// Quantity comparison (same unit only)
|
|
35
|
+
(
|
|
36
|
+
EvaluationResult::Quantity(val_a, unit_a, _),
|
|
37
|
+
EvaluationResult::Quantity(val_b, unit_b, _),
|
|
38
|
+
) => {
|
|
39
|
+
if unit_a == unit_b {
|
|
40
|
+
Some(val_a.cmp(val_b))
|
|
41
|
+
} else {
|
|
42
|
+
None
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
// String comparison
|
|
46
|
+
(EvaluationResult::String(a, _), EvaluationResult::String(b, _)) => Some(a.cmp(b)),
|
|
47
|
+
// Date/Time comparisons
|
|
48
|
+
(EvaluationResult::Date(a, _), EvaluationResult::Date(b, _)) => Some(a.cmp(b)),
|
|
49
|
+
(EvaluationResult::DateTime(a, _), EvaluationResult::DateTime(b, _)) => Some(a.cmp(b)),
|
|
50
|
+
(EvaluationResult::Time(a, _), EvaluationResult::Time(b, _)) => Some(a.cmp(b)),
|
|
51
|
+
_ => None,
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/// Implements the FHIRPath `sum()` function.
|
|
56
|
+
///
|
|
57
|
+
/// Returns the sum of all items in the collection.
|
|
58
|
+
/// If the collection is empty, returns 0 (Integer).
|
|
59
|
+
/// Items must be Integer, Decimal, or Quantity (same unit).
|
|
60
|
+
pub fn sum_function(
|
|
61
|
+
invocation_base: &EvaluationResult,
|
|
62
|
+
) -> Result<EvaluationResult, EvaluationError> {
|
|
63
|
+
let items = extract_items(invocation_base);
|
|
64
|
+
|
|
65
|
+
if items.is_empty() {
|
|
66
|
+
return Ok(EvaluationResult::integer(0));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
let mut acc = items[0].clone();
|
|
70
|
+
|
|
71
|
+
for item in &items[1..] {
|
|
72
|
+
acc = add_values(&acc, item)?;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
Ok(acc)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/// Implements the FHIRPath `min()` function.
|
|
79
|
+
///
|
|
80
|
+
/// Returns the minimum value in the collection.
|
|
81
|
+
/// If the collection is empty, returns Empty.
|
|
82
|
+
pub fn min_function(
|
|
83
|
+
invocation_base: &EvaluationResult,
|
|
84
|
+
) -> Result<EvaluationResult, EvaluationError> {
|
|
85
|
+
let items = extract_items(invocation_base);
|
|
86
|
+
|
|
87
|
+
if items.is_empty() {
|
|
88
|
+
return Ok(EvaluationResult::Empty);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
let mut min_val = items[0];
|
|
92
|
+
|
|
93
|
+
for item in &items[1..] {
|
|
94
|
+
match compare_values(min_val, item) {
|
|
95
|
+
Some(Ordering::Greater) => min_val = item,
|
|
96
|
+
Some(_) => {}
|
|
97
|
+
None => {
|
|
98
|
+
return Err(EvaluationError::TypeError(format!(
|
|
99
|
+
"min() cannot compare {} and {}",
|
|
100
|
+
min_val.type_name(),
|
|
101
|
+
item.type_name()
|
|
102
|
+
)));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
Ok(min_val.clone())
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/// Implements the FHIRPath `max()` function.
|
|
111
|
+
///
|
|
112
|
+
/// Returns the maximum value in the collection.
|
|
113
|
+
/// If the collection is empty, returns Empty.
|
|
114
|
+
pub fn max_function(
|
|
115
|
+
invocation_base: &EvaluationResult,
|
|
116
|
+
) -> Result<EvaluationResult, EvaluationError> {
|
|
117
|
+
let items = extract_items(invocation_base);
|
|
118
|
+
|
|
119
|
+
if items.is_empty() {
|
|
120
|
+
return Ok(EvaluationResult::Empty);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
let mut max_val = items[0];
|
|
124
|
+
|
|
125
|
+
for item in &items[1..] {
|
|
126
|
+
match compare_values(max_val, item) {
|
|
127
|
+
Some(Ordering::Less) => max_val = item,
|
|
128
|
+
Some(_) => {}
|
|
129
|
+
None => {
|
|
130
|
+
return Err(EvaluationError::TypeError(format!(
|
|
131
|
+
"max() cannot compare {} and {}",
|
|
132
|
+
max_val.type_name(),
|
|
133
|
+
item.type_name()
|
|
134
|
+
)));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
Ok(max_val.clone())
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/// Implements the FHIRPath `avg()` function.
|
|
143
|
+
///
|
|
144
|
+
/// Returns the average of all items in the collection.
|
|
145
|
+
/// If the collection is empty, returns Empty.
|
|
146
|
+
/// Always returns Decimal (or Quantity).
|
|
147
|
+
pub fn avg_function(
|
|
148
|
+
invocation_base: &EvaluationResult,
|
|
149
|
+
) -> Result<EvaluationResult, EvaluationError> {
|
|
150
|
+
let items = extract_items(invocation_base);
|
|
151
|
+
|
|
152
|
+
if items.is_empty() {
|
|
153
|
+
return Ok(EvaluationResult::Empty);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
let count = Decimal::from(items.len() as i64);
|
|
157
|
+
let sum = sum_function(invocation_base)?;
|
|
158
|
+
|
|
159
|
+
match sum {
|
|
160
|
+
EvaluationResult::Integer(v, _) => Ok(EvaluationResult::decimal(Decimal::from(v) / count)),
|
|
161
|
+
EvaluationResult::Integer64(v, _) => {
|
|
162
|
+
Ok(EvaluationResult::decimal(Decimal::from(v) / count))
|
|
163
|
+
}
|
|
164
|
+
EvaluationResult::Decimal(v, _) => Ok(EvaluationResult::decimal(v / count)),
|
|
165
|
+
EvaluationResult::Quantity(v, unit, _) => Ok(EvaluationResult::quantity(v / count, unit)),
|
|
166
|
+
_ => Err(EvaluationError::TypeError(
|
|
167
|
+
"avg() requires numeric or quantity items".to_string(),
|
|
168
|
+
)),
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/// Adds two numeric or quantity values together.
|
|
173
|
+
fn add_values(
|
|
174
|
+
a: &EvaluationResult,
|
|
175
|
+
b: &EvaluationResult,
|
|
176
|
+
) -> Result<EvaluationResult, EvaluationError> {
|
|
177
|
+
match (a, b) {
|
|
178
|
+
(EvaluationResult::Integer(a, _), EvaluationResult::Integer(b, _)) => {
|
|
179
|
+
Ok(EvaluationResult::integer(a + b))
|
|
180
|
+
}
|
|
181
|
+
(EvaluationResult::Integer64(a, _), EvaluationResult::Integer64(b, _)) => {
|
|
182
|
+
Ok(EvaluationResult::integer64(*a + *b))
|
|
183
|
+
}
|
|
184
|
+
(EvaluationResult::Decimal(a, _), EvaluationResult::Decimal(b, _)) => {
|
|
185
|
+
Ok(EvaluationResult::decimal(*a + *b))
|
|
186
|
+
}
|
|
187
|
+
// Mixed numeric: promote to Decimal
|
|
188
|
+
(EvaluationResult::Integer(a, _), EvaluationResult::Decimal(b, _)) => {
|
|
189
|
+
Ok(EvaluationResult::decimal(Decimal::from(*a) + *b))
|
|
190
|
+
}
|
|
191
|
+
(EvaluationResult::Decimal(a, _), EvaluationResult::Integer(b, _)) => {
|
|
192
|
+
Ok(EvaluationResult::decimal(*a + Decimal::from(*b)))
|
|
193
|
+
}
|
|
194
|
+
(EvaluationResult::Integer(a, _), EvaluationResult::Integer64(b, _)) => {
|
|
195
|
+
Ok(EvaluationResult::integer64(*a + *b))
|
|
196
|
+
}
|
|
197
|
+
(EvaluationResult::Integer64(a, _), EvaluationResult::Integer(b, _)) => {
|
|
198
|
+
Ok(EvaluationResult::integer64(*a + *b))
|
|
199
|
+
}
|
|
200
|
+
// Quantity addition (same unit)
|
|
201
|
+
(
|
|
202
|
+
EvaluationResult::Quantity(val_a, unit_a, _),
|
|
203
|
+
EvaluationResult::Quantity(val_b, unit_b, _),
|
|
204
|
+
) => {
|
|
205
|
+
if unit_a == unit_b {
|
|
206
|
+
Ok(EvaluationResult::quantity(*val_a + *val_b, unit_a.clone()))
|
|
207
|
+
} else {
|
|
208
|
+
Err(EvaluationError::TypeError(format!(
|
|
209
|
+
"sum() cannot add quantities with different units: '{}' and '{}'",
|
|
210
|
+
unit_a, unit_b
|
|
211
|
+
)))
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
_ => Err(EvaluationError::TypeError(format!(
|
|
215
|
+
"sum() requires numeric or quantity items, found {} and {}",
|
|
216
|
+
a.type_name(),
|
|
217
|
+
b.type_name()
|
|
218
|
+
))),
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
#[cfg(test)]
|
|
223
|
+
mod tests {
|
|
224
|
+
use super::*;
|
|
225
|
+
use rust_decimal_macros::dec;
|
|
226
|
+
|
|
227
|
+
#[test]
|
|
228
|
+
fn test_sum_integers() {
|
|
229
|
+
let input = EvaluationResult::Collection {
|
|
230
|
+
items: vec![
|
|
231
|
+
EvaluationResult::integer(1),
|
|
232
|
+
EvaluationResult::integer(2),
|
|
233
|
+
EvaluationResult::integer(3),
|
|
234
|
+
],
|
|
235
|
+
has_undefined_order: false,
|
|
236
|
+
type_info: None,
|
|
237
|
+
};
|
|
238
|
+
let result = sum_function(&input).unwrap();
|
|
239
|
+
assert_eq!(result, EvaluationResult::integer(6));
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
#[test]
|
|
243
|
+
fn test_sum_decimals() {
|
|
244
|
+
let input = EvaluationResult::Collection {
|
|
245
|
+
items: vec![
|
|
246
|
+
EvaluationResult::decimal(dec!(1.5)),
|
|
247
|
+
EvaluationResult::decimal(dec!(2.5)),
|
|
248
|
+
],
|
|
249
|
+
has_undefined_order: false,
|
|
250
|
+
type_info: None,
|
|
251
|
+
};
|
|
252
|
+
let result = sum_function(&input).unwrap();
|
|
253
|
+
assert_eq!(result, EvaluationResult::decimal(dec!(4.0)));
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
#[test]
|
|
257
|
+
fn test_sum_mixed_int_decimal() {
|
|
258
|
+
let input = EvaluationResult::Collection {
|
|
259
|
+
items: vec![
|
|
260
|
+
EvaluationResult::integer(1),
|
|
261
|
+
EvaluationResult::decimal(dec!(2.5)),
|
|
262
|
+
],
|
|
263
|
+
has_undefined_order: false,
|
|
264
|
+
type_info: None,
|
|
265
|
+
};
|
|
266
|
+
let result = sum_function(&input).unwrap();
|
|
267
|
+
assert_eq!(result, EvaluationResult::decimal(dec!(3.5)));
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
#[test]
|
|
271
|
+
fn test_sum_empty() {
|
|
272
|
+
let result = sum_function(&EvaluationResult::Empty).unwrap();
|
|
273
|
+
assert_eq!(result, EvaluationResult::integer(0));
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
#[test]
|
|
277
|
+
fn test_sum_quantities() {
|
|
278
|
+
let input = EvaluationResult::Collection {
|
|
279
|
+
items: vec![
|
|
280
|
+
EvaluationResult::quantity(dec!(5), "mg".to_string()),
|
|
281
|
+
EvaluationResult::quantity(dec!(3), "mg".to_string()),
|
|
282
|
+
],
|
|
283
|
+
has_undefined_order: false,
|
|
284
|
+
type_info: None,
|
|
285
|
+
};
|
|
286
|
+
let result = sum_function(&input).unwrap();
|
|
287
|
+
assert_eq!(
|
|
288
|
+
result,
|
|
289
|
+
EvaluationResult::quantity(dec!(8), "mg".to_string())
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
#[test]
|
|
294
|
+
fn test_sum_quantities_different_units() {
|
|
295
|
+
let input = EvaluationResult::Collection {
|
|
296
|
+
items: vec![
|
|
297
|
+
EvaluationResult::quantity(dec!(5), "mg".to_string()),
|
|
298
|
+
EvaluationResult::quantity(dec!(3), "kg".to_string()),
|
|
299
|
+
],
|
|
300
|
+
has_undefined_order: false,
|
|
301
|
+
type_info: None,
|
|
302
|
+
};
|
|
303
|
+
assert!(sum_function(&input).is_err());
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
#[test]
|
|
307
|
+
fn test_min_integers() {
|
|
308
|
+
let input = EvaluationResult::Collection {
|
|
309
|
+
items: vec![
|
|
310
|
+
EvaluationResult::integer(3),
|
|
311
|
+
EvaluationResult::integer(1),
|
|
312
|
+
EvaluationResult::integer(2),
|
|
313
|
+
],
|
|
314
|
+
has_undefined_order: false,
|
|
315
|
+
type_info: None,
|
|
316
|
+
};
|
|
317
|
+
let result = min_function(&input).unwrap();
|
|
318
|
+
assert_eq!(result, EvaluationResult::integer(1));
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
#[test]
|
|
322
|
+
fn test_min_empty() {
|
|
323
|
+
let result = min_function(&EvaluationResult::Empty).unwrap();
|
|
324
|
+
assert_eq!(result, EvaluationResult::Empty);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
#[test]
|
|
328
|
+
fn test_max_integers() {
|
|
329
|
+
let input = EvaluationResult::Collection {
|
|
330
|
+
items: vec![
|
|
331
|
+
EvaluationResult::integer(1),
|
|
332
|
+
EvaluationResult::integer(3),
|
|
333
|
+
EvaluationResult::integer(2),
|
|
334
|
+
],
|
|
335
|
+
has_undefined_order: false,
|
|
336
|
+
type_info: None,
|
|
337
|
+
};
|
|
338
|
+
let result = max_function(&input).unwrap();
|
|
339
|
+
assert_eq!(result, EvaluationResult::integer(3));
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
#[test]
|
|
343
|
+
fn test_max_empty() {
|
|
344
|
+
let result = max_function(&EvaluationResult::Empty).unwrap();
|
|
345
|
+
assert_eq!(result, EvaluationResult::Empty);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
#[test]
|
|
349
|
+
fn test_avg_integers() {
|
|
350
|
+
let input = EvaluationResult::Collection {
|
|
351
|
+
items: vec![
|
|
352
|
+
EvaluationResult::integer(1),
|
|
353
|
+
EvaluationResult::integer(2),
|
|
354
|
+
EvaluationResult::integer(3),
|
|
355
|
+
],
|
|
356
|
+
has_undefined_order: false,
|
|
357
|
+
type_info: None,
|
|
358
|
+
};
|
|
359
|
+
let result = avg_function(&input).unwrap();
|
|
360
|
+
assert_eq!(result, EvaluationResult::decimal(dec!(2)));
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
#[test]
|
|
364
|
+
fn test_avg_decimals() {
|
|
365
|
+
let input = EvaluationResult::Collection {
|
|
366
|
+
items: vec![
|
|
367
|
+
EvaluationResult::decimal(dec!(1.5)),
|
|
368
|
+
EvaluationResult::decimal(dec!(2.5)),
|
|
369
|
+
],
|
|
370
|
+
has_undefined_order: false,
|
|
371
|
+
type_info: None,
|
|
372
|
+
};
|
|
373
|
+
let result = avg_function(&input).unwrap();
|
|
374
|
+
assert_eq!(result, EvaluationResult::decimal(dec!(2.0)));
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
#[test]
|
|
378
|
+
fn test_avg_empty() {
|
|
379
|
+
let result = avg_function(&EvaluationResult::Empty).unwrap();
|
|
380
|
+
assert_eq!(result, EvaluationResult::Empty);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
#[test]
|
|
384
|
+
fn test_avg_quantities() {
|
|
385
|
+
let input = EvaluationResult::Collection {
|
|
386
|
+
items: vec![
|
|
387
|
+
EvaluationResult::quantity(dec!(4), "mg".to_string()),
|
|
388
|
+
EvaluationResult::quantity(dec!(6), "mg".to_string()),
|
|
389
|
+
],
|
|
390
|
+
has_undefined_order: false,
|
|
391
|
+
type_info: None,
|
|
392
|
+
};
|
|
393
|
+
let result = avg_function(&input).unwrap();
|
|
394
|
+
assert_eq!(
|
|
395
|
+
result,
|
|
396
|
+
EvaluationResult::quantity(dec!(5), "mg".to_string())
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
#[test]
|
|
401
|
+
fn test_min_strings() {
|
|
402
|
+
let input = EvaluationResult::Collection {
|
|
403
|
+
items: vec![
|
|
404
|
+
EvaluationResult::string("banana".to_string()),
|
|
405
|
+
EvaluationResult::string("apple".to_string()),
|
|
406
|
+
EvaluationResult::string("cherry".to_string()),
|
|
407
|
+
],
|
|
408
|
+
has_undefined_order: false,
|
|
409
|
+
type_info: None,
|
|
410
|
+
};
|
|
411
|
+
let result = min_function(&input).unwrap();
|
|
412
|
+
assert_eq!(result, EvaluationResult::string("apple".to_string()));
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
#[test]
|
|
416
|
+
fn test_max_strings() {
|
|
417
|
+
let input = EvaluationResult::Collection {
|
|
418
|
+
items: vec![
|
|
419
|
+
EvaluationResult::string("banana".to_string()),
|
|
420
|
+
EvaluationResult::string("apple".to_string()),
|
|
421
|
+
EvaluationResult::string("cherry".to_string()),
|
|
422
|
+
],
|
|
423
|
+
has_undefined_order: false,
|
|
424
|
+
type_info: None,
|
|
425
|
+
};
|
|
426
|
+
let result = max_function(&input).unwrap();
|
|
427
|
+
assert_eq!(result, EvaluationResult::string("cherry".to_string()));
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
#[test]
|
|
431
|
+
fn test_min_dates() {
|
|
432
|
+
let input = EvaluationResult::Collection {
|
|
433
|
+
items: vec![
|
|
434
|
+
EvaluationResult::date("2025-03-01".to_string()),
|
|
435
|
+
EvaluationResult::date("2025-01-01".to_string()),
|
|
436
|
+
EvaluationResult::date("2025-02-01".to_string()),
|
|
437
|
+
],
|
|
438
|
+
has_undefined_order: false,
|
|
439
|
+
type_info: None,
|
|
440
|
+
};
|
|
441
|
+
let result = min_function(&input).unwrap();
|
|
442
|
+
assert_eq!(result, EvaluationResult::date("2025-01-01".to_string()));
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
#[test]
|
|
446
|
+
fn test_sum_single_item() {
|
|
447
|
+
let input = EvaluationResult::integer(42);
|
|
448
|
+
let result = sum_function(&input).unwrap();
|
|
449
|
+
assert_eq!(result, EvaluationResult::integer(42));
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
#[test]
|
|
453
|
+
fn test_min_single_item() {
|
|
454
|
+
let input = EvaluationResult::integer(42);
|
|
455
|
+
let result = min_function(&input).unwrap();
|
|
456
|
+
assert_eq!(result, EvaluationResult::integer(42));
|
|
457
|
+
}
|
|
458
|
+
}
|
|
@@ -171,5 +171,6 @@ fn expression_debug_name(expr: &Expression) -> String {
|
|
|
171
171
|
Expression::Or(_, op, _) => op.clone(),
|
|
172
172
|
Expression::Implies(_, _) => "implies".to_string(),
|
|
173
173
|
Expression::Lambda(_, _) => "=>".to_string(),
|
|
174
|
+
Expression::InstanceSelector(type_name, _) => format!("{} {{...}}", type_name),
|
|
174
175
|
}
|
|
175
176
|
}
|