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.
Files changed (266) hide show
  1. {pysof-0.1.45 → pysof-0.1.47}/Cargo.toml +2 -2
  2. {pysof-0.1.45 → pysof-0.1.47}/PKG-INFO +1 -1
  3. {pysof-0.1.45 → pysof-0.1.47}/crates/fhir/Cargo.toml +3 -3
  4. {pysof-0.1.45 → pysof-0.1.47}/crates/fhir-macro/Cargo.toml +1 -1
  5. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/Cargo.toml +2 -2
  6. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/README.md +24 -14
  7. pysof-0.1.47/crates/fhirpath/src/aggregate_math_functions.rs +458 -0
  8. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/debug_trace.rs +1 -0
  9. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/evaluator.rs +273 -68
  10. pysof-0.1.47/crates/fhirpath/src/format_functions.rs +241 -0
  11. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/handlers.rs +3 -1
  12. pysof-0.1.47/crates/fhirpath/src/interval_functions.rs +453 -0
  13. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/lib.rs +4 -0
  14. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/parse_debug.rs +41 -0
  15. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/parser.rs +58 -0
  16. pysof-0.1.47/crates/fhirpath/src/repeat_all_function.rs +145 -0
  17. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/set_operations.rs +8 -7
  18. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/type_inference.rs +1 -0
  19. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/evaluator_tests.rs +359 -0
  20. {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/Cargo.toml +2 -2
  21. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/Cargo.toml +3 -3
  22. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/src/handlers.rs +2 -1
  23. {pysof-0.1.45 → pysof-0.1.47}/README.md +0 -0
  24. {pysof-0.1.45 → pysof-0.1.47}/crates/fhir/README.md +0 -0
  25. {pysof-0.1.45 → pysof-0.1.47}/crates/fhir/build.rs +0 -0
  26. {pysof-0.1.45 → pysof-0.1.47}/crates/fhir/src/lib.rs +0 -0
  27. {pysof-0.1.45 → pysof-0.1.47}/crates/fhir/src/parameters.rs +0 -0
  28. {pysof-0.1.45 → pysof-0.1.47}/crates/fhir/src/r4.rs +0 -0
  29. {pysof-0.1.45 → pysof-0.1.47}/crates/fhir/src/r4b.rs +0 -0
  30. {pysof-0.1.45 → pysof-0.1.47}/crates/fhir/src/r5.rs +0 -0
  31. {pysof-0.1.45 → pysof-0.1.47}/crates/fhir/src/r6.rs +0 -0
  32. {pysof-0.1.45 → pysof-0.1.47}/crates/fhir/src/r6.rs.template +0 -0
  33. {pysof-0.1.45 → pysof-0.1.47}/crates/fhir/tests/integer_string_integration.rs +0 -0
  34. {pysof-0.1.45 → pysof-0.1.47}/crates/fhir/tests/simple_null_test.rs +0 -0
  35. {pysof-0.1.45 → pysof-0.1.47}/crates/fhir/tests/test_contained_stack.rs +0 -0
  36. {pysof-0.1.45 → pysof-0.1.47}/crates/fhir-macro/README.md +0 -0
  37. {pysof-0.1.45 → pysof-0.1.47}/crates/fhir-macro/src/lib.rs +0 -0
  38. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/PRECISION_LIMITATION.md +0 -0
  39. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/benches/cli_benches.rs +0 -0
  40. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/benches/evaluator_benches.rs +0 -0
  41. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/benches/parser_benches.rs +0 -0
  42. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/benches/server_benches.rs +0 -0
  43. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/examples/test_ucum.rs +0 -0
  44. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/grammar/fhirpath.g4 +0 -0
  45. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/server-api.md +0 -0
  46. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/aggregate_function.rs +0 -0
  47. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/bin/fhirpath-cli.rs +0 -0
  48. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/bin/fhirpath-server.rs +0 -0
  49. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/boolean_functions.rs +0 -0
  50. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/boundary_functions.rs +0 -0
  51. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/cli.rs +0 -0
  52. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/collection_functions.rs +0 -0
  53. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/collection_navigation.rs +0 -0
  54. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/contains_function.rs +0 -0
  55. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/conversion_functions.rs +0 -0
  56. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/date_operation.rs +0 -0
  57. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/datetime_impl.rs +0 -0
  58. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/distinct_functions.rs +0 -0
  59. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/error.rs +0 -0
  60. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/extension_function.rs +0 -0
  61. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/fhir_type_hierarchy.rs +0 -0
  62. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/json_utils.rs +0 -0
  63. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/long_conversion.rs +0 -0
  64. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/models.rs +0 -0
  65. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/not_function.rs +0 -0
  66. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/polymorphic_access.rs +0 -0
  67. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/reference_key_functions.rs +0 -0
  68. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/repeat_function.rs +0 -0
  69. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/resource_type.rs +0 -0
  70. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/server.rs +0 -0
  71. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/subset_functions.rs +0 -0
  72. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/terminology_client.rs +0 -0
  73. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/terminology_functions.rs +0 -0
  74. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/trace_function.rs +0 -0
  75. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/type_function.rs +0 -0
  76. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/src/ucum.rs +0 -0
  77. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/all_function_tests.rs +0 -0
  78. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/boundary_debug_tests.rs +0 -0
  79. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/boundary_function_integration_tests.rs +0 -0
  80. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/comment_test.rs +0 -0
  81. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/common/context.rs +0 -0
  82. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/common/mod.rs +0 -0
  83. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/common/parser.rs +0 -0
  84. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/common/runner.rs +0 -0
  85. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/context_trace_test.rs +0 -0
  86. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r4/input/observation-example.json +0 -0
  87. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r4/input/observation-example.xml +0 -0
  88. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r4/input/patient-example.json +0 -0
  89. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r4/input/patient-example.xml +0 -0
  90. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r4/input/questionnaire-example.json +0 -0
  91. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r4/input/questionnaire-example.xml +0 -0
  92. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r4/input/valueset-example-expansion.json +0 -0
  93. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r4/input/valueset-example-expansion.xml +0 -0
  94. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r4/tests-fhir-r4.xml +0 -0
  95. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r5/input/appointment-examplereq.json +0 -0
  96. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r5/input/ccda.json +0 -0
  97. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r5/input/codesystem-example.json +0 -0
  98. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r5/input/conceptmap-example.json +0 -0
  99. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r5/input/explanationofbenefit-example.json +0 -0
  100. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r5/input/observation-example.json +0 -0
  101. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r5/input/observation-example.xml +0 -0
  102. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r5/input/parameters-example-types.json +0 -0
  103. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r5/input/patient-container-example.json +0 -0
  104. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r5/input/patient-example-name.json +0 -0
  105. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r5/input/patient-example-period.json +0 -0
  106. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r5/input/patient-example.json +0 -0
  107. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r5/input/patient-name-extensions.json +0 -0
  108. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r5/input/questionnaire-example.json +0 -0
  109. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r5/input/valueset-example-expansion.json +0 -0
  110. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r5/known-test-failures.json +0 -0
  111. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/r5/tests-fhir-r5.xml +0 -0
  112. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/data/testSchema.xsd +0 -0
  113. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/date_operation_tests.rs +0 -0
  114. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/datetime_boundary_tests.rs +0 -0
  115. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/debug_datetime_boundary.rs +0 -0
  116. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/debug_string_boundary_tests.rs +0 -0
  117. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/define_variable_test.rs +0 -0
  118. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/enhanced_variable_tests.rs +0 -0
  119. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/extension_tests.rs +0 -0
  120. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/fhir_boundary_tests.rs +0 -0
  121. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/is_as_method_tests.rs +0 -0
  122. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/join_function_test.rs +0 -0
  123. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/oftype_datetime_tests.rs +0 -0
  124. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/parse_debug_test.rs +0 -0
  125. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/parser_tests.rs +0 -0
  126. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/polymorphic_r4_tests.rs +0 -0
  127. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/polymorphic_tests.rs +0 -0
  128. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/precision_tests.rs +0 -0
  129. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/r4_tests.rs +0 -0
  130. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/r5_tests.rs +0 -0
  131. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/reference_key_debug.rs +0 -0
  132. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/test_boundary_zero.rs +0 -0
  133. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/trace_api_test.rs +0 -0
  134. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/trace_tests.rs +0 -0
  135. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/tree_navigation_tests.rs +0 -0
  136. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/truncate_tests.rs +0 -0
  137. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/type_operation_tests.rs +0 -0
  138. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/type_preservation_integration_test.rs +0 -0
  139. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/type_reflection_tests.rs +0 -0
  140. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/uri_type_test.rs +0 -0
  141. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath/tests/uuid_type_preservation_test.rs +0 -0
  142. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath-support/Cargo.toml +0 -0
  143. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath-support/README.md +0 -0
  144. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath-support/src/lib.rs +0 -0
  145. {pysof-0.1.45 → pysof-0.1.47}/crates/fhirpath-support/src/type_info.rs +0 -0
  146. {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/.gitignore +0 -0
  147. {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/Cargo.lock +0 -0
  148. {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/Makefile +0 -0
  149. {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/PYPI_CHECKLIST.md +0 -0
  150. {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/README.md +0 -0
  151. {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/WHEEL_BUILDING.md +0 -0
  152. {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/multithreading_example.py +0 -0
  153. {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/python-tests/__init__.py +0 -0
  154. {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/python-tests/test_content_types.py +0 -0
  155. {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/python-tests/test_core_functions.py +0 -0
  156. {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/python-tests/test_error_handling.py +0 -0
  157. {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/python-tests/test_fhir_versions.py +0 -0
  158. {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/python-tests/test_import.py +0 -0
  159. {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/python-tests/test_package_metadata.py +0 -0
  160. {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/python-tests/test_source_errors.py +0 -0
  161. {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/scripts/build-wheels.py +0 -0
  162. {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/src/lib.rs +0 -0
  163. {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/src/pysof/__init__.py +0 -0
  164. {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/src/pysof/_pysof.pyi +0 -0
  165. {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/src/pysof/py.typed +0 -0
  166. {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/test_multithreading.py +0 -0
  167. {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/tests/integration/content_types.rs +0 -0
  168. {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/tests/integration/error_handling.rs +0 -0
  169. {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/tests/integration/fhir_versions.rs +0 -0
  170. {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/tests/integration/mod.rs +0 -0
  171. {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/tests/integration.rs +0 -0
  172. {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/tests/lib_coverage_tests.rs +0 -0
  173. {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/tests/lib_tests.rs +0 -0
  174. {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/tests/multithreading_integration.rs +0 -0
  175. {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/tests/threading_test.rs +0 -0
  176. {pysof-0.1.45 → pysof-0.1.47}/crates/pysof/uv.lock +0 -0
  177. {pysof-0.1.45 → pysof-0.1.47}/crates/serde-support/Cargo.toml +0 -0
  178. {pysof-0.1.45 → pysof-0.1.47}/crates/serde-support/src/lib.rs +0 -0
  179. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/README.md +0 -0
  180. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/benches/parallel_processing_bench.rs +0 -0
  181. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/src/cli.rs +0 -0
  182. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/src/data_source.rs +0 -0
  183. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/src/error.rs +0 -0
  184. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/src/lib.rs +0 -0
  185. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/src/mod.rs +0 -0
  186. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/src/models.rs +0 -0
  187. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/src/parquet_schema.rs +0 -0
  188. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/src/server.rs +0 -0
  189. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/src/streaming.rs +0 -0
  190. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/src/traits.rs +0 -0
  191. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/common/mod.rs +0 -0
  192. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/debug_boolean_test.rs +0 -0
  193. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/debug_column_ordering.rs +0 -0
  194. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/debug_datetime_instant_constants.rs +0 -0
  195. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/debug_datetime_type_info.rs +0 -0
  196. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/debug_enum_conversion.rs +0 -0
  197. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/debug_evaluation_result_conversion.rs +0 -0
  198. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/debug_extension_function.rs +0 -0
  199. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/debug_extension_macro_fix.rs +0 -0
  200. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/debug_extension_simple.rs +0 -0
  201. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/debug_fhir_resource_structure.rs +0 -0
  202. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/debug_foreach_combinations.rs +0 -0
  203. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/debug_inequality.rs +0 -0
  204. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/debug_instant_constant.rs +0 -0
  205. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/debug_instant_type_info.rs +0 -0
  206. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/debug_raw_resource.rs +0 -0
  207. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/debug_reference_key_types.rs +0 -0
  208. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/extension_debug_test.rs +0 -0
  209. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/foreach_test.rs +0 -0
  210. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/multiselect_test.rs +0 -0
  211. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/run_foreach_tests.rs +0 -0
  212. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/server_tests.rs +0 -0
  213. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/basic.json +0 -0
  214. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/collection.json +0 -0
  215. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/combinations.json +0 -0
  216. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/constant.json +0 -0
  217. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/constant_types.json +0 -0
  218. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/fhirpath.json +0 -0
  219. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/fhirpath_numbers.json +0 -0
  220. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/fn_boundary.json +0 -0
  221. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/fn_empty.json +0 -0
  222. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/fn_extension.json +0 -0
  223. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/fn_first.json +0 -0
  224. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/fn_join.json +0 -0
  225. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/fn_oftype.json +0 -0
  226. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/fn_reference_keys.json +0 -0
  227. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/foreach.json +0 -0
  228. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/logic.json +0 -0
  229. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/repeat.json +0 -0
  230. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/union.json +0 -0
  231. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/validate.json +0 -0
  232. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/view_resource.json +0 -0
  233. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql-on-fhir-v2/tests/where.json +0 -0
  234. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/sql_on_fhir_tests.rs +0 -0
  235. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_base64_binary_constant.rs +0 -0
  236. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_boolean_constant_debug.rs +0 -0
  237. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_boolean_constant_debug2.rs +0 -0
  238. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_boolean_constant_debug3.rs +0 -0
  239. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_boolean_validation.rs +0 -0
  240. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_chunked_processing.rs +0 -0
  241. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_cli_file_source.rs +0 -0
  242. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_csv_quote_handling.rs +0 -0
  243. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_extension_fix_verification.rs +0 -0
  244. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_extension_value_access.rs +0 -0
  245. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_format_parameter_body.rs +0 -0
  246. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_header_parameter_body.rs +0 -0
  247. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_limit_parameter_body.rs +0 -0
  248. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_ndjson_input.rs +0 -0
  249. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_parallel_working.rs +0 -0
  250. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_parameter_validation.rs +0 -0
  251. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_parquet_export.rs +0 -0
  252. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_parquet_large_dataset.rs +0 -0
  253. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_parquet_server_options.rs +0 -0
  254. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_patient_reference_formats.rs +0 -0
  255. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_query_parameter_combinations.rs +0 -0
  256. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_run_operation_parameters.rs +0 -0
  257. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_runner_integration.rs +0 -0
  258. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_shortened_format_names.rs +0 -0
  259. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_where_clause_validation.rs +0 -0
  260. {pysof-0.1.45 → pysof-0.1.47}/crates/sof/tests/test_x_ndjson_support.rs +0 -0
  261. {pysof-0.1.45 → pysof-0.1.47}/pyproject.toml +0 -0
  262. {pysof-0.1.45 → pysof-0.1.47}/src/lib.rs +0 -0
  263. {pysof-0.1.45 → pysof-0.1.47}/src/pysof/__init__.py +0 -0
  264. {pysof-0.1.45 → pysof-0.1.47}/src/pysof/_pysof.cpython-311-darwin.so +0 -0
  265. {pysof-0.1.45 → pysof-0.1.47}/src/pysof/_pysof.pyi +0 -0
  266. {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.45" }
21
- helios-fhir = { path = "../fhir", version = "0.1.45" }
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"] }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pysof
3
- Version: 0.1.45
3
+ Version: 0.1.47
4
4
  Classifier: Programming Language :: Python :: 3
5
5
  Classifier: Programming Language :: Python :: 3.10
6
6
  Classifier: Programming Language :: Python :: 3.11
@@ -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.45" }
28
- helios-serde-support = { path = "../serde-support", version = "0.1.45" }
29
- helios-fhirpath-support = { path = "../fhirpath-support", version = "0.1.45" }
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.45" }
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.45" }
22
- helios-fhirpath-support = { path = "../fhirpath-support", version = "0.1.45" }
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): ❌ Not Implemented
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): ❌ Not Implemented
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): 🟡 (Basic implementation; `preserveOrder` parameter not yet supported)
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): 🟡 (Basic implementation; optional `flags` parameter not yet supported)
339
- * [matchesFull()](https://hl7.org/fhirpath/2025Jan/#matchesfullregex--string--boolean) (STU): 🟡 (Basic implementation; optional `flags` parameter not yet supported)
340
- * [replaceMatches()](https://hl7.org/fhirpath/2025Jan/#replacematchesregex--string-substitution-string--string): 🟡 (Basic implementation; optional `flags` parameter not yet supported)
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): ❌ Not Implemented (can be done via `aggregate($this + $total, 0)`)
418
- * [min()](https://hl7.org/fhirpath/2025Jan/#min--integer--long--decimal--quantity--date--datetime--time--string) (STU): ❌ Not Implemented
419
- * [max()](https://hl7.org/fhirpath/2025Jan/#max--integer--long--decimal--quantity--date--datetime--time--string) (STU): ❌ Not Implemented
420
- * [avg()](https://hl7.org/fhirpath/2025Jan/#avg--decimal--quantity) (STU): ❌ Not Implemented
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
  }