diff --git a/.gitignore b/.gitignore
index 7dd921057..c7c5cb351 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,7 +16,6 @@ VSWorkspaceState.json
.idea/
# ReSharper Settings
-*.DotSettings
*.DotSettings.user
@@ -408,4 +407,3 @@ VSCodeExtension/ncqa-cql-engine-workspace/test/*/*/results/*.json
/Demo/**/Resources/*.*
#remove global.json for testing dotnet sdk updates
global.json
-/Cql/CqlSdkPrototype/generated
diff --git a/Cql-Sdk-All.sln b/Cql-Sdk-All.sln
index 967beb973..7feb9abfd 100644
--- a/Cql-Sdk-All.sln
+++ b/Cql-Sdk-All.sln
@@ -123,10 +123,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "50 CLI Packaging Tools", "5
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CqlSdkPrototype", "Cql\CqlSdkPrototype\CqlSdkPrototype.csproj", "{67D0864B-5682-E701-002E-587BEB5DA60F}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "API", "Demo\API\API.csproj", "{C667CF52-C1E9-41C0-9723-C2DC46C21B46}"
-EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{360E03FF-3AA0-4619-ACCA-93E78265F6DD}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "60 Public API Prototype", "60 Public API Prototype", "{A474C299-9204-4344-B248-1F2FB2CF1830}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CqlApiExamples", "Demo\CqlApiExamples\CqlApiExamples.csproj", "{6165AC8C-733D-9679-5A47-978E61B742FB}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Cql", "Demo\Cql\Cql.csproj", "{A50AA2A0-7868-73A7-2E02-E810842576F9}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -265,10 +269,14 @@ Global
{67D0864B-5682-E701-002E-587BEB5DA60F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{67D0864B-5682-E701-002E-587BEB5DA60F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{67D0864B-5682-E701-002E-587BEB5DA60F}.Release|Any CPU.Build.0 = Release|Any CPU
- {C667CF52-C1E9-41C0-9723-C2DC46C21B46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C667CF52-C1E9-41C0-9723-C2DC46C21B46}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C667CF52-C1E9-41C0-9723-C2DC46C21B46}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C667CF52-C1E9-41C0-9723-C2DC46C21B46}.Release|Any CPU.Build.0 = Release|Any CPU
+ {6165AC8C-733D-9679-5A47-978E61B742FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {6165AC8C-733D-9679-5A47-978E61B742FB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {6165AC8C-733D-9679-5A47-978E61B742FB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {6165AC8C-733D-9679-5A47-978E61B742FB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A50AA2A0-7868-73A7-2E02-E810842576F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A50AA2A0-7868-73A7-2E02-E810842576F9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A50AA2A0-7868-73A7-2E02-E810842576F9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A50AA2A0-7868-73A7-2E02-E810842576F9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -288,7 +296,7 @@ Global
{A252DF59-263B-46D9-A1E4-48E16D8D0DEC} = {F2ADD1CA-E231-4E88-B0EE-5C6885422629}
{399B718D-41C6-4DD9-A7E3-B9758CAAEB89} = {F2ADD1CA-E231-4E88-B0EE-5C6885422629}
{02DCA9CB-92A1-47C9-BF40-97DDD56DEFF3} = {D0F1B9D3-DD8C-4F25-B7AF-0C2EB4E17AC3}
- {8FF55BB5-6004-4461-B8EA-19E9F41B970B} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
+ {8FF55BB5-6004-4461-B8EA-19E9F41B970B} = {F2ADD1CA-E231-4E88-B0EE-5C6885422629}
{50590B39-F376-4E96-92CF-67F84C149E86} = {97D67019-21A7-4591-A216-1B76BD0E68A4}
{FEF0C134-505D-4206-8630-569A6762F787} = {295F5788-3893-4794-A574-4B0A75A3A4BC}
{A7111722-6C8F-42AA-8CDD-732D34FB678A} = {85401759-2C64-414D-9882-4DB1EA41C2ED}
@@ -306,7 +314,7 @@ Global
{AF223BDF-576D-45FD-9A33-483BAFE1BB75} = {A2259B13-A115-4EA3-9DFB-5411DB88EA6F}
{BF2C14B3-EF9B-4BE9-A843-EAA05368BD5A} = {A2259B13-A115-4EA3-9DFB-5411DB88EA6F}
{F2ADD1CA-E231-4E88-B0EE-5C6885422629} = {4B5D75F5-E2FC-4CCB-90EF-6DC4A0193032}
- {BF5D524C-0582-4471-BC79-B04B2D8DFCDB} = {295F5788-3893-4794-A574-4B0A75A3A4BC}
+ {BF5D524C-0582-4471-BC79-B04B2D8DFCDB} = {360E03FF-3AA0-4619-ACCA-93E78265F6DD}
{0E5F5704-5289-4C65-AD4E-364CD5F60C44} = {D33B7DF4-8AC5-47D1-A782-AC5078FFDEC7}
{2929117D-FB33-4BD6-8F20-4B623C114438} = {0E5F5704-5289-4C65-AD4E-364CD5F60C44}
{1AF2E4B2-99C5-4678-B926-D224CC83A8F7} = {D33B7DF4-8AC5-47D1-A782-AC5078FFDEC7}
@@ -317,11 +325,13 @@ Global
{02EA681E-C7D8-13C7-8484-4AC65E1B71E8} = {B00E560D-AE0C-48E1-9013-57115AF05F41}
{85401759-2C64-414D-9882-4DB1EA41C2ED} = {B00E560D-AE0C-48E1-9013-57115AF05F41}
{295F5788-3893-4794-A574-4B0A75A3A4BC} = {4B5D75F5-E2FC-4CCB-90EF-6DC4A0193032}
- {67D0864B-5682-E701-002E-587BEB5DA60F} = {295F5788-3893-4794-A574-4B0A75A3A4BC}
- {C667CF52-C1E9-41C0-9723-C2DC46C21B46} = {7D787B73-D44A-4B65-B83F-4AF4A752F26C}
+ {67D0864B-5682-E701-002E-587BEB5DA60F} = {A474C299-9204-4344-B248-1F2FB2CF1830}
+ {A474C299-9204-4344-B248-1F2FB2CF1830} = {4B5D75F5-E2FC-4CCB-90EF-6DC4A0193032}
+ {6165AC8C-733D-9679-5A47-978E61B742FB} = {A474C299-9204-4344-B248-1F2FB2CF1830}
+ {A50AA2A0-7868-73A7-2E02-E810842576F9} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
- FileExplorer = |build\|docs\|LibrarySets\|submodules\Firely.Cql.Sdk.Integration.Runner\NodeJsUtils\
SolutionGuid = {366252DE-C2FB-4EAC-96EE-22210BD43DE2}
+ FileExplorer = |build\|docs\|LibrarySets\|submodules\Firely.Cql.Sdk.Integration.Runner\NodeJsUtils\
EndGlobalSection
EndGlobal
diff --git a/Cql/CodeGeneration.NET/AssemblyData.cs b/Cql/CodeGeneration.NET/AssemblyBinary.cs
similarity index 68%
rename from Cql/CodeGeneration.NET/AssemblyData.cs
rename to Cql/CodeGeneration.NET/AssemblyBinary.cs
index 1e0eecfcc..872bfcfe2 100644
--- a/Cql/CodeGeneration.NET/AssemblyData.cs
+++ b/Cql/CodeGeneration.NET/AssemblyBinary.cs
@@ -9,16 +9,16 @@
namespace Hl7.Cql.CodeGeneration.NET;
///
-/// Stores information about a dynamically generated assembly.
+/// Contains the binary data for an assembly, and optionally its debug symbols.
///
/// This assembly's binary data.
-/// The assembly's debug symbols in binary data.
-public record AssemblyData(
+/// The assembly's debug symbols.
+public record AssemblyBinary(
byte[]? AssemblyBytes,
byte[]? DebugSymbolsBytes = null)
{
///
- /// An empty instance of .
+ /// An empty instance of .
///
- public static AssemblyData Default { get; } = new(null!, null!);
+ public static AssemblyBinary Default { get; } = new(null!, null!);
}
\ No newline at end of file
diff --git a/Cql/CodeGeneration.NET/AssemblyDataExtensions.cs b/Cql/CodeGeneration.NET/AssemblyBinaryExtensions.cs
similarity index 81%
rename from Cql/CodeGeneration.NET/AssemblyDataExtensions.cs
rename to Cql/CodeGeneration.NET/AssemblyBinaryExtensions.cs
index fd5b5b192..f819e2346 100644
--- a/Cql/CodeGeneration.NET/AssemblyDataExtensions.cs
+++ b/Cql/CodeGeneration.NET/AssemblyBinaryExtensions.cs
@@ -9,9 +9,9 @@
namespace Hl7.Cql.CodeGeneration.NET;
///
-/// Extension methods for .
+/// Extension methods for .
///
-public static class AssemblyDataExtensions
+public static class AssemblyBinaryExtensions
{
///
/// Writes the assembly data to disk.
@@ -19,11 +19,11 @@ public static class AssemblyDataExtensions
/// The assembly data to write.
/// The path to write the assembly to.
/// The path to write the debug symbols to.
- public static TAssemblyData SaveToFiles(
- this TAssemblyData self,
+ public static TAssemblyBinary SaveToFile(
+ this TAssemblyBinary self,
FileInfo? assemblyFile = null,
FileInfo? debugSymbolsFile = null)
- where TAssemblyData : AssemblyData
+ where TAssemblyBinary : AssemblyBinary
{
if (assemblyFile is { } asmFile && self.AssemblyBytes is { Length: > 0 } asmBytes)
{
@@ -43,16 +43,16 @@ public static TAssemblyData SaveToFiles(
///
/// Loads assembly data from files and returns a new instance.
///
- ///
+ ///
/// The assembly data to start from
/// The path to read the assembly from.
/// The path to read the debug symbols from.
///
- public static TAssemblyData LoadFromFiles(
- this TAssemblyData self,
+ public static TAssemblyBinary LoadFromFile(
+ this TAssemblyBinary self,
FileInfo? assemblyFile = null,
FileInfo? debugSymbolsFile = null)
- where TAssemblyData : AssemblyData
+ where TAssemblyBinary : AssemblyBinary
{
var assemblyBytes = assemblyFile is {} f1 ? File.ReadAllBytes(f1.FullName) : self.AssemblyBytes;
var debugSymbolsBytes = debugSymbolsFile is {} f2 ? File.ReadAllBytes(f2.FullName) : self.DebugSymbolsBytes;
diff --git a/Cql/CodeGeneration.NET/AssemblyDataWithSourceCode.cs b/Cql/CodeGeneration.NET/AssemblyBinaryWithSourceCode.cs
similarity index 82%
rename from Cql/CodeGeneration.NET/AssemblyDataWithSourceCode.cs
rename to Cql/CodeGeneration.NET/AssemblyBinaryWithSourceCode.cs
index c2a44bdd4..2e6890bfa 100644
--- a/Cql/CodeGeneration.NET/AssemblyDataWithSourceCode.cs
+++ b/Cql/CodeGeneration.NET/AssemblyBinaryWithSourceCode.cs
@@ -14,24 +14,24 @@ namespace Hl7.Cql.CodeGeneration.NET;
/// This assembly's binary data.
/// The collection of source code files that contributed to this assembly.
/// The assembly's debug symbols in binary data.
-internal record AssemblyDataWithSourceCode(
+internal record AssemblyBinaryWithSourceCode(
byte[]? AssemblyBytes,
IReadOnlyDictionary? SourceCode,
- byte[]? DebugSymbolsBytes = null) : AssemblyData(AssemblyBytes, DebugSymbolsBytes)
+ byte[]? DebugSymbolsBytes = null) : AssemblyBinary(AssemblyBytes, DebugSymbolsBytes)
{
///
- /// An empty instance of .
+ /// An empty instance of .
///
- public new static AssemblyDataWithSourceCode Default { get; } = new(null, null, null);
+ public new static AssemblyBinaryWithSourceCode Default { get; } = new(null, null, null);
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
/// This assembly's binary data.
///
///
/// The assembly's debug symbols in binary data.
- public AssemblyDataWithSourceCode(
+ public AssemblyBinaryWithSourceCode(
byte[]? assemblyBytes,
string? sourceCodeFileName,
string? sourceCode,
diff --git a/Cql/CodeGeneration.NET/AssemblyCompiler.cs b/Cql/CodeGeneration.NET/AssemblyCompiler.cs
index 4fd450f27..46a595c1d 100644
--- a/Cql/CodeGeneration.NET/AssemblyCompiler.cs
+++ b/Cql/CodeGeneration.NET/AssemblyCompiler.cs
@@ -49,12 +49,12 @@ public AssemblyCompiler(TypeResolver typeResolver)
});
}
- public IEnumerable<(Library library, Func generateAssemblyDataWithSourceCode)> CompileDeferred(
+ public IEnumerable<(Library library, Func generateAssemblyBinaryWithSourceCode)> CompileDeferred(
LibrarySet librarySet,
IEnumerable<(Library Library, string CSharp)> input,
AssemblyCompilerDebugInformationFormat debugInformationFormat = AssemblyCompilerDebugInformationFormat.None)
{
- Dictionary results = new();
+ Dictionary results = new();
Assembly[] assemblyReferences = _referencesLazy.Value;
foreach (var (library, cSharp) in input)
yield return (library, () =>
@@ -74,9 +74,9 @@ private static CSharpCompilationOptions CreateCSharpCompilationOptions(
sourceReferenceResolver: new SourceFileResolver(ImmutableArray.Empty, null)
);
- private AssemblyDataWithSourceCode CompileNode(
+ private AssemblyBinaryWithSourceCode CompileNode(
string librarySourceString,
- Dictionary assemblies,
+ Dictionary assemblies,
LibrarySet librarySet,
Library library,
IEnumerable assemblyReferences,
@@ -152,7 +152,7 @@ private AssemblyDataWithSourceCode CompileNode(
}
var bytes = codeStream.ToArray();
var debugSymbols = pdbStream?.ToArray();
- var asmData = new AssemblyDataWithSourceCode(bytes, new Dictionary { { libraryVersionedIdentifier!, librarySourceString }}, debugSymbols);
+ var asmData = new AssemblyBinaryWithSourceCode(bytes, new Dictionary { { libraryVersionedIdentifier!, librarySourceString }}, debugSymbols);
return asmData;
}
@@ -231,7 +231,7 @@ private static void AddNetCoreReferences(List metadataReferen
internal static class AssemblyCompilerExtensions
{
- public static IEnumerable<(Library library, AssemblyDataWithSourceCode assemblyDataWithSourceCode)> Compile(
+ public static IEnumerable<(Library library, AssemblyBinaryWithSourceCode assemblyBinaryWithSourceCode)> Compile(
this AssemblyCompiler compiler,
LibrarySet librarySet,
IEnumerable<(Library Library, string CSharp)> input,
@@ -239,7 +239,7 @@ internal static class AssemblyCompilerExtensions
{
return compiler
.CompileDeferred(librarySet, input, debugInformationFormat)
- .Select(x => (x.library, x.generateAssemblyDataWithSourceCode()));
+ .Select(x => (x.library, x.generateAssemblyBinaryWithSourceCode()));
}
}
diff --git a/Cql/CodeGeneration.NET/PublicAPI.Unshipped.txt b/Cql/CodeGeneration.NET/PublicAPI.Unshipped.txt
index 511d390cd..7d49e55d5 100644
--- a/Cql/CodeGeneration.NET/PublicAPI.Unshipped.txt
+++ b/Cql/CodeGeneration.NET/PublicAPI.Unshipped.txt
@@ -1,26 +1,26 @@
#nullable enable
+Hl7.Cql.CodeGeneration.NET.AssemblyBinary
+Hl7.Cql.CodeGeneration.NET.AssemblyBinary.AssemblyBinary(byte[]? AssemblyBytes, byte[]? DebugSymbolsBytes = null) -> void
+Hl7.Cql.CodeGeneration.NET.AssemblyBinary.AssemblyBinary(Hl7.Cql.CodeGeneration.NET.AssemblyBinary! original) -> void
+Hl7.Cql.CodeGeneration.NET.AssemblyBinary.AssemblyBytes.get -> byte[]?
+Hl7.Cql.CodeGeneration.NET.AssemblyBinary.AssemblyBytes.init -> void
+Hl7.Cql.CodeGeneration.NET.AssemblyBinary.DebugSymbolsBytes.get -> byte[]?
+Hl7.Cql.CodeGeneration.NET.AssemblyBinary.DebugSymbolsBytes.init -> void
+Hl7.Cql.CodeGeneration.NET.AssemblyBinary.Deconstruct(out byte[]? AssemblyBytes, out byte[]? DebugSymbolsBytes) -> void
Hl7.Cql.CodeGeneration.NET.AssemblyCompilerDebugInformationFormat
Hl7.Cql.CodeGeneration.NET.AssemblyCompilerDebugInformationFormat.Embedded = 3 -> Hl7.Cql.CodeGeneration.NET.AssemblyCompilerDebugInformationFormat
Hl7.Cql.CodeGeneration.NET.AssemblyCompilerDebugInformationFormat.None = 0 -> Hl7.Cql.CodeGeneration.NET.AssemblyCompilerDebugInformationFormat
Hl7.Cql.CodeGeneration.NET.AssemblyCompilerDebugInformationFormat.PortablePdb = 2 -> Hl7.Cql.CodeGeneration.NET.AssemblyCompilerDebugInformationFormat
-Hl7.Cql.CodeGeneration.NET.AssemblyData
-Hl7.Cql.CodeGeneration.NET.AssemblyData.AssemblyBytes.get -> byte[]?
-Hl7.Cql.CodeGeneration.NET.AssemblyData.AssemblyBytes.init -> void
-Hl7.Cql.CodeGeneration.NET.AssemblyData.AssemblyData(byte[]? AssemblyBytes, byte[]? DebugSymbolsBytes = null) -> void
-Hl7.Cql.CodeGeneration.NET.AssemblyData.AssemblyData(Hl7.Cql.CodeGeneration.NET.AssemblyData! original) -> void
-Hl7.Cql.CodeGeneration.NET.AssemblyData.DebugSymbolsBytes.get -> byte[]?
-Hl7.Cql.CodeGeneration.NET.AssemblyData.DebugSymbolsBytes.init -> void
-Hl7.Cql.CodeGeneration.NET.AssemblyData.Deconstruct(out byte[]? AssemblyBytes, out byte[]? DebugSymbolsBytes) -> void
-Hl7.Cql.CodeGeneration.NET.AssemblyDataExtensions
-override Hl7.Cql.CodeGeneration.NET.AssemblyData.Equals(object? obj) -> bool
-override Hl7.Cql.CodeGeneration.NET.AssemblyData.GetHashCode() -> int
-override Hl7.Cql.CodeGeneration.NET.AssemblyData.ToString() -> string!
-static Hl7.Cql.CodeGeneration.NET.AssemblyData.Default.get -> Hl7.Cql.CodeGeneration.NET.AssemblyData!
-static Hl7.Cql.CodeGeneration.NET.AssemblyData.operator !=(Hl7.Cql.CodeGeneration.NET.AssemblyData? left, Hl7.Cql.CodeGeneration.NET.AssemblyData? right) -> bool
-static Hl7.Cql.CodeGeneration.NET.AssemblyData.operator ==(Hl7.Cql.CodeGeneration.NET.AssemblyData? left, Hl7.Cql.CodeGeneration.NET.AssemblyData? right) -> bool
-static Hl7.Cql.CodeGeneration.NET.AssemblyDataExtensions.LoadFromFiles(this TAssemblyData! self, System.IO.FileInfo? assemblyFile = null, System.IO.FileInfo? debugSymbolsFile = null) -> TAssemblyData!
-static Hl7.Cql.CodeGeneration.NET.AssemblyDataExtensions.SaveToFiles(this TAssemblyData! self, System.IO.FileInfo? assemblyFile = null, System.IO.FileInfo? debugSymbolsFile = null) -> TAssemblyData!
-virtual Hl7.Cql.CodeGeneration.NET.AssemblyData.$() -> Hl7.Cql.CodeGeneration.NET.AssemblyData!
-virtual Hl7.Cql.CodeGeneration.NET.AssemblyData.EqualityContract.get -> System.Type!
-virtual Hl7.Cql.CodeGeneration.NET.AssemblyData.Equals(Hl7.Cql.CodeGeneration.NET.AssemblyData? other) -> bool
-virtual Hl7.Cql.CodeGeneration.NET.AssemblyData.PrintMembers(System.Text.StringBuilder! builder) -> bool
+Hl7.Cql.CodeGeneration.NET.AssemblyBinaryExtensions
+override Hl7.Cql.CodeGeneration.NET.AssemblyBinary.Equals(object? obj) -> bool
+override Hl7.Cql.CodeGeneration.NET.AssemblyBinary.GetHashCode() -> int
+override Hl7.Cql.CodeGeneration.NET.AssemblyBinary.ToString() -> string!
+static Hl7.Cql.CodeGeneration.NET.AssemblyBinary.Default.get -> Hl7.Cql.CodeGeneration.NET.AssemblyBinary!
+static Hl7.Cql.CodeGeneration.NET.AssemblyBinary.operator !=(Hl7.Cql.CodeGeneration.NET.AssemblyBinary? left, Hl7.Cql.CodeGeneration.NET.AssemblyBinary? right) -> bool
+static Hl7.Cql.CodeGeneration.NET.AssemblyBinary.operator ==(Hl7.Cql.CodeGeneration.NET.AssemblyBinary? left, Hl7.Cql.CodeGeneration.NET.AssemblyBinary? right) -> bool
+static Hl7.Cql.CodeGeneration.NET.AssemblyBinaryExtensions.LoadFromFile(this TAssemblyBinary! self, System.IO.FileInfo? assemblyFile = null, System.IO.FileInfo? debugSymbolsFile = null) -> TAssemblyBinary!
+static Hl7.Cql.CodeGeneration.NET.AssemblyBinaryExtensions.SaveToFile(this TAssemblyBinary! self, System.IO.FileInfo? assemblyFile = null, System.IO.FileInfo? debugSymbolsFile = null) -> TAssemblyBinary!
+virtual Hl7.Cql.CodeGeneration.NET.AssemblyBinary.$() -> Hl7.Cql.CodeGeneration.NET.AssemblyBinary!
+virtual Hl7.Cql.CodeGeneration.NET.AssemblyBinary.EqualityContract.get -> System.Type!
+virtual Hl7.Cql.CodeGeneration.NET.AssemblyBinary.Equals(Hl7.Cql.CodeGeneration.NET.AssemblyBinary? other) -> bool
+virtual Hl7.Cql.CodeGeneration.NET.AssemblyBinary.PrintMembers(System.Text.StringBuilder! builder) -> bool
diff --git a/Cql/CoreTests/ExpressionBuilderContextTests.cs b/Cql/CoreTests/ExpressionBuilderContextTests.cs
index 148d5dba2..fab4dcd80 100644
--- a/Cql/CoreTests/ExpressionBuilderContextTests.cs
+++ b/Cql/CoreTests/ExpressionBuilderContextTests.cs
@@ -1,4 +1,4 @@
-using CqlSdkPrototype.Elm;
+using CqlSdkPrototype.Elm.Internal;
using Hl7.Cql.Abstractions;
using Hl7.Cql.Compiler;
using Hl7.Cql.Runtime.Hosting;
@@ -13,7 +13,7 @@ public class ExpressionBuilderContextTests
[TestMethod]
public void Get_Property_Uses_TypeResolver()
{
- using var serviceProvider = ElmApiState.AddCqlCompilerServices(new ServiceCollection().AddDebugLogging()).BuildServiceProvider(validateScopes: true);
+ using var serviceProvider = ElmToAssemblyProcessorServices.AddCqlCompilerServices(new ServiceCollection().AddDebugLogging()).BuildServiceProvider(validateScopes: true);
var property = ExpressionBuilderContext.GetProperty(typeof(MeasureReport.PopulationComponent), "id", serviceProvider.GetRequiredService())!;
Assert.AreEqual(typeof(Element), property.DeclaringType);
Assert.AreEqual(nameof(Element.ElementId), property.Name);
diff --git a/Cql/CoreTests/ExpressionBuilderTests.cs b/Cql/CoreTests/ExpressionBuilderTests.cs
index 1579f49e8..aa5964476 100644
--- a/Cql/CoreTests/ExpressionBuilderTests.cs
+++ b/Cql/CoreTests/ExpressionBuilderTests.cs
@@ -1,14 +1,14 @@
using Hl7.Fhir.Model;
-using CqlSdkPrototype.Elm;
using Hl7.Cql.Compiler;
using Hl7.Cql.Runtime.Hosting;
+using CqlSdkPrototype.Elm.Internal;
namespace CoreTests
{
[TestClass]
public class LibraryExpressionBuilderTests
{
- private static ServiceProvider BuildServiceProvider() => ElmApiState.AddCqlCompilerServices(new ServiceCollection().AddDebugLogging()).BuildServiceProvider(validateScopes: true);
+ private static ServiceProvider BuildServiceProvider() => ElmToAssemblyProcessorServices.AddCqlCompilerServices(new ServiceCollection().AddDebugLogging()).BuildServiceProvider(validateScopes: true);
[TestMethod]
public void AggregateQueries_1_0_0()
diff --git a/Cql/CoreTests/RuntimeApiTests.cs b/Cql/CoreTests/FluentInvocationToolkitTests.cs
similarity index 64%
rename from Cql/CoreTests/RuntimeApiTests.cs
rename to Cql/CoreTests/FluentInvocationToolkitTests.cs
index acff845f5..7eae5b197 100644
--- a/Cql/CoreTests/RuntimeApiTests.cs
+++ b/Cql/CoreTests/FluentInvocationToolkitTests.cs
@@ -1,8 +1,10 @@
#nullable enable
using CoreTests.Tuples;
using CqlSdkPrototype.Infrastructure;
-using CqlSdkPrototype.Runtime;
-using CqlSdkPrototype.Runtime.Extensions;
+using CqlSdkPrototype.Invocation;
+using CqlSdkPrototype.Invocation.Extensions;
+using CqlSdkPrototype.Invocation.Fluent;
+using CqlSdkPrototype.Invocation.Fluent.Extensions;
using Hl7.Cql.Abstractions.Infrastructure;
using Hl7.Cql.CodeGeneration.NET;
using Hl7.Cql.Fhir;
@@ -10,7 +12,7 @@
namespace CoreTests;
[TestClass]
-public class RuntimeApiTests
+public class FluentInvocationToolkitTests
{
///
[TestMethod]
@@ -22,14 +24,14 @@ public void TestRuntimeScopeAgainstLibraryDefinitionResults()
.Select(dir => Path.GetFullPath(Path.Combine(dir.FullName, "Dlls", "CqlNestedTupleTest-1.0.0.dll")))
.First(File.Exists);
var ctx = FhirCqlContext.ForBundle();
- using var invocationScope = new RuntimeApi()
- .AddAssemblies([AssemblyData.Default.LoadFromFiles(new FileInfo(filePath))])
- .CreateRuntimeScope();
+ using var librarySetInvoker = new FluentInvocationToolkit()
+ .AddAssemblyBinaries(AssemblyBinary.Default.LoadFromFile(new FileInfo(filePath)))
+ .ToLibrarySetInvoker();
// Act
- var result = invocationScope
+ var result = librarySetInvoker
.EnumerateLibraryDefinitionsResults(ctx, CqlVersionedLibraryIdentifier.Parse("CqlNestedTupleTest-1.0.0"))
- .Select(t => (t.definition, t.getResult()))
+ .Select(t => (definition: t.definitionInvoker.DefinitionName, t.getResult()))
.ToDictionary();
// Assert
diff --git a/Cql/CoreTests/LibrarySetExpressionBuilderTests.cs b/Cql/CoreTests/LibrarySetExpressionBuilderTests.cs
index 7c8c3fd84..aec2a9028 100644
--- a/Cql/CoreTests/LibrarySetExpressionBuilderTests.cs
+++ b/Cql/CoreTests/LibrarySetExpressionBuilderTests.cs
@@ -1,4 +1,4 @@
-using CqlSdkPrototype.Elm;
+using CqlSdkPrototype.Elm.Internal;
using Hl7.Cql.Abstractions;
using Hl7.Cql.Compiler;
using Hl7.Cql.Primitives;
@@ -16,7 +16,7 @@ public class LibrarySetExpressionBuilderTests
[TestMethod]
public void LoadLibraryAndDependencies_CrossLibraryCodeSystems()
{
- var serviceProvider = ElmApiState.AddCqlCompilerServices(new ServiceCollection().AddDebugLogging()).BuildServiceProvider(validateScopes: true);
+ var serviceProvider = ElmToAssemblyProcessorServices.AddCqlCompilerServices(new ServiceCollection().AddDebugLogging()).BuildServiceProvider(validateScopes: true);
using var servicesScope = serviceProvider.CreateScope();
LibrarySet librarySet = new();
librarySet.LoadLibraryAndDependencies(LibrarySetsDirs.Cms.ElmDir, "CumulativeMedicationDuration");
diff --git a/Cql/CoreTests/PrimitiveTests.cs b/Cql/CoreTests/PrimitiveTests.cs
index 4b6abaca8..f38bbbfc2 100644
--- a/Cql/CoreTests/PrimitiveTests.cs
+++ b/Cql/CoreTests/PrimitiveTests.cs
@@ -5,14 +5,14 @@
using Hl7.Cql.Operators;
using Hl7.Cql.Primitives;
using Hl7.Cql.Runtime;
-using CqlSdkPrototype.Elm;
+using CqlSdkPrototype.Elm.Fluent;
using Hl7.Cql.Runtime.Hosting;
namespace CoreTests
{
using DateTimePrecision = Hl7.Cql.Iso8601.DateTimePrecision;
using Expression = System.Linq.Expressions.Expression;
-
+
[TestClass]
[TestCategory("UnitTest")]
public class PrimitiveTests
@@ -3408,9 +3408,9 @@ public void Aggregate_Query_Test()
Assert.That.DoesNotThrow(() =>
{
- new ElmApi(loggerFactory)
+ new FluentElmToolkit(loggerFactory)
.AddElmLibraries(librarySet)
- .Compile();
+ .CompileElmToAssemblies();
});
}
diff --git a/Cql/CoreTests/QueriesTest.cs b/Cql/CoreTests/QueriesTest.cs
index b474c6a82..4bbe4ffda 100644
--- a/Cql/CoreTests/QueriesTest.cs
+++ b/Cql/CoreTests/QueriesTest.cs
@@ -3,9 +3,9 @@
using Hl7.Cql.Runtime;
using Hl7.Cql.ValueSets;
using Hl7.Fhir.Model;
-using CqlSdkPrototype.Elm;
using Hl7.Cql.Compiler;
using Hl7.Cql.Runtime.Hosting;
+using CqlSdkPrototype.Elm.Internal;
namespace CoreTests
{
@@ -15,7 +15,7 @@ public class QueriesTest
[ClassInitialize]
public static void Initialize(TestContext context)
{
- using var serviceProvider = ElmApiState.AddCqlCompilerServices(new ServiceCollection().AddDebugLogging()).BuildServiceProvider(validateScopes: true);
+ using var serviceProvider = ElmToAssemblyProcessorServices.AddCqlCompilerServices(new ServiceCollection().AddDebugLogging()).BuildServiceProvider(validateScopes: true);
using var servicesScope = serviceProvider.CreateScope();
var elm = new FileInfo(@"Input\ELM\Test\QueriesTest-1.0.0.json");
diff --git a/Cql/CoreTests/Tuples/CqlTupleTests.cs b/Cql/CoreTests/Tuples/CqlTupleTests.cs
index 74b2a5656..d2ff7adb3 100644
--- a/Cql/CoreTests/Tuples/CqlTupleTests.cs
+++ b/Cql/CoreTests/Tuples/CqlTupleTests.cs
@@ -1,7 +1,9 @@
#nullable enable
using CqlSdkPrototype.Infrastructure;
-using CqlSdkPrototype.Runtime;
-using CqlSdkPrototype.Runtime.Extensions;
+using CqlSdkPrototype.Invocation;
+using CqlSdkPrototype.Invocation.Extensions;
+using CqlSdkPrototype.Invocation.Fluent;
+using CqlSdkPrototype.Invocation.Fluent.Extensions;
using Hl7.Cql.Abstractions.Infrastructure;
using Hl7.Cql.CodeGeneration.NET;
using Hl7.Cql.Fhir;
@@ -100,7 +102,7 @@ public void ExpressionReturningNestedTuplesFromDirectLibraryInstance_ResultCanBe
""", str);
}
- ///
+ ///
[TestMethod]
public void ExpressionReturningNestedTuplesFromAssemblyLoadedLibraryInstance_ResultCanBeSerialized()
{
@@ -109,14 +111,14 @@ public void ExpressionReturningNestedTuplesFromAssemblyLoadedLibraryInstance_Res
.Select(dir => Path.GetFullPath(Path.Combine(dir.FullName, "Dlls", "CqlNestedTupleTest-1.0.0.dll")))
.First(File.Exists);
var ctx = FhirCqlContext.ForBundle();
- using var invocationScope = new RuntimeApi()
- .AddAssemblies([AssemblyData.Default.LoadFromFiles(new FileInfo(filePath))])
- .CreateRuntimeScope();
+ using var librarySetInvoker = new FluentInvocationToolkit()
+ .AddAssemblyBinaries(AssemblyBinary.Default.LoadFromFile(new FileInfo(filePath)))
+ .ToLibrarySetInvoker();
// Act
- var result = invocationScope
+ var result = librarySetInvoker
.EnumerateLibraryDefinitionsResults(ctx, CqlVersionedLibraryIdentifier.Parse("CqlNestedTupleTest-1.0.0"))
- .Select(t => (t.definition, t.getResult()))
+ .Select(t => (definition: t.definitionInvoker.DefinitionName, t.getResult()))
.ToDictionary();
Assert.IsNotNull(result);
result.TryGetValue("Result", out var obj);
diff --git a/Cql/Cql.Abstractions/Infrastructure/EnumerableExtensions.cs b/Cql/Cql.Abstractions/Infrastructure/EnumerableExtensions.cs
index a4b4f7cb0..6b148f516 100644
--- a/Cql/Cql.Abstractions/Infrastructure/EnumerableExtensions.cs
+++ b/Cql/Cql.Abstractions/Infrastructure/EnumerableExtensions.cs
@@ -106,4 +106,9 @@ public static bool TryPeek(
value = stack.Peek();
return true;
}
+
+ public static IEnumerable EnumerateSingle(this T item)
+ {
+ yield return item;
+ }
}
\ No newline at end of file
diff --git a/Cql/Cql.Compiler/Infrastructure/Graphs/Traversal.cs b/Cql/Cql.Compiler/Infrastructure/Graphs/Traversal.cs
index ea18c7427..8a90dec71 100644
--- a/Cql/Cql.Compiler/Infrastructure/Graphs/Traversal.cs
+++ b/Cql/Cql.Compiler/Infrastructure/Graphs/Traversal.cs
@@ -6,6 +6,8 @@
* available at https://raw.githubusercontent.com/FirelyTeam/firely-cql-sdk/main/LICENSE
*/
+using Hl7.Cql.Abstractions.Infrastructure;
+
namespace Hl7.Cql.Compiler.Infrastructure.Graphs;
internal static class Traversal
@@ -56,7 +58,7 @@ public static IEnumerable TopologicalSort(
case 0:
return [];
case 1:
- return EnumerateSingle(unvisited.First());
+ return unvisited.First().EnumerateSingle();
}
HashSet visited = [];
@@ -88,9 +90,4 @@ public static IEnumerable TopologicalSort(
return results;
}
-
- private static IEnumerable EnumerateSingle(T first)
- {
- yield return first;
- }
}
\ No newline at end of file
diff --git a/Cql/Cql.CqlToElm/CqlToElmConverter.cs b/Cql/Cql.CqlToElm/CqlToElmConverter.cs
index 8dad7ee9f..dcc7d206f 100644
--- a/Cql/Cql.CqlToElm/CqlToElmConverter.cs
+++ b/Cql/Cql.CqlToElm/CqlToElmConverter.cs
@@ -23,11 +23,12 @@ public CqlToElmConverter(IServiceProvider services,
///
/// CQL to Elm IServiceProvider
///
- public IServiceProvider Services { get; }
+ private IServiceProvider Services { get; }
+
///
/// CQL to Elm Logger
///
- public ILogger Logger { get; }
+ private ILogger Logger { get; }
///
/// Converts the CQL contained in to an ELM .
@@ -70,17 +71,9 @@ internal LibraryBuilder GetBuilder(LibraryVisitor libraryVisitor, string cql)
internal LibraryBuilder GetBuilder(LibraryVisitor libraryVisitor, TextReader cqlReader)
{
- try
- {
- var libraryContext = ParseLibrary(cqlReader);
- var libraryBuilder = libraryVisitor.Visit(libraryContext);
- return libraryBuilder;
- }
- catch (Exception e)
- {
- Logger.LogCritical(e, "Exception while converting CQL to ELM.");
- throw;
- }
+ var libraryContext = ParseLibrary(cqlReader);
+ var libraryBuilder = libraryVisitor.Visit(libraryContext);
+ return libraryBuilder;
}
internal static LibraryVisitor GetLibraryVisitorScoped(IServiceScope scope)
diff --git a/Cql/Cql.CqlToElm/DependencyInjection/CqlToElmServiceProviderExtensions.cs b/Cql/Cql.CqlToElm/DependencyInjection/CqlToElmServiceProviderExtensions.cs
deleted file mode 100644
index 98180bc0f..000000000
--- a/Cql/Cql.CqlToElm/DependencyInjection/CqlToElmServiceProviderExtensions.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (c) 2024, NCQA and contributors
- * See the file CONTRIBUTORS for details.
- *
- * This file is licensed under the BSD 3-Clause license
- * available at https://raw.githubusercontent.com/FirelyTeam/firely-cql-sdk/main/LICENSE
- */
-
-using Hl7.Cql.CqlToElm;
-using Hl7.Cql.CqlToElm.Builtin;
-using Hl7.Cql.CqlToElm.LibraryProviders;
-
-// ReSharper disable once CheckNamespace
-#pragma warning disable IDE0130 // Namespace does not match folder structure
-namespace Microsoft.Extensions.DependencyInjection;
-
-internal static class CqlToElmServiceProviderExtensions
-{
- public static CqlToElmConverter GetCqlToElmConverter(this IServiceProvider sp) => sp.GetRequiredService();
-
- public static CoercionProvider GetCoercionProvider(this IServiceProvider sp) => sp.GetRequiredService();
-
- public static ElmFactory GetElmFactory(this IServiceProvider sp) => sp.GetRequiredService();
-
- public static SystemLibrary GetSystemLibrary(this IServiceProvider sp) => sp.GetRequiredService();
-
- public static StreamInspector GetStreamInspector(this IServiceProvider sp) => sp.GetRequiredService();
-
- public static InvocationBuilder GetInvocationBuilder(this IServiceProvider sp) => sp.GetRequiredService();
-
- public static LocalIdentifierProvider GetLocalIdentifierProviderTransient(this IServiceProvider sp) => sp.GetRequiredService();
-
- public static MessageProvider GetMessageProvider(this IServiceProvider sp) => sp.GetRequiredService();
-
- public static IModelProvider GetModelProvider(this IServiceProvider sp) => sp.GetRequiredService();
-}
\ No newline at end of file
diff --git a/Cql/Cql.CqlToElm/PublicAPI.Unshipped.txt b/Cql/Cql.CqlToElm/PublicAPI.Unshipped.txt
index c889221a7..6d4dc9fb1 100644
--- a/Cql/Cql.CqlToElm/PublicAPI.Unshipped.txt
+++ b/Cql/Cql.CqlToElm/PublicAPI.Unshipped.txt
@@ -8,8 +8,6 @@ Hl7.Cql.CqlToElm.CqlToElmConverter.ConvertLibrary(string! cql) -> Hl7.Cql.Elm.Li
Hl7.Cql.CqlToElm.CqlToElmConverter.ConvertLibrary(System.IO.Stream! cqlLibrary) -> Hl7.Cql.Elm.Library!
Hl7.Cql.CqlToElm.CqlToElmConverter.ConvertLibrary(System.IO.TextReader! cqlLibrary) -> Hl7.Cql.Elm.Library!
Hl7.Cql.CqlToElm.CqlToElmConverter.CqlToElmConverter(System.IServiceProvider! services, Microsoft.Extensions.Logging.ILogger! logger) -> void
-Hl7.Cql.CqlToElm.CqlToElmConverter.Logger.get -> Microsoft.Extensions.Logging.ILogger!
-Hl7.Cql.CqlToElm.CqlToElmConverter.Services.get -> System.IServiceProvider!
Hl7.Cql.CqlToElm.CqlToElmOptions
Hl7.Cql.CqlToElm.CqlToElmOptions.AllowNullIntervals.get -> bool?
Hl7.Cql.CqlToElm.CqlToElmOptions.AllowNullIntervals.set -> void
diff --git a/Cql/Cql.Packaging/ResourcePackager.cs b/Cql/Cql.Packaging/ResourcePackager.cs
index 29fc5ebe0..bc6280c7f 100644
--- a/Cql/Cql.Packaging/ResourcePackager.cs
+++ b/Cql/Cql.Packaging/ResourcePackager.cs
@@ -14,6 +14,7 @@
using Hl7.Cql.Primitives;
using Hl7.Fhir.Model;
using Hl7.Fhir.Utility;
+using DateTime = System.DateTime;
namespace Hl7.Cql.Packaging;
@@ -30,14 +31,14 @@ namespace Hl7.Cql.Packaging;
internal class ResourcePackager(
TypeResolver typeResolver)
{
- // private readonly FhirResourcePostProcessor? _fhirResourcePostProcessor = fhirResourcePostProcessor;
+ private readonly CqlTypeToFhirTypeMapper _cqlTypeToFhirTypeMapper = new(typeResolver);
public IReadOnlyCollection PackageResources(
DirectoryInfo elmDirectory,
DirectoryInfo cqlDirectory,
string? resourceCanonicalRootUrl,
LibrarySet elmLibrarySet,
- IReadOnlyDictionary assembliesByLibraryName)
+ IReadOnlyDictionary assembliesByLibraryName)
{
var resources = new List();
var librariesByVersionedIdentifier = new Dictionary();
@@ -47,8 +48,6 @@ void OnResourceCreated(FhirResource resource)
resources!.Add(resource);
}
- var typeCrosswalk = new CqlTypeToFhirTypeMapper(typeResolver);
-
foreach (var (name, asmData) in assembliesByLibraryName)
{
var library = elmLibrarySet.GetLibrary(name)!;
@@ -76,7 +75,7 @@ void OnResourceCreated(FhirResource resource)
if (library.GetVersionedIdentifier() is null)
throw new InvalidOperationException("Library VersionedIdentifier should not be null.");
- var fhirLibrary = LibraryPackager.CreateLibraryResource(elmFile, cqlFile, resourceCanonicalRootUrl, asmData, typeCrosswalk, library);
+ var fhirLibrary = LibraryPackager.CreateLibraryResource(elmFile, cqlFile, resourceCanonicalRootUrl, asmData, _cqlTypeToFhirTypeMapper, library);
librariesByVersionedIdentifier.Add(library.GetVersionedIdentifier()!, fhirLibrary);
// Analyze datarequirements and add to the FHIR Library resource.
@@ -264,14 +263,14 @@ from p in populations
}
}
-file static class LibraryPackager
+internal static class LibraryPackager
{
public static FhirLibrary CreateLibraryResource(
FileInfo elmFile,
FileInfo? cqlFile,
string? resourceCanonicalRootUrl,
- AssemblyDataWithSourceCode assemblyDataWithSourceCode,
- CqlTypeToFhirTypeMapper typeCrosswalk,
+ AssemblyBinaryWithSourceCode assemblyBinaryWithSourceCode,
+ CqlTypeToFhirTypeMapper cqlTypeToFhirTypeMapper,
ElmLibrary? elmLibrary = null)
{
if (!elmFile.Exists)
@@ -284,8 +283,27 @@ public static FhirLibrary CreateLibraryResource(
throw new ArgumentException($"File at {elmFile.FullName} is not valid ELM");
}
- var fhirLibrary = CreateFhirLibrary(elmLibrary, elmFile, resourceCanonicalRootUrl);
- AddElmAttachment(elmLibrary, elmFile, fhirLibrary);
+ var elmFileLastWriteTimeUtc = elmFile.LastWriteTimeUtc;
+ var elmBytes = File.ReadAllBytes(elmFile.FullName);
+ byte[]? cqlBytes = null;
+ if (cqlFile!.Exists)
+ cqlBytes = File.ReadAllBytes(cqlFile.FullName);
+
+ return CreateLibraryResource(cqlTypeToFhirTypeMapper, elmLibrary, elmBytes, cqlBytes, assemblyBinaryWithSourceCode.AssemblyBytes, assemblyBinaryWithSourceCode.SourceCode, resourceCanonicalRootUrl, elmFileLastWriteTimeUtc);
+ }
+
+ public static FhirLibrary CreateLibraryResource(
+ CqlTypeToFhirTypeMapper typeCrosswalk,
+ ElmLibrary elmLibrary,
+ byte[] elmBytes,
+ byte[]? cqlBytes,
+ byte[]? assemblyBytes,
+ IEnumerable>? cSharpSourceCodeById,
+ string? resourceCanonicalRootUrl,
+ DateTime? elmFileLastWriteTimeUtc)
+ {
+ var fhirLibrary = CreateFhirLibrary(elmLibrary, resourceCanonicalRootUrl, elmFileLastWriteTimeUtc ?? DateTime.Now);
+ AddElmAttachment(elmLibrary, fhirLibrary, elmBytes);
var parameters = new List();
AddInParameters(elmLibrary, parameters, typeCrosswalk);
AddOutParameters(elmLibrary, parameters, typeCrosswalk);
@@ -295,24 +313,27 @@ public static FhirLibrary CreateLibraryResource(
var fhirParameters = CreateFhirParameters(elmLibrary);
if (fhirParameters.Any())
- AddCQLOptions(fhirLibrary, fhirParameters);
+ AddCqlOptions(fhirLibrary, fhirParameters);
- if (cqlFile!.Exists)
- AddCqlAttachment(elmLibrary, fhirLibrary, cqlFile);
+ if (cqlBytes != null)
+ AddCqlAttachment(elmLibrary, fhirLibrary, cqlBytes);
+
+ if (assemblyBytes != null)
+ AddDllAttachment(elmLibrary, fhirLibrary, assemblyBytes);
- AddDllAttachment(elmLibrary, fhirLibrary, assemblyDataWithSourceCode);
- foreach (var kvp in assemblyDataWithSourceCode.SourceCode!)
- AddCSharpAttachment(fhirLibrary, kvp);
+ if (cSharpSourceCodeById != null)
+ foreach (var kvp in cSharpSourceCodeById)
+ AddCSharpAttachment(fhirLibrary, kvp);
return fhirLibrary;
}
private static void AddElmAttachment(
ElmLibrary elmLibrary,
- FileInfo elmFile,
- FhirLibrary fhirLibrary)
+ FhirLibrary fhirLibrary,
+ byte[] elmBytes)
{
- var bytes = File.ReadAllBytes(elmFile.FullName);
+ var bytes = elmBytes;
var attachment = new Attachment
{
ElementId = $"{elmLibrary.GetVersionedIdentifier()}+elm",
@@ -324,8 +345,8 @@ private static void AddElmAttachment(
private static FhirLibrary CreateFhirLibrary(
ElmLibrary elmLibrary,
- FileInfo elmFile,
- string? resourceCanonicalRootUrl)
+ string? resourceCanonicalRootUrl,
+ DateTime date)
{
var fhirLibrary = new FhirLibrary();
fhirLibrary.Type = LogicLibraryCodeableConcept;
@@ -333,7 +354,7 @@ private static FhirLibrary CreateFhirLibrary(
fhirLibrary.Version = elmLibrary.identifier?.version!;
fhirLibrary.Name = elmLibrary.identifier?.id!;
fhirLibrary.Status = PublicationStatus.Active;
- fhirLibrary.Date = new DateTimeIso8601(elmFile.LastWriteTimeUtc, DateTimePrecision.Millisecond).ToString();
+ fhirLibrary.Date = new DateTimeIso8601(date, DateTimePrecision.Millisecond).ToString();
fhirLibrary.Url = fhirLibrary.CanonicalUri(resourceCanonicalRootUrl);
return fhirLibrary;
}
@@ -398,10 +419,8 @@ private static void AddInParameters(
private static void AddCqlAttachment(
ElmLibrary? elmLibrary,
FhirLibrary fhirLibrary,
- FileInfo cqlFile)
+ byte[] cqlBytes)
{
- var cqlBytes = File.ReadAllBytes(cqlFile.FullName);
-
var attachment = new Attachment
{
ElementId = $"{elmLibrary!.GetVersionedIdentifier()}+cql",
@@ -411,7 +430,7 @@ private static void AddCqlAttachment(
fhirLibrary.Content.Add(attachment);
}
- private static void AddCQLOptions(
+ private static void AddCqlOptions(
FhirLibrary fhirLibrary,
IReadOnlyList fhirParameters)
{
@@ -453,9 +472,8 @@ private static void AddCSharpAttachment(FhirLibrary library, KeyValuePair? filePredicate = null)
+ {
+ var files = directory.EnumerateFiles("*.cql", options ?? InternalConstants.DefaultEnumerationOptions);
+ if (filePredicate is not null) files = files.Where(filePredicate);
+ return cqlToolkit.AddCqlLibraryFiles(files);
+ }
+
+ public static FluentCqlToolkit AddCqlLibraryFiles(
+ this FluentCqlToolkit cqlToolkit,
+ IEnumerable files)
+ {
+ var logger = cqlToolkit.CreateLogger();
+ var cqlLibraries = files
+ .Select(f =>
+ {
+ logger.LogInformation("Loading library from file: {file}", f);
+ var cqlContent = File.ReadAllText(f.FullName);
+ var cqlLibrary = CqlLibraryString.Parse(cqlContent);
+ return cqlLibrary;
+ }); // Log errors
+
+ return cqlToolkit.AddCqlLibraries(cqlLibraries);
+ }
+}
\ No newline at end of file
diff --git a/Cql/CqlSdkPrototype/Cql/Extensions/CqlApiExtensions.Saving.cs b/Cql/CqlSdkPrototype/Cql.Fluent/Extensions/FluentCqlToolkitExtensions.Saving.cs
similarity index 51%
rename from Cql/CqlSdkPrototype/Cql/Extensions/CqlApiExtensions.Saving.cs
rename to Cql/CqlSdkPrototype/Cql.Fluent/Extensions/FluentCqlToolkitExtensions.Saving.cs
index 61e2806d5..affa42ff7 100644
--- a/Cql/CqlSdkPrototype/Cql/Extensions/CqlApiExtensions.Saving.cs
+++ b/Cql/CqlSdkPrototype/Cql.Fluent/Extensions/FluentCqlToolkitExtensions.Saving.cs
@@ -1,21 +1,18 @@
-using CqlSdkPrototype.Cql.Extensibility;
+namespace CqlSdkPrototype.Cql.Fluent.Extensions;
-namespace CqlSdkPrototype.Cql.Extensions;
-
-public static partial class CqlApiExtensions
+public static partial class FluentCqlToolkitExtensions
{
- public static TCqlApi SaveElmFileToDirectory(
- this TCqlApi cqlApi,
+ public static FluentCqlToolkit SaveElmFilesToDirectory(
+ this FluentCqlToolkit cqlToolkit,
DirectoryInfo directory,
bool writeIndented = true)
- where TCqlApi : ICqlApiExtendable
{
if (!directory.Exists)
directory.Create();
- var logger = cqlApi.LoggerFactory.CreateLogger(typeof(CqlApiExtensions));
+ var logger = cqlToolkit.CreateLogger();
- foreach (var (libraryName, (_, elmLibrary)) in cqlApi.Entries)
+ foreach (var (libraryName, (_, elmLibrary)) in cqlToolkit.CqlToElmTranslations)
{
if (elmLibrary == null)
continue;
@@ -25,6 +22,6 @@ public static TCqlApi SaveElmFileToDirectory(
logger.LogInformation("Saved ELM to file: {file}", fileName);
}
- return cqlApi;
+ return cqlToolkit;
}
}
\ No newline at end of file
diff --git a/Cql/CqlSdkPrototype/Cql.Fluent/Extensions/FluentCqlToolkitExtensions.cs b/Cql/CqlSdkPrototype/Cql.Fluent/Extensions/FluentCqlToolkitExtensions.cs
new file mode 100644
index 000000000..3cca58a9f
--- /dev/null
+++ b/Cql/CqlSdkPrototype/Cql.Fluent/Extensions/FluentCqlToolkitExtensions.cs
@@ -0,0 +1,17 @@
+using Hl7.Cql.Abstractions.Exceptions;
+
+namespace CqlSdkPrototype.Cql.Fluent.Extensions;
+
+public static partial class FluentCqlToolkitExtensions
+{
+ private static ILogger CreateLogger(this FluentCqlToolkit cqlToolkit) =>
+ cqlToolkit.LoggerFactory.CreateLogger(typeof(FluentCqlToolkitExtensions));
+
+ public static FluentCqlToolkit ConfigIgnoreExceptions(this FluentCqlToolkit cqlToolkit, bool stopAfterFirstException = false) =>
+ cqlToolkit.Reconfigure(o => o with
+ {
+ ProcessBatchItemExceptionHandling = stopAfterFirstException
+ ? ProcessBatchItemExceptionHandling.IgnoreExceptionAndBreak
+ : ProcessBatchItemExceptionHandling.IgnoreExceptionAndContinue
+ });
+}
\ No newline at end of file
diff --git a/Cql/CqlSdkPrototype/Cql.Fluent/FluentCqlToolkit.cs b/Cql/CqlSdkPrototype/Cql.Fluent/FluentCqlToolkit.cs
new file mode 100644
index 000000000..ab62900c1
--- /dev/null
+++ b/Cql/CqlSdkPrototype/Cql.Fluent/FluentCqlToolkit.cs
@@ -0,0 +1,72 @@
+using CqlSdkPrototype.Infrastructure;
+
+namespace CqlSdkPrototype.Cql.Fluent;
+
+///
+/// Provides a fluent interface for working with CQL to ELM translations.
+///
+public sealed class FluentCqlToolkit(CqlToElmTranslator cqlToElmTranslator)
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The logger factory to use for logging.
+ /// The configuration settings for the translator.
+ public FluentCqlToolkit(
+ ILoggerFactory? loggerFactory = null,
+ CqlToElmTranslatorConfig? config = null) : this(new CqlToElmTranslator(loggerFactory, config))
+ {
+ }
+
+ ///
+ /// Gets the service provider used by tests.
+ ///
+ internal ServiceProvider ServiceProvider => cqlToElmTranslator.ServiceProvider;
+
+ ///
+ /// Gets the logger factory used by extensions.
+ ///
+ public ILoggerFactory LoggerFactory => cqlToElmTranslator.LoggerFactory;
+
+ ///
+ /// Gets the configuration settings for the translator.
+ ///
+ public CqlToElmTranslatorConfig Config => cqlToElmTranslator.Config;
+
+ ///
+ /// Gets the dictionary of CQL to ELM translations.
+ ///
+ public CqlToElmTranslationReadOnlyDictionary CqlToElmTranslations => cqlToElmTranslator.CqlToElmTranslations;
+
+ ///
+ /// Reconfigures the translator with new configuration settings.
+ ///
+ /// A function that takes the current configuration and returns a new configuration.
+ /// The current instance of .
+ public FluentCqlToolkit Reconfigure(Func configure)
+ {
+ cqlToElmTranslator.Reconfigure(configure(Config));
+ return this;
+ }
+
+ ///
+ /// Adds CQL libraries to the translator.
+ ///
+ /// The CQL libraries to add.
+ /// The current instance of .
+ public FluentCqlToolkit AddCqlLibraries(IEnumerable libraries)
+ {
+ cqlToElmTranslator.AddCqlLibraries(libraries);
+ return this;
+ }
+
+ ///
+ /// Translates the CQL libraries to ELM libraries.
+ ///
+ /// The current instance of .
+ public FluentCqlToolkit TranslateCqlToElm()
+ {
+ cqlToElmTranslator.TranslateCqlToElm();
+ return this;
+ }
+}
diff --git a/Cql/CqlSdkPrototype/Cql/CqlApi.cs b/Cql/CqlSdkPrototype/Cql/CqlApi.cs
deleted file mode 100644
index 6dcb73518..000000000
--- a/Cql/CqlSdkPrototype/Cql/CqlApi.cs
+++ /dev/null
@@ -1,143 +0,0 @@
-using CqlSdkPrototype.Cql.Extensibility;
-using CqlSdkPrototype.Cql.Internal;
-using CqlSdkPrototype.Infrastructure;
-using CqlSdkPrototype.Internal;
-using Hl7.Cql.CqlToElm;
-using Hl7.Cql.Runtime;
-
-namespace CqlSdkPrototype.Cql;
-
-public class CqlApi :
- ICqlApiExtendable,
- ICqlApiInternal
-{
- public CqlApi(
- ILoggerFactory? loggerFactory = null,
- CqlApiOptions? options = null)
- {
- options ??= CqlApiOptions.Default;
- var entries = CqlApiStateEntryDictionary.Empty.WithComparers(CqlVersionedLibraryIdentifier.IdentifierOnlyEqualityComparer);
- var entriesBuilder = entries.ToBuilder();
- var libraryProvider = new EntriesBuilderLibraryProvider(entriesBuilder);
- _entries = entries;
- _services = CqlApiServices.Create(loggerFactory ?? NullLoggerFactory.Instance, options, libraryProvider);
- _options = options;
- }
-
- #region State
-
- private CqlApiServices _services;
- private CqlApiStateEntryDictionary _entries;
- private CqlApiOptions _options;
-
- private CqlApi WithEntries(
- CqlApiStateEntryDictionary entries)
- {
- _entries = entries;
- _services.LibraryProvider.EntriesBuilder = entries.ToBuilder();
- return this;
- }
-
- public CqlApi WithOptions(
- Func replaceOptions)
- {
- var libraryProvider = _services.LibraryProvider;
- _services.ServiceProvider.Dispose();
- _options = replaceOptions(_options);
- _services = CqlApiServices.Create(_services.LoggerFactory, _options, libraryProvider);
- return this;
- }
-
- ILoggerFactory ICqlApiExtendable.LoggerFactory => _services.LoggerFactory;
- CqlApiOptions ICqlApiExtendable.Options => _options;
- IReadOnlyDictionary ICqlApiExtendable.Entries => _entries;
- CqlApiServices ICqlApiInternal.Services => _services;
-
- #endregion
-
- #region Input (CQL Library Strings)
-
- public CqlApi AddCqlLibraries(IEnumerable cqlLibraries)
- {
- var logger = _services.Logger;
- var entriesBuilder = _services.LibraryProvider.EntriesBuilder;
- var cqlToElmConverter = _services.CqlToElmConverter;
- using var scope = _services.ServiceProvider.CreateScope()!;
- var hasChanged = false;
- foreach (var cqlLibrary in cqlLibraries)
- {
- var versionedIdentifier = cqlLibrary.VersionedLibraryIdentifier;
-
- if (entriesBuilder.ContainsKey(versionedIdentifier))
- {
- logger.LogInformation("Skipping adding previous cql to translation: {versionedIdentifier}", versionedIdentifier);
- continue;
- }
-
- var libraryVisitor = CqlToElmConverter.GetLibraryVisitorScoped(scope);
- var libraryBuilder = cqlToElmConverter.GetBuilder(libraryVisitor, cqlLibrary.Cql);
- var entry = new CqlApiStateEntry(cqlLibrary) { ElmLibraryBuilder = libraryBuilder };
- entriesBuilder.Add(versionedIdentifier, entry);
- logger.LogInformation("Adding cql library to translation: {versionedIdentifier}", versionedIdentifier);
- hasChanged = true;
- }
-
- return hasChanged
- ? WithEntries(entries: entriesBuilder.ToImmutable())
- : this;
- }
-
- #endregion
-
- #region Processing (CQL-to-ELM)
-
- public CqlApi Translate()
- {
- CqlApiStateEntryDictionary.Builder entriesBuilder = _services.LibraryProvider.EntriesBuilder;
- var logger = _services.Logger;
- bool atFirst = true;
-
- IEnumerable<(CqlVersionedLibraryIdentifier versionedIdentifier, CqlApiStateEntry cqlTranslationEntry)> GetLibrariesForProcessing()
- {
- foreach (var (versionedIdentifier, cqlTranslationEntry) in
- _entries.Where(kv => kv.Value.ElmLibrary is null))
- {
- if (atFirst)
- {
- atFirst = false;
- logger.LogInformation("Translating CQL into ELM");
- }
-
- logger.LogInformation("Translating CQL: {id}", versionedIdentifier);
- yield return (versionedIdentifier, cqlTranslationEntry);
- }
- }
-
- LogExceptionMessageAction log = logger.GetLogExceptionMessageAction(_options.ProcessBatchItemExceptionHandling);
-
- int changedCount =
- GetLibrariesForProcessing()
- .TryProcessEach(t => ProcessLibrary(t.versionedIdentifier, t.cqlTranslationEntry))
- .WithEachException(outcome =>
- {
- var libraryName = outcome.Input.versionedIdentifier;
- log(outcome.Exception, "Error translating CQL library '{libraryName}' to ELM.", libraryName);
- })
- .HandleExceptions(_options.ProcessBatchItemExceptionHandling)
- .Count() // We must enumerate all
- ;
-
- if (changedCount <= 0)
- return this;
-
- return WithEntries(entries: entriesBuilder.ToImmutable());
-
- void ProcessLibrary(CqlVersionedLibraryIdentifier versionedIdentifier, CqlApiStateEntry cqlTranslationEntry)
- {
- var library = cqlTranslationEntry.ElmLibraryBuilder!.Build();
- entriesBuilder[versionedIdentifier] = cqlTranslationEntry with { ElmLibrary = library };
- }
- }
-
- #endregion
-}
\ No newline at end of file
diff --git a/Cql/CqlSdkPrototype/Cql/CqlModel.cs b/Cql/CqlSdkPrototype/Cql/CqlModel.cs
index 73dd7789b..1b80dabf6 100644
--- a/Cql/CqlSdkPrototype/Cql/CqlModel.cs
+++ b/Cql/CqlSdkPrototype/Cql/CqlModel.cs
@@ -1,7 +1,19 @@
-namespace CqlSdkPrototype.Cql;
+using Hl7.Cql.Model;
+namespace CqlSdkPrototype.Cql;
+
+///
+/// Represents the model of CQL to use.
+///
public enum CqlModel
{
+ ///
+ /// Represents the ELM R1 model which maps to
+ ///
ElmR1 = 1,
+
+ ///
+ /// Represents the FHIR 4.0.1 model which maps to
+ ///
Fhir401 = 2,
}
\ No newline at end of file
diff --git a/Cql/CqlSdkPrototype/Cql/CqlToElmTranslation.cs b/Cql/CqlSdkPrototype/Cql/CqlToElmTranslation.cs
new file mode 100644
index 000000000..fbfe9cd16
--- /dev/null
+++ b/Cql/CqlSdkPrototype/Cql/CqlToElmTranslation.cs
@@ -0,0 +1,20 @@
+using CqlSdkPrototype.Infrastructure;
+using Hl7.Cql.CqlToElm;
+using Hl7.Cql.Elm;
+
+namespace CqlSdkPrototype.Cql;
+
+///
+/// Represents a translation from a CQL library to an ELM library.
+///
+/// The input CQL library string.
+/// The output ELM library.
+public readonly record struct CqlToElmTranslation(
+ CqlLibraryString CqlLibraryString,
+ Library? ElmLibrary = null) {
+
+ ///
+ /// The builder for the ELM library. This is set during .
+ ///
+ internal LibraryBuilder? ElmLibraryBuilder { get; init; }
+}
\ No newline at end of file
diff --git a/Cql/CqlSdkPrototype/Cql/CqlToElmTranslator.cs b/Cql/CqlSdkPrototype/Cql/CqlToElmTranslator.cs
new file mode 100644
index 000000000..145277f7c
--- /dev/null
+++ b/Cql/CqlSdkPrototype/Cql/CqlToElmTranslator.cs
@@ -0,0 +1,191 @@
+using CqlSdkPrototype.Cql.Fluent;
+using CqlSdkPrototype.Cql.Internal;
+using CqlSdkPrototype.Infrastructure;
+using CqlSdkPrototype.Internal;
+using Hl7.Cql.CqlToElm;
+using Hl7.Cql.Runtime;
+
+namespace CqlSdkPrototype.Cql;
+
+///
+/// Translates CQL libraries to ELM libraries.
+///
+public sealed class CqlToElmTranslator
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The logger factory to use for logging.
+ /// The configuration settings for the translator.
+ public CqlToElmTranslator(
+ ILoggerFactory? loggerFactory = null,
+ CqlToElmTranslatorConfig? config = null)
+ {
+ config ??= CqlToElmTranslatorConfig.Default;
+ loggerFactory ??= NullLoggerFactory.Instance;
+ LoggerFactory = loggerFactory;
+ _cqlToElmTranslations = CqlToElmTranslationDictionary.Empty;
+ Config = config;
+ _services = CqlToElmTranslatorServices.Create(loggerFactory, config, _cqlToElmTranslations);
+ }
+
+ private CqlToElmTranslationDictionary _cqlToElmTranslations;
+ private CqlToElmTranslatorServices _services;
+
+ ///
+ /// Gets the logger factory used by extensions via .
+ ///
+ internal ILoggerFactory LoggerFactory { get; }
+
+ ///
+ /// Gets the service provider used by tests via .
+ ///
+ internal ServiceProvider ServiceProvider => _services.ServiceProvider;
+
+ ///
+ /// Gets the configuration settings for the translator.
+ ///
+ public CqlToElmTranslatorConfig Config { get; private set; }
+
+ ///
+ /// Gets the dictionary of CQL to ELM translations.
+ ///
+ public CqlToElmTranslationReadOnlyDictionary CqlToElmTranslations => _cqlToElmTranslations;
+
+ ///
+ /// Sets the CQL to ELM conversions.
+ ///
+ /// The dictionary of translations to set.
+ private void SetCqlToElmTranslations(
+ CqlToElmTranslationDictionary translations)
+ {
+ _cqlToElmTranslations = translations;
+ _services.LibraryBuilderProvider.TranslationsBuilder = translations.ToBuilder();
+ }
+
+ ///
+ /// Reconfigures the translator with new configuration settings.
+ ///
+ /// The new configuration settings.
+ public void Reconfigure(
+ CqlToElmTranslatorConfig config)
+ {
+ if (Config == config)
+ return;
+
+ _services.ServiceProvider.Dispose();
+ Config = config;
+ _services = CqlToElmTranslatorServices.Create(_services.LoggerFactory, config, _cqlToElmTranslations);
+ }
+
+ ///
+ /// Adds CQL libraries to the translator.
+ ///
+ /// The CQL libraries to add.
+ public void AddCqlLibraries(IEnumerable cqlLibraries)
+ {
+ var logger = _services.Logger;
+ var entriesBuilder = _services.LibraryBuilderProvider.TranslationsBuilder;
+ LogExceptionMessageAction log = logger.GetLogExceptionMessageAction(Config.ProcessBatchItemExceptionHandling);
+
+ int addedCount =
+ BuildTranslations(cqlLibraries, entriesBuilder)
+ .TryProcessEach(t => (t.cqlLibraryString, cqlToElmTranslation: t.cqlToElmTranslationFn()))
+ .WithEachException(outcome =>
+ {
+ var libraryName = outcome.Input.cqlLibraryString.VersionedLibraryIdentifier;
+ log(outcome.Exception, "Error parsing CQL library '{libraryName}' to ELM.", libraryName);
+ })
+ .HandleExceptions(Config.ProcessBatchItemExceptionHandling)
+ .Count() // We must enumerate all
+ ;
+
+ if (addedCount > 0)
+ SetCqlToElmTranslations(translations: entriesBuilder.ToImmutable());
+ }
+
+ ///
+ /// Builds the translations for the given CQL libraries.
+ ///
+ /// The CQL libraries to translate.
+ /// The builder for the translation entries.
+ /// A collection of CQL library strings and their corresponding translation functions.
+ private IEnumerable<(CqlLibraryString cqlLibraryString, Func cqlToElmTranslationFn)> BuildTranslations(
+ IEnumerable cqlLibraries,
+ CqlToElmTranslationDictionary.Builder entriesBuilder)
+ {
+ var logger = _services.Logger;
+ var cqlToElmConverter = _services.CqlToElmConverter;
+ using var scope = _services.ServiceProvider.CreateScope()!;
+ foreach (var cqlLibrary in cqlLibraries)
+ {
+ var versionedIdentifier = cqlLibrary.VersionedLibraryIdentifier;
+
+ if (entriesBuilder.ContainsKey(versionedIdentifier))
+ {
+ logger.LogInformation("Skipping adding previous cql to translation: {versionedIdentifier}", versionedIdentifier);
+ continue;
+ }
+
+ yield return (cqlLibrary,
+ () =>
+ {
+ var libraryVisitor = CqlToElmConverter.GetLibraryVisitorScoped(scope);
+ var libraryBuilder = cqlToElmConverter.GetBuilder(libraryVisitor, cqlLibrary.Cql);
+ var translation = new CqlToElmTranslation(cqlLibrary) { ElmLibraryBuilder = libraryBuilder };
+ entriesBuilder.Add(versionedIdentifier, translation);
+ return translation;
+ }
+ );
+ }
+ }
+
+ ///
+ /// Translates the CQL libraries to ELM libraries.
+ ///
+ public void TranslateCqlToElm()
+ {
+ CqlToElmTranslationDictionary.Builder processItemsBuilder = _services.LibraryBuilderProvider.TranslationsBuilder;
+ var logger = _services.Logger;
+ bool atFirst = true;
+
+ IEnumerable<(CqlVersionedLibraryIdentifier versionedIdentifier, CqlToElmTranslation cqlTranslationEntry)> GetLibrariesForProcessing()
+ {
+ foreach (var (versionedIdentifier, cqlTranslationEntry) in
+ _cqlToElmTranslations.Where(kv => kv.Value.ElmLibrary is null))
+ {
+ if (atFirst)
+ {
+ atFirst = false;
+ logger.LogInformation("Translating CQL into ELM");
+ }
+
+ logger.LogInformation("Translating CQL: {id}", versionedIdentifier);
+ yield return (versionedIdentifier, cqlTranslationEntry);
+ }
+ }
+
+ LogExceptionMessageAction log = logger.GetLogExceptionMessageAction(Config.ProcessBatchItemExceptionHandling);
+
+ int changedCount =
+ GetLibrariesForProcessing()
+ .TryProcessEach(t => ProcessLibrary(t.versionedIdentifier, t.cqlTranslationEntry))
+ .WithEachException(outcome =>
+ {
+ var libraryName = outcome.Input.versionedIdentifier;
+ log(outcome.Exception, "Error translating CQL library '{libraryName}' to ELM.", libraryName);
+ })
+ .HandleExceptions(Config.ProcessBatchItemExceptionHandling)
+ .Count() // We must enumerate all
+ ;
+
+ if (changedCount > 0)
+ SetCqlToElmTranslations(translations: processItemsBuilder.ToImmutable());
+
+ void ProcessLibrary(CqlVersionedLibraryIdentifier versionedIdentifier, CqlToElmTranslation cqlTranslationEntry)
+ {
+ var library = cqlTranslationEntry.ElmLibraryBuilder!.Build();
+ processItemsBuilder[versionedIdentifier] = cqlTranslationEntry with { ElmLibrary = library };
+ }
+ }
+}
diff --git a/Cql/CqlSdkPrototype/Cql/CqlApiOptions.cs b/Cql/CqlSdkPrototype/Cql/CqlToElmTranslatorConfig.cs
similarity index 79%
rename from Cql/CqlSdkPrototype/Cql/CqlApiOptions.cs
rename to Cql/CqlSdkPrototype/Cql/CqlToElmTranslatorConfig.cs
index cb2916187..2935bb48f 100644
--- a/Cql/CqlSdkPrototype/Cql/CqlApiOptions.cs
+++ b/Cql/CqlSdkPrototype/Cql/CqlToElmTranslatorConfig.cs
@@ -4,8 +4,10 @@
namespace CqlSdkPrototype.Cql;
-public record CqlApiOptions
-(
+///
+/// The settings used to configure the CQL to ELM processor.
+///
+public sealed record CqlToElmTranslatorConfig(
ProcessBatchItemExceptionHandling ProcessBatchItemExceptionHandling = default,
ImmutableHashSet? Models = null,
ImmutableHashSet? ModelInfos = null,
@@ -14,13 +16,18 @@ public record CqlApiOptions
bool EnableListPromotion = false,
bool EnableIntervalPromotion = false,
bool EnableIntervalDemotion = false,
- bool AllowNullInterval = false
- )
+ bool AllowNullIntervals = false)
{
- public static CqlApiOptions Default { get; } = new();
+ public static CqlToElmTranslatorConfig Default { get; } = new();
+ ///
+ /// The model information to use when translating CQL to ELM.
+ ///
public ImmutableHashSet Models { get; init; } = Models ?? [];
+ ///
+ /// The model information to use when translating CQL to ELM.
+ ///
public ImmutableHashSet ModelInfos { get; init; } = ModelInfos ?? [];
///
@@ -30,7 +37,7 @@ public record CqlApiOptions
/// The default value is .
///
///
- public bool AllowNullIntervals { get; set; } = AllowNullInterval;
+ public bool AllowNullIntervals { get; init; } = AllowNullIntervals;
///
/// When , point intervals will automatically be created as necessary from scalar values.
@@ -68,7 +75,7 @@ public record CqlApiOptions
/// This type of conversion will issue a warning and could fail at runtime.
///
///
- public bool EnableListDemotion { get; set; } = EnableListDemotion;
+ public bool EnableListDemotion { get; init; } = EnableListDemotion;
///
///
@@ -80,17 +87,14 @@ public record CqlApiOptions
///
///
public AmbiguousTypeBehavior AmbiguousTypeBehavior { get; init; } = AmbiguousTypeBehavior;
-}
-internal static class CqlApiOptionsExtensions
-{
- internal static void ApplyToCqlToElmOptions(this CqlApiOptions options, CqlToElmOptions opt)
+ internal void ApplyToCqlToElmOptions(CqlToElmOptions opt)
{
- opt.AmbiguousTypeBehavior = options.AmbiguousTypeBehavior;
- opt.EnableListPromotion = options.EnableListPromotion;
- opt.EnableListDemotion = options.EnableListDemotion;
- opt.EnableIntervalPromotion = options.EnableIntervalPromotion;
- opt.EnableIntervalDemotion = options.EnableIntervalDemotion;
- opt.AllowNullIntervals = options.AllowNullIntervals;
+ opt.AmbiguousTypeBehavior = AmbiguousTypeBehavior;
+ opt.EnableListPromotion = EnableListPromotion;
+ opt.EnableListDemotion = EnableListDemotion;
+ opt.EnableIntervalPromotion = EnableIntervalPromotion;
+ opt.EnableIntervalDemotion = EnableIntervalDemotion;
+ opt.AllowNullIntervals = AllowNullIntervals;
}
}
\ No newline at end of file
diff --git a/Cql/CqlSdkPrototype/Cql/Extensibility/CqlApiExtendableExtensions.cs b/Cql/CqlSdkPrototype/Cql/Extensibility/CqlApiExtendableExtensions.cs
deleted file mode 100644
index f7bef000e..000000000
--- a/Cql/CqlSdkPrototype/Cql/Extensibility/CqlApiExtendableExtensions.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace CqlSdkPrototype.Cql.Extensibility;
-
-public static class CqlApiExtendableExtensions
-{
- public static ICqlApiExtendable AsExtendable(this CqlApi self) => self;
-}
\ No newline at end of file
diff --git a/Cql/CqlSdkPrototype/Cql/Extensibility/CqlApiStateEntry.cs b/Cql/CqlSdkPrototype/Cql/Extensibility/CqlApiStateEntry.cs
deleted file mode 100644
index 41b857cd7..000000000
--- a/Cql/CqlSdkPrototype/Cql/Extensibility/CqlApiStateEntry.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using CqlSdkPrototype.Infrastructure;
-using Hl7.Cql.CqlToElm;
-using Hl7.Cql.Elm;
-
-namespace CqlSdkPrototype.Cql.Extensibility;
-
-public readonly record struct CqlApiStateEntry(CqlLibraryString CqlLibraryString, Library? ElmLibrary = null) {
- internal LibraryBuilder? ElmLibraryBuilder { get; init; }
-}
\ No newline at end of file
diff --git a/Cql/CqlSdkPrototype/Cql/Extensibility/ICqlApiExtendable.cs b/Cql/CqlSdkPrototype/Cql/Extensibility/ICqlApiExtendable.cs
deleted file mode 100644
index fd4f83f14..000000000
--- a/Cql/CqlSdkPrototype/Cql/Extensibility/ICqlApiExtendable.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using CqlSdkPrototype.Infrastructure;
-
-namespace CqlSdkPrototype.Cql.Extensibility;
-
-public interface ICqlApiExtendable
- where TCqlApi : ICqlApiExtendable
-{
- ILoggerFactory LoggerFactory { get; }
- CqlApiOptions Options { get; }
- IReadOnlyDictionary Entries { get; }
- TCqlApi WithOptions(Func replaceOptions);
- TCqlApi AddCqlLibraries(IEnumerable libraries);
- TCqlApi Translate();
-}
\ No newline at end of file
diff --git a/Cql/CqlSdkPrototype/Cql/Extensions/CqlApiExtensions.Adding.cs b/Cql/CqlSdkPrototype/Cql/Extensions/CqlApiExtensions.Adding.cs
deleted file mode 100644
index 7b323b3e5..000000000
--- a/Cql/CqlSdkPrototype/Cql/Extensions/CqlApiExtensions.Adding.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-using CqlSdkPrototype.Cql.Extensibility;
-using CqlSdkPrototype.Infrastructure;
-using CqlSdkPrototype.Internal;
-
-namespace CqlSdkPrototype.Cql.Extensions;
-
-public static partial class CqlApiExtensions
-{
- public static TCqlApi AddCqlLibraryString(
- this TCqlApi cqlApi,
- CqlLibraryString cqlLibrary)
- where TCqlApi : ICqlApiExtendable
- {
- return cqlApi.AddCqlLibraries([cqlLibrary]);
- }
-
- public static TCqlApi AddCqlLibrariesFromDirectory(
- this TCqlApi cqlApi,
- DirectoryInfo directory,
- EnumerationOptions? options = null,
- Func? filePredicate = null)
- where TCqlApi : ICqlApiExtendable
- {
- var files = directory.EnumerateFiles("*.cql", options ?? InternalConstants.DefaultEnumerationOptions);
- if (filePredicate is not null) files = files.Where(filePredicate);
- return cqlApi.AddCqlLibraryFiles(files);
- }
-
- public static TCqlApi AddCqlLibraryFiles(
- this TCqlApi cqlApi,
- IEnumerable files)
- where TCqlApi : ICqlApiExtendable
- {
- var logger = cqlApi.LoggerFactory.CreateLogger(typeof(CqlApiExtensions));
- var cqlLibraries = files
- .Select(f =>
- {
- logger.LogInformation("Loading library from file: {file}", f);
- var cqlContent = File.ReadAllText(f.FullName);
- var cqlLibrary = CqlLibraryString.Parse(cqlContent);
- return cqlLibrary;
- }); // Log errors
-
- return cqlApi.AddCqlLibraries(cqlLibraries);
- }
-}
\ No newline at end of file
diff --git a/Cql/CqlSdkPrototype/Cql/Extensions/CqlApiExtensions.cs b/Cql/CqlSdkPrototype/Cql/Extensions/CqlApiExtensions.cs
deleted file mode 100644
index e635ea282..000000000
--- a/Cql/CqlSdkPrototype/Cql/Extensions/CqlApiExtensions.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace CqlSdkPrototype.Cql.Extensions;
-
-public static partial class CqlApiExtensions
-{
- // This file is intentionally left blank - since it's the parent file for nested CqlApiExtensions.* files
-}
\ No newline at end of file
diff --git a/Cql/CqlSdkPrototype/Cql/Internal/CqlApiInternalExtensions.cs b/Cql/CqlSdkPrototype/Cql/Internal/CqlApiInternalExtensions.cs
deleted file mode 100644
index 6efe08946..000000000
--- a/Cql/CqlSdkPrototype/Cql/Internal/CqlApiInternalExtensions.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace CqlSdkPrototype.Cql.Internal;
-
-internal static class CqlApiInternalExtensions
-{
- internal static ICqlApiInternal AsInternal(this ICqlApiInternal self)
- where TCqlApi : ICqlApiInternal => self;
-}
\ No newline at end of file
diff --git a/Cql/CqlSdkPrototype/Cql/Internal/CqlApiServices.cs b/Cql/CqlSdkPrototype/Cql/Internal/CqlApiServices.cs
deleted file mode 100644
index 4395fb603..000000000
--- a/Cql/CqlSdkPrototype/Cql/Internal/CqlApiServices.cs
+++ /dev/null
@@ -1,81 +0,0 @@
-using CqlSdkPrototype.Internal;
-using CqlSdkPrototype.Logging.Internal;
-using Hl7.Cql.CqlToElm;
-using Hl7.Cql.Elm;
-using Hl7.Cql.Model;
-using Hl7.Cql.Runtime.Hosting;
-using ExpressionVisitor = Hl7.Cql.CqlToElm.Visitors.ExpressionVisitor;
-
-namespace CqlSdkPrototype.Cql.Internal;
-
-internal readonly record struct CqlApiServices(
- ILoggerFactory LoggerFactory,
- ServiceProvider ServiceProvider,
- CqlToElmConverter CqlToElmConverter,
- EntriesBuilderLibraryProvider LibraryProvider)
-{
- private static readonly (CqlModel CqlModel, ModelInfo ModelInfo)[] AllMappedModelsInOrder = [
- (CqlModel.ElmR1, Models.ElmR1),
- (CqlModel.Fhir401, Models.Fhir401)];
-
- public static CqlApiServices Create(ILoggerFactory loggerFactory, CqlApiOptions options, EntriesBuilderLibraryProvider libraryProvider)
- {
- var services = new ServiceCollection();
- services.AddExternalLogging(loggerFactory);
- AddCqlServices(services, options, libraryProvider);
- var serviceProvider = services.BuildServiceProvider(validateScopes: true);
- var cqlToElmConverter = serviceProvider.GetCqlToElmConverter();
-
- return new CqlApiServices(
- loggerFactory,
- serviceProvider,
- cqlToElmConverter,
- libraryProvider);
- }
-
- private static void AddCqlServices(
- IServiceCollection serviceCollection,
- CqlApiOptions options,
- ILibraryProvider libraryProvider)
- {
- SuppressCqlDebugAssertions();
-
- serviceCollection
- .AddCqlToElmServices()
- .AddCqlToElmModels(ConfigureModelProvider())
- .AddCqlToElmOptions(ConfigureCqlToElmOptions())
- .AddSingleton(libraryProvider)
- .AddCqlToElmMessaging();
- return;
-
- Action ConfigureCqlToElmOptions()
- {
- return options.ApplyToCqlToElmOptions;
- }
-
- Action ConfigureModelProvider()
- {
- var modelInfos = AllMappedModelsInOrder
- .SelectWhereNotNull(t => options.Models.Contains(t.CqlModel) ? t.ModelInfo : null)
- .Concat(options.ModelInfos);
- return modelProvider =>
- {
- foreach (var modelInfo in modelInfos)
- modelProvider.Add(modelInfo);
- };
- }
- }
-
- private static void SuppressCqlDebugAssertions()
- {
- // This is really annoying in debug mode
- ExpressionVisitor.EnableDebugAssertions = false;
- Library.EnableDebugAssertions = false;
- }
-
- public ILoggerFactory LoggerFactory { get; } = LoggerFactory;
- public ServiceProvider ServiceProvider { get; } = ServiceProvider!;
- public ILogger Logger { get; } = ServiceProvider.GetLogger();
- public CqlToElmConverter CqlToElmConverter { get; } = CqlToElmConverter!;
- public EntriesBuilderLibraryProvider LibraryProvider { get; } = LibraryProvider!;
-}
\ No newline at end of file
diff --git a/Cql/CqlSdkPrototype/Cql/Internal/CqlServicesOptions.cs b/Cql/CqlSdkPrototype/Cql/Internal/CqlServicesOptions.cs
deleted file mode 100644
index f4b61bf35..000000000
--- a/Cql/CqlSdkPrototype/Cql/Internal/CqlServicesOptions.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-using Hl7.Cql.Model;
-
-namespace CqlSdkPrototype.Cql.Internal;
-
-internal class CqlServicesOptions
-{
- public ModelInfo[] Models { get; set; } = [];
-}
\ No newline at end of file
diff --git a/Cql/CqlSdkPrototype/Cql/Internal/CqlToElmTranslatorServices.cs b/Cql/CqlSdkPrototype/Cql/Internal/CqlToElmTranslatorServices.cs
new file mode 100644
index 000000000..0fa6a1085
--- /dev/null
+++ b/Cql/CqlSdkPrototype/Cql/Internal/CqlToElmTranslatorServices.cs
@@ -0,0 +1,114 @@
+using CqlSdkPrototype.Internal;
+using Hl7.Cql.CqlToElm;
+using Hl7.Cql.Elm;
+using Hl7.Cql.Model;
+using ExpressionVisitor = Hl7.Cql.CqlToElm.Visitors.ExpressionVisitor;
+
+namespace CqlSdkPrototype.Cql.Internal;
+
+///
+/// Services for translating CQL to ELM used by the .
+///
+internal readonly record struct CqlToElmTranslatorServices(
+ ILoggerFactory LoggerFactory,
+ ServiceProvider ServiceProvider,
+ CqlToElmConverter CqlToElmConverter,
+ LibraryBuilderProvider LibraryBuilderProvider)
+{
+ private static readonly (CqlModel CqlModel, ModelInfo ModelInfo)[] AllMappedModelsInOrder = [
+ (CqlModel.ElmR1, Models.ElmR1),
+ (CqlModel.Fhir401, Models.Fhir401)];
+
+ ///
+ /// Creates an instance of .
+ ///
+ /// The logger factory.
+ /// The configuration for the translator.
+ /// The translation dictionary.
+ /// A new instance of .
+ public static CqlToElmTranslatorServices Create(
+ ILoggerFactory loggerFactory,
+ CqlToElmTranslatorConfig config,
+ CqlToElmTranslationDictionary translations)
+ {
+ var translationsBuilder = translations.ToBuilder();
+ var libraryBuilderProvider = new LibraryBuilderProvider(translationsBuilder);
+
+ var services = new ServiceCollection();
+ services.AddExternalLogging(loggerFactory);
+ AddCqlServices(services, config, libraryBuilderProvider);
+ var serviceProvider = services.BuildServiceProvider(validateScopes: true);
+
+ return ActivatorUtilities.CreateInstance(serviceProvider, serviceProvider, libraryBuilderProvider);
+ }
+
+ ///
+ /// Adds CQL services to the service collection.
+ ///
+ /// The service collection.
+ /// The configuration for the translator.
+ /// The library provider.
+ private static void AddCqlServices(
+ IServiceCollection serviceCollection,
+ CqlToElmTranslatorConfig config,
+ ILibraryProvider libraryProvider)
+ {
+ SuppressCqlDebugAssertions();
+
+ serviceCollection
+ .AddCqlToElmServices()
+ .AddCqlToElmModels(ConfigureModelProvider())
+ .AddCqlToElmOptions(ConfigureCqlToElmOptions())
+ .AddSingleton(libraryProvider)
+ .AddCqlToElmMessaging();
+ return;
+
+ Action ConfigureCqlToElmOptions()
+ {
+ return config.ApplyToCqlToElmOptions;
+ }
+
+ Action ConfigureModelProvider()
+ {
+ var modelInfos = AllMappedModelsInOrder
+ .SelectWhereNotNull(t => config.Models.Contains(t.CqlModel) ? t.ModelInfo : null)
+ .Concat(config.ModelInfos);
+ return modelProvider =>
+ {
+ foreach (var modelInfo in modelInfos)
+ modelProvider.Add(modelInfo);
+ };
+ }
+ }
+
+ ///
+ /// Suppresses CQL debug assertions.
+ ///
+ private static void SuppressCqlDebugAssertions()
+ {
+ // This is really annoying in debug mode
+ ExpressionVisitor.EnableDebugAssertions = false;
+ Library.EnableDebugAssertions = false;
+ }
+
+ ///
+ /// Gets the logger factory.
+ ///
+ public ILoggerFactory LoggerFactory { get; } = LoggerFactory;
+ ///
+ /// Gets the service provider.
+ ///
+ public ServiceProvider ServiceProvider { get; } = ServiceProvider!;
+ ///
+ /// Gets the logger for the CQL to ELM translator.
+ ///
+ public ILogger Logger { get; } = LoggerFactory.CreateLogger();
+ ///
+ /// Gets the CQL to ELM converter.
+ ///
+ public CqlToElmConverter CqlToElmConverter { get; } = CqlToElmConverter!;
+ ///
+ /// Gets the library builder provider.
+ ///
+ public LibraryBuilderProvider LibraryBuilderProvider { get; } = LibraryBuilderProvider!;
+}
\ No newline at end of file
diff --git a/Cql/CqlSdkPrototype/Cql/Internal/ICqlApiInternal.cs b/Cql/CqlSdkPrototype/Cql/Internal/ICqlApiInternal.cs
deleted file mode 100644
index 63f2c239d..000000000
--- a/Cql/CqlSdkPrototype/Cql/Internal/ICqlApiInternal.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using CqlSdkPrototype.Cql.Extensibility;
-
-namespace CqlSdkPrototype.Cql.Internal;
-
-///
-/// Used to gain internal access to the state of the CQL API.
-///
-internal interface ICqlApiInternal : ICqlApiExtendable
- where TCqlApi : ICqlApiExtendable
-{
- CqlApiServices Services { get; }
-}
\ No newline at end of file
diff --git a/Cql/CqlSdkPrototype/Cql/Internal/EntriesBuilderLibraryProvider.cs b/Cql/CqlSdkPrototype/Cql/Internal/LibraryBuilderProvider.cs
similarity index 51%
rename from Cql/CqlSdkPrototype/Cql/Internal/EntriesBuilderLibraryProvider.cs
rename to Cql/CqlSdkPrototype/Cql/Internal/LibraryBuilderProvider.cs
index d5777fc7b..6ecae10d0 100644
--- a/Cql/CqlSdkPrototype/Cql/Internal/EntriesBuilderLibraryProvider.cs
+++ b/Cql/CqlSdkPrototype/Cql/Internal/LibraryBuilderProvider.cs
@@ -3,10 +3,15 @@
namespace CqlSdkPrototype.Cql.Internal;
-internal class EntriesBuilderLibraryProvider(CqlApiStateEntryDictionary.Builder entriesBuilder)
+///
+/// Provides the implementation for
+/// which resolves a given a library name and version on a .
+///
+///
+internal sealed class LibraryBuilderProvider(CqlToElmTranslationDictionary.Builder translationsBuilder)
: ILibraryProvider
{
- public CqlApiStateEntryDictionary.Builder EntriesBuilder { get; set; } = entriesBuilder;
+ public CqlToElmTranslationDictionary.Builder TranslationsBuilder { get; set; } = translationsBuilder;
public bool TryResolveLibrary(
string libraryName,
@@ -21,7 +26,8 @@ public bool TryResolveLibrary(
CqlLibraryIdentifier.Parse(libraryName),
version is null ? null : CqlLibraryVersion.Parse(version));
- if (EntriesBuilder.TryGetValue(libVer, out var entry) && entry.ElmLibraryBuilder is { } lb)
+ if (TranslationsBuilder.TryGetValue(libVer, out var processItem)
+ && processItem.ElmLibraryBuilder is { } lb)
{
libraryBuilder = lb;
return true;
diff --git a/Cql/CqlSdkPrototype/CqlSdkPrototype.csproj b/Cql/CqlSdkPrototype/CqlSdkPrototype.csproj
index 51629cd31..3f9e208e9 100644
--- a/Cql/CqlSdkPrototype/CqlSdkPrototype.csproj
+++ b/Cql/CqlSdkPrototype/CqlSdkPrototype.csproj
@@ -3,7 +3,7 @@
- Exe
+ Library
net8.0
enable
enable
@@ -31,8 +31,4 @@
-
-
-
-
diff --git a/Cql/CqlSdkPrototype/Elm.Fluent/Extensions/FluentCqlToolkitExtensions.cs b/Cql/CqlSdkPrototype/Elm.Fluent/Extensions/FluentCqlToolkitExtensions.cs
new file mode 100644
index 000000000..acfaa72a8
--- /dev/null
+++ b/Cql/CqlSdkPrototype/Elm.Fluent/Extensions/FluentCqlToolkitExtensions.cs
@@ -0,0 +1,23 @@
+using CqlSdkPrototype.Cql.Fluent;
+
+namespace CqlSdkPrototype.Elm.Fluent.Extensions;
+
+public static class FluentCqlToolkitExtensions
+{
+ public static FluentElmToolkit ToFluentElmToolkit(
+ this FluentCqlToolkit cqlToolkit,
+ Func? configure = null)
+ {
+ var config = new ElmToAssemblyCompilerConfig(ProcessBatchItemExceptionHandling: cqlToolkit.Config.ProcessBatchItemExceptionHandling);
+ if (configure is not null) config = configure(config);
+ var elmToolkit = new FluentElmToolkit(cqlToolkit.LoggerFactory, config).AddElmFromFluentCqlToolkit(cqlToolkit);
+ return elmToolkit;
+ }
+
+ public static FluentElmToolkit CompileCqlToAssemblies(
+ this FluentCqlToolkit cqlToolkit) =>
+ cqlToolkit
+ .TranslateCqlToElm()
+ .ToFluentElmToolkit()
+ .CompileElmToAssemblies();
+}
diff --git a/Cql/CqlSdkPrototype/Elm.Fluent/Extensions/FluentElmToolkitExtensions.Adding.cs b/Cql/CqlSdkPrototype/Elm.Fluent/Extensions/FluentElmToolkitExtensions.Adding.cs
new file mode 100644
index 000000000..7214356d0
--- /dev/null
+++ b/Cql/CqlSdkPrototype/Elm.Fluent/Extensions/FluentElmToolkitExtensions.Adding.cs
@@ -0,0 +1,84 @@
+using CqlSdkPrototype.Cql.Fluent;
+using CqlSdkPrototype.Infrastructure;
+using CqlSdkPrototype.Internal;
+using Hl7.Cql.Elm;
+
+#pragma warning disable RS0027
+
+namespace CqlSdkPrototype.Elm.Fluent.Extensions;
+
+public static partial class FluentElmToolkitExtensions
+{
+ public static FluentElmToolkit AddElmFromFluentCqlToolkit(
+ this FluentElmToolkit elmToolkit,
+ FluentCqlToolkit cqlToolkit) =>
+ elmToolkit.AddElmLibraries(
+ from entry in cqlToolkit.CqlToElmTranslations
+ let elmLibrary = entry.Value.ElmLibrary
+ where elmLibrary is not null
+ select elmLibrary);
+
+ public static FluentElmToolkit AddElmFileInDirectory(
+ this FluentElmToolkit elmToolkit,
+ DirectoryInfo directory,
+ CqlVersionedLibraryIdentifier versionedLibraryIdentifier)
+ {
+ FileInfo file = new(Path.Combine(directory.FullName, $"{versionedLibraryIdentifier}.json"));
+ if (file.Exists)
+ {
+ return elmToolkit.AddElmFile(file);
+ }
+
+ if (versionedLibraryIdentifier.Version is null)
+ throw new FileNotFoundException($"Could not find file '{file.FullName}'.");
+
+ var logger = elmToolkit.LoggerFactory.CreateLogger(typeof(FluentElmToolkitExtensions));
+ logger.LogWarning("Could not load library from file with name and version, trying without version: {file}", file.FullName);
+ file = new FileInfo(Path.Combine(directory.FullName, $"{versionedLibraryIdentifier with { Version = null }}.json"));
+ return elmToolkit.AddElmFile(file);
+ }
+
+ public static FluentElmToolkit AddElmFiles(
+ this FluentElmToolkit elmToolkit,
+ IEnumerable files)
+ {
+ var logger = elmToolkit.LoggerFactory.CreateLogger(typeof(FluentElmToolkitExtensions));
+ var libraries = files
+ .Select(f =>
+ {
+ logger.LogInformation("Loading library from file: {file}", f);
+ var library = Library.LoadFromJson(f);
+ return library;
+ }); // Log errors
+ return elmToolkit.AddElmLibraries(libraries);
+ }
+
+ public static FluentElmToolkit AddElmFilesFromDirectory(
+ this FluentElmToolkit elmToolkit,
+ DirectoryInfo directory,
+ EnumerationOptions? options = null,
+ Func? filePredicate = null)
+ {
+ var files = directory.EnumerateFiles("*.json", options ?? InternalConstants.DefaultEnumerationOptions);
+ if (filePredicate is not null) files = files.Where(filePredicate);
+ return elmToolkit.AddElmFiles(files);
+ }
+
+ public static FluentElmToolkit AddElmFileWithDependencies(
+ this FluentElmToolkit elmToolkit,
+ FileInfo file,
+ EnumerationOptions? options) =>
+ throw new NotImplementedException();
+
+ public static FluentElmToolkit AddElmFileWithDependencies(
+ this FluentElmToolkit elmToolkit,
+ DirectoryInfo directory,
+ CqlVersionedLibraryIdentifier fileName,
+ EnumerationOptions? options) =>
+ throw new NotImplementedException();
+
+ public static FluentElmToolkit AddElmFile(
+ this FluentElmToolkit elmToolkit,
+ FileInfo file) =>
+ elmToolkit.AddElmFiles([file]);
+}
\ No newline at end of file
diff --git a/Cql/CqlSdkPrototype/Elm/Extensions/ElmApiExtensions.Saving.cs b/Cql/CqlSdkPrototype/Elm.Fluent/Extensions/FluentElmToolkitExtensions.Saving.cs
similarity index 65%
rename from Cql/CqlSdkPrototype/Elm/Extensions/ElmApiExtensions.Saving.cs
rename to Cql/CqlSdkPrototype/Elm.Fluent/Extensions/FluentElmToolkitExtensions.Saving.cs
index 938044116..f745469db 100644
--- a/Cql/CqlSdkPrototype/Elm/Extensions/ElmApiExtensions.Saving.cs
+++ b/Cql/CqlSdkPrototype/Elm.Fluent/Extensions/FluentElmToolkitExtensions.Saving.cs
@@ -1,21 +1,17 @@
-using CqlSdkPrototype.Elm.Extensibility;
+namespace CqlSdkPrototype.Elm.Fluent.Extensions;
-namespace CqlSdkPrototype.Elm.Extensions;
-
-public static partial class ElmApiExtensions
+public static partial class FluentElmToolkitExtensions
{
-
- public static TElmApi SaveCSharpFilesToDirectory(
- this TElmApi elmApi,
+ public static FluentElmToolkit SaveCSharpFilesToDirectory(
+ this FluentElmToolkit elmToolkit,
DirectoryInfo directory)
- where TElmApi : IElmApiExtendable
{
if (!directory.Exists)
directory.Create();
- var logger = elmApi.LoggerFactory.CreateLogger(typeof(ElmApiExtensions));
+ var logger = elmToolkit.LoggerFactory.CreateLogger(typeof(FluentElmToolkitExtensions));
- foreach (var (libraryName, (_, cSharpSourceCode, _, _)) in elmApi.Entries)
+ foreach (var (libraryName, (_, cSharpSourceCode, _, _)) in elmToolkit.ElmToAssemblyCompilations)
{
if (cSharpSourceCode is null)
continue;
@@ -25,20 +21,19 @@ public static TElmApi SaveCSharpFilesToDirectory(
logger.LogInformation("Saved C# source code to file: {file}", fileName);
}
- return elmApi;
+ return elmToolkit;
}
- public static TElmApi SaveAssemblyBinariesToDirectory(
- this TElmApi elmApi,
+ public static FluentElmToolkit SaveAssemblyBinariesToDirectory(
+ this FluentElmToolkit elmToolkit,
DirectoryInfo directory)
- where TElmApi : IElmApiExtendable
{
if (!directory.Exists)
directory.Create();
- var logger = elmApi.LoggerFactory.CreateLogger(typeof(ElmApiExtensions));
+ var logger = elmToolkit.LoggerFactory.CreateLogger(typeof(FluentElmToolkitExtensions));
- foreach (var (libraryName, (_, _, assemblyBytes, symbolsBytes)) in elmApi.Entries)
+ foreach (var (libraryName, (_, _, assemblyBytes, symbolsBytes)) in elmToolkit.ElmToAssemblyCompilations)
{
if (assemblyBytes is null)
continue;
@@ -55,7 +50,6 @@ public static TElmApi SaveAssemblyBinariesToDirectory(
}
}
- return elmApi;
+ return elmToolkit;
}
-
}
\ No newline at end of file
diff --git a/Cql/CqlSdkPrototype/Elm.Fluent/Extensions/FluentElmToolkitExtensions.cs b/Cql/CqlSdkPrototype/Elm.Fluent/Extensions/FluentElmToolkitExtensions.cs
new file mode 100644
index 000000000..3ba09be75
--- /dev/null
+++ b/Cql/CqlSdkPrototype/Elm.Fluent/Extensions/FluentElmToolkitExtensions.cs
@@ -0,0 +1,27 @@
+using Hl7.Cql.Abstractions.Exceptions;
+using Hl7.Cql.CodeGeneration.NET;
+
+namespace CqlSdkPrototype.Elm.Fluent.Extensions;
+
+public static partial class FluentElmToolkitExtensions
+{
+ public static FluentElmToolkit ConfigIgnoreExceptions(this FluentElmToolkit cqlToolkit, bool stopAfterFirstException = false) =>
+ cqlToolkit.Reconfigure(o => o with
+ {
+ ProcessBatchItemExceptionHandling = stopAfterFirstException
+ ? ProcessBatchItemExceptionHandling.IgnoreExceptionAndBreak
+ : ProcessBatchItemExceptionHandling.IgnoreExceptionAndContinue
+ });
+
+ public static FluentElmToolkit ConfigAssemblyDebugInformationToEmbedded(this FluentElmToolkit cqlToolkit) =>
+ cqlToolkit.Reconfigure(o => o with
+ {
+ AssemblyCompilerDebugInformationFormat = AssemblyCompilerDebugInformationFormat.Embedded
+ });
+
+ public static FluentElmToolkit ConfigAssemblyDebugInformationToPortablePdb(this FluentElmToolkit cqlToolkit) =>
+ cqlToolkit.Reconfigure(o => o with
+ {
+ AssemblyCompilerDebugInformationFormat = AssemblyCompilerDebugInformationFormat.PortablePdb
+ });
+}
\ No newline at end of file
diff --git a/Cql/CqlSdkPrototype/Elm.Fluent/FluentElmToolkit.cs b/Cql/CqlSdkPrototype/Elm.Fluent/FluentElmToolkit.cs
new file mode 100644
index 000000000..56682c3f7
--- /dev/null
+++ b/Cql/CqlSdkPrototype/Elm.Fluent/FluentElmToolkit.cs
@@ -0,0 +1,83 @@
+using Hl7.Cql.Elm;
+
+namespace CqlSdkPrototype.Elm.Fluent;
+
+///
+/// Provides a fluent interface for working with the ElmToAssemblyCompiler.
+///
+public sealed class FluentElmToolkit
+{
+ private readonly ElmToAssemblyCompiler elmToAssemblyCompiler;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The logger factory to use for logging.
+ /// The configuration settings for the compiler.
+ public FluentElmToolkit(
+ ILoggerFactory? loggerFactory = null,
+ ElmToAssemblyCompilerConfig? settings = null) : this(new ElmToAssemblyCompiler(loggerFactory, settings))
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class with the specified compiler.
+ ///
+ /// The compiler to use.
+ public FluentElmToolkit(ElmToAssemblyCompiler elmToAssemblyCompiler)
+ {
+ this.elmToAssemblyCompiler = elmToAssemblyCompiler;
+ }
+
+ ///
+ /// Gets the service provider used by tests.
+ ///
+ internal ServiceProvider ServiceProvider => elmToAssemblyCompiler.ServiceProvider;
+
+ ///
+ /// Gets the logger factory used by extensions.
+ ///
+ public ILoggerFactory LoggerFactory => elmToAssemblyCompiler.LoggerFactory;
+
+ ///
+ /// Gets the configuration for the compiler.
+ ///
+ public ElmToAssemblyCompilerConfig ProcessorConfig => elmToAssemblyCompiler.Config;
+
+ ///
+ /// Gets the dictionary of ELM to assembly compilations.
+ ///
+ public ElmToAssemblyCompilationReadOnlyDictionary ElmToAssemblyCompilations => elmToAssemblyCompiler.ElmToAssemblyCompilations;
+
+ ///
+ /// Reconfigures the compiler with the specified configuration.
+ ///
+ /// A function that takes the current configuration and returns a new configuration.
+ /// The current instance of .
+ public FluentElmToolkit Reconfigure(Func configure)
+ {
+ elmToAssemblyCompiler.Reconfigure(configure(ProcessorConfig));
+ return this;
+ }
+
+ ///
+ /// Adds ELM libraries to the compiler.
+ ///
+ /// The libraries to add.
+ /// The current instance of .
+ public FluentElmToolkit AddElmLibraries(IEnumerable libraries)
+ {
+ elmToAssemblyCompiler.AddElmLibraries(libraries);
+ return this;
+ }
+
+ ///
+ /// Compiles the ELM libraries into .NET assemblies.
+ ///
+ /// The current instance of .
+ public FluentElmToolkit CompileElmToAssemblies()
+ {
+ elmToAssemblyCompiler.CompileElmToAssemblies();
+ return this;
+ }
+}
diff --git a/Cql/CqlSdkPrototype/Elm/ElmApi.cs b/Cql/CqlSdkPrototype/Elm/ElmApi.cs
deleted file mode 100644
index 6ff3bdcc7..000000000
--- a/Cql/CqlSdkPrototype/Elm/ElmApi.cs
+++ /dev/null
@@ -1,236 +0,0 @@
-using CqlSdkPrototype.Elm.Extensibility;
-using CqlSdkPrototype.Elm.Internal;
-using CqlSdkPrototype.Infrastructure;
-using CqlSdkPrototype.Internal;
-using Hl7.Cql.Abstractions.Exceptions;
-using Hl7.Cql.CodeGeneration.NET;
-using Hl7.Cql.Compiler;
-using Hl7.Cql.Elm;
-using Hl7.Cql.Runtime;
-
-namespace CqlSdkPrototype.Elm;
-
-public class ElmApi :
- IElmApiExtendable,
- IElmApiInternal
-{
- public ElmApi(
- ILoggerFactory? loggerFactory = null,
- ElmApiOptions? options = null) : this(ElmApiState.Create(loggerFactory ?? NullLoggerFactory.Instance, options ?? ElmApiOptions.Default)) { }
-
- internal ElmApi(ElmApiState state) => _state = state; // Might make this public later. Used for testing now.
-
- #region State
-
- private ElmApiState _state;
-
- ElmApiOptions IElmApiExtendable.Options => _state.Options;
- IReadOnlyDictionary IElmApiExtendable.Entries => _state.Entries;
-
- private ElmApi WithEntries(
- ElmApiStateEntryDictionary entries)
- {
- _state = _state with { Entries = entries };
- return this;
- }
-
- public ElmApi WithOptions(
- Func replaceOptions)
- {
- var newOptions = replaceOptions(_state.Options);
- if (!ReferenceEquals(_state.Options, newOptions))
- _state = _state with { Options = newOptions };
- return this;
- }
-
- TResult IElmApiExtendable.UseLogger(Func, TResult> action) => action(this, _state.Logger);
- ElmApiState IElmApiInternal.State => _state;
- ILoggerFactory IElmApiExtendable.LoggerFactory => _state.LoggerFactory;
-
- #endregion
-
- #region Input (ELM Libraries)
-
- public ElmApi AddElmLibraries(IEnumerable libraries)
- {
- var entries = _state.Entries.ToBuilder();
- var hasChanged = false;
- foreach (var library in libraries)
- {
- var versionedIdentifier = CqlVersionedLibraryIdentifier.FromVersionedIdentifier(library.identifier);
-
- if (entries.TryGetValue(versionedIdentifier, out var existingVersionedIdentifier))
- {
- _state.Logger.LogInformation("Skipping replacing library {existingVersionedIdentifier} to compiler with new library: {versionedIdentifier}, ",
- existingVersionedIdentifier, versionedIdentifier);
- continue;
- }
-
- var libraryCompilation = new ElmApiStateEntry(library);
- entries.Add(versionedIdentifier, libraryCompilation);
- _state.Logger.LogInformation("Adding library to compiler: {versionedIdentifier}", versionedIdentifier);
- hasChanged = true;
- }
-
- return hasChanged
- ? WithEntries(entries: entries.ToImmutable())
- : this;
- }
-
- #endregion
-
- #region Processing
-
- public ElmApi Compile()
- {
- var entries = _state.Entries;
- if (entries.Values.All(predicate: lc => lc is { AssemblyBinary: not null }))
- return this;
-
- using var servicesScope = _state.CreateScopedState();
- var logger = _state.Logger;
- logger.LogInformation(message: "Compiling ELM into C# and .NET Binaries");
- var exceptionHandling = _state.Options.ProcessBatchItemExceptionHandling;
- var debugInformationFormat = _state.Options.AssemblyCompilerDebugInformationFormat;
- AssemblyCompiler assemblyCompiler = _state.AssemblyCompiler;
- LibrarySetCSharpCodeGenerator cSharpCodeProcessor = _state.LibrarySetCSharpCodeGenerator;
- LibrarySetExpressionBuilder librarySetExpressionBuilderScoped = servicesScope.LibrarySetExpressionBuilder;
- Library[] libraries = entries.Values.Select(selector: v => v.ElmLibrary).ToArray();
- LibrarySet librarySet = new LibrarySet(name: "", libraries: libraries);
-
- var removedLibraries = librarySet.RemoveLibrariesWithMissingDependencies();
- foreach (var (id, _) in removedLibraries)
- logger.LogWarning(message: "Removed library with missing dependencies: {id}", args: id);
-
- LogExceptionMessageAction log = logger.GetLogExceptionMessageAction(exceptionHandling);
-
- var librarySetDefinitions = BuildLibrarySetDefinitions(librarySetExpressionBuilderScoped, librarySet, logger, log, exceptionHandling);
-
- var cSharps = GenerateCSharp(cSharpCodeProcessor, librarySet, librarySetDefinitions, logger, log, exceptionHandling);
-
- var assemblyDatas = CompileAssemblies(assemblyCompiler, librarySet, cSharps, debugInformationFormat, logger, log, exceptionHandling);
-
- var entriesBuilder = entries.ToBuilder();
- var hasChanged = UpdateStateEntries(assemblyDatas, entriesBuilder, logger);
- return hasChanged ? WithEntries(entries: entriesBuilder.ToImmutable()) : this;
- }
-
- private static bool UpdateStateEntries(
- IEnumerable<(Library library, AssemblyDataWithSourceCode assemblyDataWithSourceCode)> assemblyDatas,
- ElmApiStateEntryDictionary.Builder entriesBuilder,
- ILogger logger)
- {
- bool hasChanged = false;
- foreach (var (library, (assemblyBinary, sourceCodePerName, debugSymbols)) in assemblyDatas)
- {
- var elmVersionedIdentifier = CqlVersionedLibraryIdentifier.FromVersionedIdentifier(library.identifier);
- var libraryCompilation = entriesBuilder[key: elmVersionedIdentifier];
- if (libraryCompilation.CSharpSourceCode is not null
- || libraryCompilation.AssemblyBinary is not null)
- {
- logger.LogInformation(message: "Library already compiled: {versionedIdentifier}", args: elmVersionedIdentifier);
- continue;
- }
-
- var cSharpSourceCode = sourceCodePerName!.Values.Single(); // We always expect a single source file
- libraryCompilation = libraryCompilation with
- {
- CSharpSourceCode = cSharpSourceCode,
- AssemblyBinary = assemblyBinary,
- DebugSymbolsBinary = debugSymbols,
- };
- entriesBuilder[key: elmVersionedIdentifier] = libraryCompilation;
- logger.LogInformation(message: "Library compiled: {versionedIdentifier}", args: elmVersionedIdentifier);
- hasChanged = true;
- }
-
- return hasChanged;
- }
-
- private static IEnumerable<(Library library, AssemblyDataWithSourceCode assemblyDataWithSourceCode)> CompileAssemblies(
- AssemblyCompiler assemblyCompiler,
- LibrarySet librarySet,
- IEnumerable<(Library library, string cSharp)> cSharps,
- AssemblyCompilerDebugInformationFormat debugInformationFormat,
- ILogger logger,
- LogExceptionMessageAction log,
- ProcessBatchItemExceptionHandling exceptionHandling)
- {
- var assemblyDatas = assemblyCompiler
- .CompileDeferred(librarySet, cSharps, debugInformationFormat)
- .Select(t =>
- {
- var libraryName = t.library.identifier;
- logger.LogInformation("Compiling assembly for library : {libraryName}", libraryName);
- return t;
- })
- .TryProcessEach(t => (t.library, assemblyDataWithSourceCode: t.generateAssemblyDataWithSourceCode()))
- .WithEachException(t =>
- {
- var libraryName = t.Input.library.identifier;
- log(t.Exception, "Error compiling assembly for library: {libraryName}", libraryName);
- })
- .HandleExceptions(exceptionHandling)
- //.ToArray()
- ;
- return assemblyDatas;
- }
-
- private static IEnumerable<(Library library, string cSharp)> GenerateCSharp(
- LibrarySetCSharpCodeGenerator cSharpCodeProcessor,
- LibrarySet librarySet,
- DefinitionDictionary librarySetDefinitions,
- ILogger logger,
- LogExceptionMessageAction log,
- ProcessBatchItemExceptionHandling exceptionHandling)
- {
- var cSharps = cSharpCodeProcessor
- .GenerateCSharpDeferred(librarySet, librarySetDefinitions)
- .Select(t =>
- {
- var libraryName = t.library.identifier;
- logger.LogInformation("Generating C# for library : {libraryName}", libraryName);
- return t;
- })
- .TryProcessEach(t => (t.library, cSharp: t.generateCSharp()))
- .WithEachException(t =>
- {
- var libraryName = t.Input.library.identifier;
- log(t.Exception, "Error generating C# for library : {libraryName}", libraryName);
- })
- .HandleExceptions(exceptionHandling);
- return cSharps;
- }
-
- private static DefinitionDictionary BuildLibrarySetDefinitions(
- LibrarySetExpressionBuilder librarySetExpressionBuilderScoped,
- LibrarySet librarySet,
- ILogger logger,
- LogExceptionMessageAction log,
- ProcessBatchItemExceptionHandling exceptionHandling)
- {
- var libraryDefinitions = librarySetExpressionBuilderScoped
- .ProcessLibrarySetDeferred(librarySet: librarySet)
- .Select(t =>
- {
- var libraryName = t.library.identifier;
- logger.LogInformation("Generating definitions for library : {libraryName}", libraryName);
- return t;
- })
- .TryProcessEach(t => (t.library, cSharp: t.generateLibraryDefinitions))
- .WithEachException(t =>
- {
- var libraryName = t.Input.library.identifier;
- log(t.Exception, "Error generating definitions for library : {libraryName}", libraryName);
- })
- .HandleExceptions(exceptionHandling);
-
- DefinitionDictionary librarySetDefinitions = new();
- foreach (var (_, generateLibraryDefinitions) in libraryDefinitions)
- librarySetDefinitions.Merge(generateLibraryDefinitions());
-
- return librarySetDefinitions;
- }
-
- #endregion
-}
\ No newline at end of file
diff --git a/Cql/CqlSdkPrototype/Elm/ElmApiOptions.cs b/Cql/CqlSdkPrototype/Elm/ElmApiOptions.cs
deleted file mode 100644
index c2ede1695..000000000
--- a/Cql/CqlSdkPrototype/Elm/ElmApiOptions.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using Hl7.Cql.Abstractions.Exceptions;
-using Hl7.Cql.CodeGeneration.NET;
-
-namespace CqlSdkPrototype.Elm;
-
-public record ElmApiOptions(
- ProcessBatchItemExceptionHandling ProcessBatchItemExceptionHandling = default,
- AssemblyCompilerDebugInformationFormat AssemblyCompilerDebugInformationFormat = AssemblyCompilerDebugInformationFormat.None)
-{
- public static ElmApiOptions Default { get; } = new();
-}
\ No newline at end of file
diff --git a/Cql/CqlSdkPrototype/Elm/ElmApiState.cs b/Cql/CqlSdkPrototype/Elm/ElmApiState.cs
deleted file mode 100644
index b7cbdf3e2..000000000
--- a/Cql/CqlSdkPrototype/Elm/ElmApiState.cs
+++ /dev/null
@@ -1,99 +0,0 @@
-using CqlSdkPrototype.Elm.Extensibility;
-using CqlSdkPrototype.Infrastructure;
-using CqlSdkPrototype.Logging.Internal;
-using Hl7.Cql.Abstractions;
-using Hl7.Cql.CodeGeneration.NET;
-using Hl7.Cql.Compiler;
-using Hl7.Cql.Conversion;
-using Hl7.Cql.Fhir;
-using Hl7.Cql.Runtime.Hosting;
-using Hl7.Fhir.Introspection;
-
-namespace CqlSdkPrototype.Elm;
-
-internal readonly record struct ElmApiState(
- ILoggerFactory LoggerFactory,
- ImmutableDictionary Entries,
- ElmApiOptions Options,
- ServiceProvider ServiceProvider,
- ILogger Logger,
- AssemblyCompiler AssemblyCompiler,
- LibrarySetCSharpCodeGenerator LibrarySetCSharpCodeGenerator)
-{
- public static ElmApiState Create(
- ILoggerFactory loggerFactory,
- ElmApiOptions options)
- {
- var entries = ImmutableDictionary.Empty.WithComparers(CqlVersionedLibraryIdentifier.IdentifierOnlyEqualityComparer);
- return new ElmApiState(loggerFactory, entries, null!, null!, null!, null!, null!)
- {
- // Must be set through the property initializer, to ensure the services are created
- Options = options
- };
- }
-
- private readonly ElmApiOptions _options = Options;
-
- public ElmApiOptions Options
- {
- get => _options;
- init
- {
- if (ReferenceEquals(_options, value))
- return;
-
- ServiceProvider?.Dispose();
-
- _options = value;
-
- var services = new ServiceCollection();
- services.AddExternalLogging(LoggerFactory!);
- AddCqlCodeGenerationServices(services);
- ServiceProvider = services.BuildServiceProvider(validateScopes:true);
- Logger = ServiceProvider.GetLogger();
- AssemblyCompiler = ServiceProvider.GetRequiredService();
- LibrarySetCSharpCodeGenerator = ServiceProvider.GetRequiredService();
- }
- }
-
- public ILoggerFactory LoggerFactory { get; } = LoggerFactory;
-
- public ElmApiScopedState CreateScopedState() => new(ServiceProvider.CreateScope());
-
- private static void AddCqlCodeGenerationServices(IServiceCollection services)
- {
- AddCqlCompilerServices(services);
- services.TryAddSingleton();
- services.TryAddSingleton();
- services.TryAddSingleton();
- }
-
- public static IServiceCollection AddCqlCompilerServices(IServiceCollection services)
- {
- services.TryAddSingleton(_ => Hl7.Fhir.Model.ModelInfo.ModelInspector);
- services.TryAddSingleton();
-
- const int cacheSize = 0; // TODO: Must move to configuration
- services.TryAddSingleton(sp =>
- {
- var modelInspector = sp.GetRequiredService();
- var logger = sp.GetLogger();
- var converter = FhirTypeConverter
- .Create(modelInspector, cacheSize)
- .UseLogger(logger);
- converter.CaptureAvailableConverters();
- return converter;
- });
-
- services.TryAddSingleton();
- services.TryAddSingleton();
- services.TryAddSingleton(_ => ExpressionBuilderSettings.Default); // TODO: Must move to configuration
- services.TryAddScoped();
- services.TryAddScoped();
- services.TryAddScoped();
- services.TryAddScoped();
-
- return services;
- }
-
-}
\ No newline at end of file
diff --git a/Cql/CqlSdkPrototype/Elm/ElmToAssemblyCompilation.cs b/Cql/CqlSdkPrototype/Elm/ElmToAssemblyCompilation.cs
new file mode 100644
index 000000000..f94904691
--- /dev/null
+++ b/Cql/CqlSdkPrototype/Elm/ElmToAssemblyCompilation.cs
@@ -0,0 +1,21 @@
+using Hl7.Cql.CodeGeneration.NET;
+using Hl7.Cql.Elm;
+
+namespace CqlSdkPrototype.Elm;
+
+///
+/// Represents the compilation result of an ELM library to an assembly.
+///
+/// The input ELM library being compiled.
+/// The output generated C# source code from the ELM library.
+/// The output compiled assembly binary.
+///
+/// The output debug symbols binary for the compiled assembly.
+/// This is only emitted if
+/// is set to .
+///
+public readonly record struct ElmToAssemblyCompilation(
+ Library ElmLibrary,
+ string? CSharpSourceCode = null,
+ byte[]? AssemblyBinary = null,
+ byte[]? DebugSymbolsBinary = null);
\ No newline at end of file
diff --git a/Cql/CqlSdkPrototype/Elm/ElmToAssemblyCompiler.cs b/Cql/CqlSdkPrototype/Elm/ElmToAssemblyCompiler.cs
new file mode 100644
index 000000000..40ca8ec67
--- /dev/null
+++ b/Cql/CqlSdkPrototype/Elm/ElmToAssemblyCompiler.cs
@@ -0,0 +1,299 @@
+using CqlSdkPrototype.Elm.Fluent;
+using CqlSdkPrototype.Elm.Internal;
+using CqlSdkPrototype.Infrastructure;
+using CqlSdkPrototype.Internal;
+using Hl7.Cql.Abstractions.Exceptions;
+using Hl7.Cql.CodeGeneration.NET;
+using Hl7.Cql.Compiler;
+using Hl7.Cql.Elm;
+using Hl7.Cql.Runtime;
+
+namespace CqlSdkPrototype.Elm;
+
+///
+/// Compiles ELM (Expression Logical Model) into .NET assemblies.
+///
+public sealed class ElmToAssemblyCompiler
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The logger factory to use for logging.
+ /// The configuration for the compiler.
+ public ElmToAssemblyCompiler(
+ ILoggerFactory? loggerFactory = null,
+ ElmToAssemblyCompilerConfig? config = null)
+ {
+ config ??= ElmToAssemblyCompilerConfig.Default;
+ loggerFactory ??= NullLoggerFactory.Instance;
+ LoggerFactory = loggerFactory;
+ _elmToAssemblyCompilations = ElmToAssemblyCompilationDictionary.Empty;
+ Config = config;
+ _services = ElmToAssemblyProcessorServices.Create(loggerFactory, config);
+ }
+
+ private ElmToAssemblyCompilationDictionary _elmToAssemblyCompilations;
+ private ElmToAssemblyProcessorServices _services;
+
+ ///
+ /// Gets the logger factory used by extensions via .
+ ///
+ internal ILoggerFactory LoggerFactory { get; }
+
+ ///
+ /// Gets the service provider used by tests via .
+ ///
+ internal ServiceProvider ServiceProvider => _services.ServiceProvider;
+
+ ///
+ /// Gets the configuration for the compiler.
+ ///
+ public ElmToAssemblyCompilerConfig Config { get; private set; }
+
+ ///
+ /// Gets the dictionary of ELM to assembly compilations.
+ ///
+ public ElmToAssemblyCompilationReadOnlyDictionary ElmToAssemblyCompilations => _elmToAssemblyCompilations;
+
+ ///
+ /// Sets the conversions for the ELM to assembly compilations.
+ ///
+ /// The dictionary of ELM to assembly compilations.
+ private void SetConversions(
+ ElmToAssemblyCompilationDictionary elmToAssemblyCompilations)
+ {
+ _elmToAssemblyCompilations = elmToAssemblyCompilations;
+ }
+
+ ///
+ /// Reconfigures the compiler with the specified configuration.
+ ///
+ /// The new configuration for the compiler.
+ public void Reconfigure(
+ ElmToAssemblyCompilerConfig config)
+ {
+ if (Config == config)
+ return;
+
+ _services.ServiceProvider.Dispose();
+ Config = config;
+ _services = ElmToAssemblyProcessorServices.Create(LoggerFactory, config);
+ }
+
+ ///
+ /// Adds ELM libraries to the compiler.
+ ///
+ /// The libraries to add.
+ public void AddElmLibraries(IEnumerable libraries)
+ {
+ var entries = _elmToAssemblyCompilations.ToBuilder();
+ var hasChanged = false;
+ foreach (var library in libraries)
+ {
+ var versionedIdentifier = CqlVersionedLibraryIdentifier.FromVersionedIdentifier(library.identifier);
+
+ if (entries.TryGetValue(versionedIdentifier, out var existingVersionedIdentifier))
+ {
+ _services.Logger.LogInformation(
+ "Skipping replacing library {existingVersionedIdentifier} to compiler with new library: {versionedIdentifier}, ",
+ existingVersionedIdentifier, versionedIdentifier);
+ continue;
+ }
+
+ var libraryCompilation = new ElmToAssemblyCompilation(library);
+ entries.Add(versionedIdentifier, libraryCompilation);
+ _services.Logger.LogInformation("Adding library to compiler: {versionedIdentifier}", versionedIdentifier);
+ hasChanged = true;
+ }
+
+ if (hasChanged)
+ SetConversions(elmToAssemblyCompilations: entries.ToImmutable());
+ }
+
+ ///
+ /// Compiles the ELM libraries into .NET assemblies.
+ ///
+ public void CompileElmToAssemblies()
+ {
+ var entries = _elmToAssemblyCompilations;
+ if (entries.Values.All(predicate: lc => lc is { AssemblyBinary: not null })) return;
+
+ using var servicesScope = _services.CreateScopedState();
+ var logger = _services.Logger;
+ logger.LogInformation(message: "Compiling ELM into C# and .NET Binaries");
+ var exceptionHandling = Config.ProcessBatchItemExceptionHandling;
+ var debugInformationFormat = Config.AssemblyCompilerDebugInformationFormat;
+ AssemblyCompiler assemblyCompiler = _services.AssemblyCompiler;
+ LibrarySetCSharpCodeGenerator cSharpCodeProcessor = _services.LibrarySetCSharpCodeGenerator;
+ LibrarySetExpressionBuilder librarySetExpressionBuilderScoped = servicesScope.LibrarySetExpressionBuilder;
+ Library[] libraries = entries.Values.Select(selector: v => v.ElmLibrary).ToArray();
+ LibrarySet librarySet = new LibrarySet(name: "", libraries: libraries);
+
+ var removedLibraries = librarySet.RemoveLibrariesWithMissingDependencies();
+ foreach (var (id, _) in removedLibraries)
+ logger.LogWarning(message: "Removed library with missing dependencies: {id}", args: id);
+
+ LogExceptionMessageAction log = logger.GetLogExceptionMessageAction(exceptionHandling);
+
+ var librarySetDefinitions = BuildLibrarySetDefinitions(librarySetExpressionBuilderScoped, librarySet, logger, log, exceptionHandling);
+
+ var cSharps = GenerateCSharp(cSharpCodeProcessor, librarySet, librarySetDefinitions, logger, log, exceptionHandling);
+
+ var assemblyBinaries = CompileAssemblies(assemblyCompiler, librarySet, cSharps, debugInformationFormat, logger, log, exceptionHandling);
+
+ var entriesBuilder = entries.ToBuilder();
+ var hasChanged = UpdateStateEntries(assemblyBinaries, entriesBuilder, logger);
+ if (hasChanged)
+ SetConversions(elmToAssemblyCompilations: entriesBuilder.ToImmutable());
+ }
+
+ ///
+ /// Updates the state entries with the compiled assemblies.
+ ///
+ /// The compiled assemblies.
+ /// The builder for the entries dictionary.
+ /// The logger to use for logging.
+ /// if the state entries were updated; otherwise, .
+ private static bool UpdateStateEntries(
+ IEnumerable<(Library library, AssemblyBinaryWithSourceCode assemblyBinaryWithSourceCode)> assemblyBinaries,
+ ElmToAssemblyCompilationDictionary.Builder entriesBuilder,
+ ILogger logger)
+ {
+ bool hasChanged = false;
+ foreach (var (library, (assemblyBinary, sourceCodePerName, debugSymbols)) in assemblyBinaries)
+ {
+ var elmVersionedIdentifier = CqlVersionedLibraryIdentifier.FromVersionedIdentifier(library.identifier);
+ var libraryCompilation = entriesBuilder[key: elmVersionedIdentifier];
+ if (libraryCompilation.CSharpSourceCode is not null
+ || libraryCompilation.AssemblyBinary is not null)
+ {
+ logger.LogInformation(message: "Library already compiled: {versionedIdentifier}", args: elmVersionedIdentifier);
+ continue;
+ }
+
+ var cSharpSourceCode = sourceCodePerName!.Values.Single(); // We always expect a single source file
+ libraryCompilation = libraryCompilation with
+ {
+ CSharpSourceCode = cSharpSourceCode,
+ AssemblyBinary = assemblyBinary,
+ DebugSymbolsBinary = debugSymbols,
+ };
+ entriesBuilder[key: elmVersionedIdentifier] = libraryCompilation;
+ logger.LogInformation(message: "Library compiled: {versionedIdentifier}", args: elmVersionedIdentifier);
+ hasChanged = true;
+ }
+
+ return hasChanged;
+ }
+
+ ///
+ /// Compiles the C# code into .NET assemblies.
+ ///
+ /// The assembly compiler to use.
+ /// The set of libraries to compile.
+ /// The C# code to compile.
+ /// The format for debug information.
+ /// The logger to use for logging.
+ /// The action to log exceptions.
+ /// The exception handling strategy.
+ /// The compiled assemblies.
+ private static IEnumerable<(Library library, AssemblyBinaryWithSourceCode assemblyBinaryWithSourceCode)> CompileAssemblies(
+ AssemblyCompiler assemblyCompiler,
+ LibrarySet librarySet,
+ IEnumerable<(Library library, string cSharp)> cSharps,
+ AssemblyCompilerDebugInformationFormat debugInformationFormat,
+ ILogger logger,
+ LogExceptionMessageAction log,
+ ProcessBatchItemExceptionHandling exceptionHandling)
+ {
+ var assemblyBinaries = assemblyCompiler
+ .CompileDeferred(librarySet, cSharps, debugInformationFormat)
+ .Select(t =>
+ {
+ var libraryName = t.library.identifier;
+ logger.LogInformation("Compiling assembly for library : {libraryName}", libraryName);
+ return t;
+ })
+ .TryProcessEach(t => (t.library, assemblyBinaryWithSourceCode: t.generateAssemblyBinaryWithSourceCode()))
+ .WithEachException(t =>
+ {
+ var libraryName = t.Input.library.identifier;
+ log(t.Exception, "Error compiling assembly for library: {libraryName}", libraryName);
+ })
+ .HandleExceptions(exceptionHandling);
+ return assemblyBinaries;
+ }
+
+ ///
+ /// Generates the C# code for the libraries.
+ ///
+ /// The C# code processor to use.
+ /// The set of libraries to generate code for.
+ /// The definitions for the library set.
+ /// The logger to use for logging.
+ /// The action to log exceptions.
+ /// The exception handling strategy.
+ /// The generated C# code.
+ private static IEnumerable<(Library library, string cSharp)> GenerateCSharp(
+ LibrarySetCSharpCodeGenerator cSharpCodeProcessor,
+ LibrarySet librarySet,
+ DefinitionDictionary librarySetDefinitions,
+ ILogger logger,
+ LogExceptionMessageAction log,
+ ProcessBatchItemExceptionHandling exceptionHandling)
+ {
+ var cSharps = cSharpCodeProcessor
+ .GenerateCSharpDeferred(librarySet, librarySetDefinitions)
+ .Select(t =>
+ {
+ var libraryName = t.library.identifier;
+ logger.LogInformation("Generating C# for library : {libraryName}", libraryName);
+ return t;
+ })
+ .TryProcessEach(t => (t.library, cSharp: t.generateCSharp()))
+ .WithEachException(t =>
+ {
+ var libraryName = t.Input.library.identifier;
+ log(t.Exception, "Error generating C# for library : {libraryName}", libraryName);
+ })
+ .HandleExceptions(exceptionHandling);
+ return cSharps;
+ }
+
+ ///
+ /// Builds the library set definitions.
+ ///
+ /// The library set expression builder to use.
+ /// The set of libraries to build definitions for.
+ /// The logger to use for logging.
+ /// The action to log exceptions.
+ /// The exception handling strategy.
+ /// The dictionary of library set definitions.
+ private static DefinitionDictionary BuildLibrarySetDefinitions(
+ LibrarySetExpressionBuilder librarySetExpressionBuilderScoped,
+ LibrarySet librarySet,
+ ILogger logger,
+ LogExceptionMessageAction log,
+ ProcessBatchItemExceptionHandling exceptionHandling)
+ {
+ DefinitionDictionary librarySetDefinitions = new();
+ foreach (var (_, libraryDefinitions) in
+ librarySetExpressionBuilderScoped
+ .ProcessLibrarySetDeferred(librarySet: librarySet)
+ .Select(t =>
+ {
+ var libraryName = t.library.identifier;
+ logger.LogInformation("Generating definitions for library : {libraryName}", libraryName);
+ return t;
+ })
+ .TryProcessEach(t => (t.library, cSharp: t.generateLibraryDefinitions()))
+ .WithEachException(t =>
+ {
+ var libraryName = t.Input.library.identifier;
+ log(t.Exception, "Error generating definitions for library : {libraryName}", libraryName);
+ })
+ .HandleExceptions(exceptionHandling))
+ librarySetDefinitions.Merge(libraryDefinitions);
+ return librarySetDefinitions;
+ }
+}
diff --git a/Cql/CqlSdkPrototype/Elm/ElmToAssemblyCompilerConfig.cs b/Cql/CqlSdkPrototype/Elm/ElmToAssemblyCompilerConfig.cs
new file mode 100644
index 000000000..9e49adcac
--- /dev/null
+++ b/Cql/CqlSdkPrototype/Elm/ElmToAssemblyCompilerConfig.cs
@@ -0,0 +1,78 @@
+using Hl7.Cql.Abstractions.Exceptions;
+using Hl7.Cql.CodeGeneration.NET;
+using Hl7.Cql.Compiler;
+
+namespace CqlSdkPrototype.Elm;
+
+///
+/// Configuration settings for the Elm to Assembly compiler.
+///
+/// The exception handling policy to use while processing a batch of items.
+/// The format of the debug information emitted by the compiler.
+/// Allows a child scope to redefine an existing parent scope. Default is .
+///
+/// When , functions declared external will throw at runtime
+/// if they are not found in customImplementations. When , an
+/// will be thrown during compilation if they are not found in customImplementations.
+/// The default value is .
+///
+/// The size of the Least Recently Used (LRU) cache.
+public sealed record ElmToAssemblyCompilerConfig(
+ ProcessBatchItemExceptionHandling ProcessBatchItemExceptionHandling = default,
+ AssemblyCompilerDebugInformationFormat AssemblyCompilerDebugInformationFormat = AssemblyCompilerDebugInformationFormat.None,
+ bool AllowScopeRedefinition = true,
+ bool AllowUnresolvedExternals = true,
+ int LRUCacheSize = 0)
+{
+ ///
+ /// Gets the default configuration settings.
+ ///
+ public static ElmToAssemblyCompilerConfig Default { get; } = new();
+
+ ///
+ /// Allows a child scope to redefine an existing parent scope. Default is .
+ ///
+ ///
+ /// For example, consider this query:
+ ///
+ /// IinCC X
+ /// return
+ /// (
+ /// Tuple
+ /// {
+ /// sdate: start of X,
+ /// edate:
+ /// end of X,
+ /// ndate:
+ /// end of X + System.Quantity { value: DDiff, unit: 'day' }
+ /// }
+ /// ) X
+ /// return Tuple
+ /// {
+ /// CInterval: Interval[X.sdate, X.edate],
+ /// RInterval: Interval[X.sdate, X.ndate]
+ /// }
+ ///
+ /// Here, X is being used twice, because the source is a subquery. X is used in the subquery and is also used
+ /// in the main query. ELM does not limit the scope of the first X to only the subquery as it probably should.
+ ///
+ public bool AllowScopeRedefinition { get; init; } = AllowScopeRedefinition;
+
+ ///
+ /// When , functions declared external will throw at runtime
+ /// if they are not found in customImplementations. When , an
+ /// will be thrown during compilation if they are not found in customImplementations.
+ /// The default value is .
+ ///
+ public bool AllowUnresolvedExternals { get; init; } = AllowUnresolvedExternals;
+
+ ///
+ /// Converts the current configuration settings to .
+ ///
+ /// An instance of with the current configuration settings.
+ internal ExpressionBuilderSettings ToExpressionBuilderSettings() => new()
+ {
+ AllowScopeRedefinition = AllowScopeRedefinition,
+ AllowUnresolvedExternals = AllowUnresolvedExternals
+ };
+}
diff --git a/Cql/CqlSdkPrototype/Elm/Extensibility/ElmApiExtendableExtensions.cs b/Cql/CqlSdkPrototype/Elm/Extensibility/ElmApiExtendableExtensions.cs
deleted file mode 100644
index 2487a6c02..000000000
--- a/Cql/CqlSdkPrototype/Elm/Extensibility/ElmApiExtendableExtensions.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace CqlSdkPrototype.Elm.Extensibility;
-
-public static class ElmApiExtendableExtensions
-{
- public static IElmApiExtendable AsExtendable(this ElmApi self) => self;
-}
\ No newline at end of file
diff --git a/Cql/CqlSdkPrototype/Elm/Extensibility/ElmApiStateEntry.cs b/Cql/CqlSdkPrototype/Elm/Extensibility/ElmApiStateEntry.cs
deleted file mode 100644
index 53c85fa6a..000000000
--- a/Cql/CqlSdkPrototype/Elm/Extensibility/ElmApiStateEntry.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-using Hl7.Cql.Elm;
-
-namespace CqlSdkPrototype.Elm.Extensibility;
-
-public readonly record struct ElmApiStateEntry
- (Library ElmLibrary, string? CSharpSourceCode = null, byte[]? AssemblyBinary = null, byte[]? DebugSymbolsBinary = null);
\ No newline at end of file
diff --git a/Cql/CqlSdkPrototype/Elm/Extensibility/IElmApiExtendable.cs b/Cql/CqlSdkPrototype/Elm/Extensibility/IElmApiExtendable.cs
deleted file mode 100644
index 35fd1f0fd..000000000
--- a/Cql/CqlSdkPrototype/Elm/Extensibility/IElmApiExtendable.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using CqlSdkPrototype.Infrastructure;
-using Hl7.Cql.Elm;
-
-namespace CqlSdkPrototype.Elm.Extensibility;
-
-public interface IElmApiExtendable
- where TElmApi : IElmApiExtendable
-{
- ILoggerFactory LoggerFactory { get; }
- ElmApiOptions Options { get; }
- IReadOnlyDictionary Entries { get; }
- TElmApi WithOptions(Func replaceOptions);
- TElmApi AddElmLibraries(IEnumerable libraries);
- TElmApi Compile();
- TResult UseLogger(Func, TResult> action); // Useful for extensions methods
-}
\ No newline at end of file
diff --git a/Cql/CqlSdkPrototype/Elm/Extensions/ElmApiExtensions.Adding.cs b/Cql/CqlSdkPrototype/Elm/Extensions/ElmApiExtensions.Adding.cs
deleted file mode 100644
index ac12e0c61..000000000
--- a/Cql/CqlSdkPrototype/Elm/Extensions/ElmApiExtensions.Adding.cs
+++ /dev/null
@@ -1,103 +0,0 @@
-using CqlSdkPrototype.Cql.Extensibility;
-using CqlSdkPrototype.Elm.Extensibility;
-using CqlSdkPrototype.Infrastructure;
-using CqlSdkPrototype.Internal;
-using Hl7.Cql.Elm;
-
-#pragma warning disable RS0027
-
-namespace CqlSdkPrototype.Elm.Extensions;
-
-public static partial class ElmApiExtensions
-{
- public static TElmApi AddElmFromCqlApi(
- this TElmApi elmApi,
- TCqlApi cqlApi)
- where TElmApi : IElmApiExtendable
- where TCqlApi : ICqlApiExtendable
- {
- return elmApi.AddElmLibraries(
- from entry in cqlApi.Entries
- let elmLibrary = entry.Value.ElmLibrary
- where elmLibrary is not null
- select elmLibrary);
- }
-
- public static TElmApi AddElmFile(
- this TElmApi elmApi,
- DirectoryInfo directory,
- CqlVersionedLibraryIdentifier versionedLibraryIdentifier)
- where TElmApi : IElmApiExtendable
- {
- FileInfo file = new(Path.Combine(directory.FullName, $"{versionedLibraryIdentifier}.json"));
- if (file.Exists)
- return elmApi.AddElmFile(file);
-
- if (versionedLibraryIdentifier.Version is null)
- throw new FileNotFoundException($"Could not find file '{file.FullName}'.");
-
- return elmApi.UseLogger((elmApi, logger) =>
- {
- logger.LogWarning("Could not load library from file with name and version, trying without version: {file}", file.FullName);
- file = new FileInfo(Path.Combine(directory.FullName, $"{versionedLibraryIdentifier with { Version = null }}.json"));
- return elmApi.AddElmFile(file);
- });
- }
-
- public static TElmApi AddElmFiles(
- this TElmApi elmApi,
- IEnumerable files)
- where TElmApi : IElmApiExtendable
- {
- var logger = elmApi.LoggerFactory.CreateLogger(typeof(ElmApiExtensions));
- var libraries = files
- .Select(f =>
- {
- logger.LogInformation("Loading library from file: {file}", f);
- var library = Library.LoadFromJson(f);
- return library;
- }); // Log errors
- return elmApi.AddElmLibraries(libraries);
- }
-
- public static TElmApi AddElmFilesFromDirectory(
- this TElmApi elmApi,
- DirectoryInfo directory,
- EnumerationOptions? options = null,
- Func? filePredicate = null)
- where TElmApi : IElmApiExtendable
- {
- var files = directory.EnumerateFiles("*.json", options ?? InternalConstants.DefaultEnumerationOptions);
- if (filePredicate is not null) files = files.Where(filePredicate);
- return elmApi.AddElmFiles(files);
- }
-
- public static TElmApi AddElmFileWithDependencies(
- this TElmApi elmApi,
- FileInfo file,
- EnumerationOptions? options)
- where TElmApi : IElmApiExtendable
- {
- // TODO
- return elmApi;
- }
-
- public static TElmApi AddElmFileWithDependencies(
- this TElmApi elmApi,
- DirectoryInfo directory,
- CqlVersionedLibraryIdentifier fileName,
- EnumerationOptions? options)
- where TElmApi : IElmApiExtendable
- {
- // TODO
- return elmApi;
- }
-
- public static TElmApi AddElmFile(
- this TElmApi elmApi,
- FileInfo file)
- where TElmApi : IElmApiExtendable
- {
- return elmApi.AddElmFiles([file]);
- }
-}
\ No newline at end of file
diff --git a/Cql/CqlSdkPrototype/Elm/Extensions/ElmApiExtensions.Cql.cs b/Cql/CqlSdkPrototype/Elm/Extensions/ElmApiExtensions.Cql.cs
deleted file mode 100644
index baa91288b..000000000
--- a/Cql/CqlSdkPrototype/Elm/Extensions/ElmApiExtensions.Cql.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-using CqlSdkPrototype.Cql;
-using CqlSdkPrototype.Runtime.Extensions;
-
-namespace CqlSdkPrototype.Elm.Extensions;
-
-public static partial class ElmApiExtensions
-{
- public static ElmApi Compile(
- this CqlApi cqlApi)
- {
- return cqlApi
- .Translate()
- .CreateElmApi()
- .Compile();
- }
-
-}
diff --git a/Cql/CqlSdkPrototype/Elm/Extensions/ElmApiExtensions.cs b/Cql/CqlSdkPrototype/Elm/Extensions/ElmApiExtensions.cs
deleted file mode 100644
index fc12f32df..000000000
--- a/Cql/CqlSdkPrototype/Elm/Extensions/ElmApiExtensions.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace CqlSdkPrototype.Elm.Extensions;
-
-public static partial class ElmApiExtensions
-{
- // This file is intentionally left blank - since it's the parent file for nested ElmApiExtensions.* files
-}
\ No newline at end of file
diff --git a/Cql/CqlSdkPrototype/Elm/Internal/ElmApiInternalExtensions.cs b/Cql/CqlSdkPrototype/Elm/Internal/ElmApiInternalExtensions.cs
deleted file mode 100644
index 2ab55142e..000000000
--- a/Cql/CqlSdkPrototype/Elm/Internal/ElmApiInternalExtensions.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace CqlSdkPrototype.Elm.Internal;
-
-internal static class ElmApiInternalExtensions
-{
- internal static IElmApiInternal AsInternal(this IElmApiInternal self)
- where TElmApi : IElmApiInternal => self;
-}
\ No newline at end of file
diff --git a/Cql/CqlSdkPrototype/Elm/ElmApiScopedState.cs b/Cql/CqlSdkPrototype/Elm/Internal/ElmApiScopedState.cs
similarity index 65%
rename from Cql/CqlSdkPrototype/Elm/ElmApiScopedState.cs
rename to Cql/CqlSdkPrototype/Elm/Internal/ElmApiScopedState.cs
index 973a89cc7..bdde96a4b 100644
--- a/Cql/CqlSdkPrototype/Elm/ElmApiScopedState.cs
+++ b/Cql/CqlSdkPrototype/Elm/Internal/ElmApiScopedState.cs
@@ -1,8 +1,8 @@
using Hl7.Cql.Compiler;
-namespace CqlSdkPrototype.Elm;
+namespace CqlSdkPrototype.Elm.Internal;
-internal class ElmApiScopedState(IServiceScope scope) : IDisposable
+internal sealed class ElmToolkitScopedState(IServiceScope scope) : IDisposable
{
public LibrarySetExpressionBuilder LibrarySetExpressionBuilder { get; } = scope.ServiceProvider.GetRequiredService();
diff --git a/Cql/CqlSdkPrototype/Elm/Internal/ElmToAssemblyProcessorServices.cs b/Cql/CqlSdkPrototype/Elm/Internal/ElmToAssemblyProcessorServices.cs
new file mode 100644
index 000000000..c4abf4538
--- /dev/null
+++ b/Cql/CqlSdkPrototype/Elm/Internal/ElmToAssemblyProcessorServices.cs
@@ -0,0 +1,79 @@
+using CqlSdkPrototype.Internal;
+using Hl7.Cql.Abstractions;
+using Hl7.Cql.CodeGeneration.NET;
+using Hl7.Cql.Compiler;
+using Hl7.Cql.Conversion;
+using Hl7.Cql.Fhir;
+using Hl7.Cql.Runtime.Hosting;
+using Hl7.Fhir.Introspection;
+
+namespace CqlSdkPrototype.Elm.Internal;
+
+internal readonly record struct ElmToAssemblyProcessorServices(
+ ServiceProvider ServiceProvider,
+ ILogger Logger,
+ AssemblyCompiler AssemblyCompiler,
+ LibrarySetCSharpCodeGenerator LibrarySetCSharpCodeGenerator)
+{
+ public static ElmToAssemblyProcessorServices Create(
+ ILoggerFactory loggerFactory,
+ ElmToAssemblyCompilerConfig config)
+ {
+ var services = new ServiceCollection();
+ services.AddExternalLogging(loggerFactory);
+ AddCqlCodeGenerationServices(services, config);
+ var serviceProvider = services.BuildServiceProvider(validateScopes: true);
+
+ return ActivatorUtilities.CreateInstance(serviceProvider, serviceProvider);
+ }
+
+ private static void AddCqlCodeGenerationServices(
+ IServiceCollection services,
+ ElmToAssemblyCompilerConfig config)
+ {
+ var expressionBuilderSettings = config.ToExpressionBuilderSettings();
+ AddCqlCompilerServices(services, config.LRUCacheSize, expressionBuilderSettings);
+ services.TryAddSingleton();
+ services.TryAddSingleton();
+ services.TryAddSingleton();
+ }
+
+ ///
+ /// Used by and by many test cases
+ ///
+ public static IServiceCollection AddCqlCompilerServices(
+ IServiceCollection services,
+ int lruCacheSize = 0,
+ ExpressionBuilderSettings? expressionBuilderSettings = null)
+ {
+ expressionBuilderSettings ??= ExpressionBuilderSettings.Default;
+ services.TryAddSingleton(_ => Hl7.Fhir.Model.ModelInfo.ModelInspector);
+ services.TryAddSingleton();
+
+ services.TryAddSingleton(sp =>
+ {
+ var modelInspector = sp.GetRequiredService();
+ var logger = sp.GetLogger();
+ var converter = FhirTypeConverter
+ .Create(modelInspector, lruCacheSize)
+ .UseLogger(logger);
+ converter.CaptureAvailableConverters();
+ return converter;
+ });
+
+ services.TryAddSingleton();
+ services.TryAddSingleton();
+ services.TryAddSingleton(_ => expressionBuilderSettings);
+ services.TryAddScoped();
+ services.TryAddScoped();
+ services.TryAddScoped();
+ services.TryAddScoped();
+
+ return services;
+ }
+
+ public ServiceProvider ServiceProvider { get; } = ServiceProvider;
+ public AssemblyCompiler AssemblyCompiler { get; } = AssemblyCompiler;
+ public LibrarySetCSharpCodeGenerator LibrarySetCSharpCodeGenerator { get; } = LibrarySetCSharpCodeGenerator;
+ public ElmToolkitScopedState CreateScopedState() => new(ServiceProvider.CreateScope());
+}
\ No newline at end of file
diff --git a/Cql/CqlSdkPrototype/Elm/Internal/IElmApiInternal.cs b/Cql/CqlSdkPrototype/Elm/Internal/IElmApiInternal.cs
deleted file mode 100644
index dacf01121..000000000
--- a/Cql/CqlSdkPrototype/Elm/Internal/IElmApiInternal.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using CqlSdkPrototype.Elm.Extensibility;
-
-namespace CqlSdkPrototype.Elm.Internal;
-
-///
-/// Used to gain internal access to the state of the ELM API.
-///
-internal interface IElmApiInternal : IElmApiExtendable
- where TElmApi : IElmApiExtendable
-{
- ElmApiState State { get; }
-}
\ No newline at end of file
diff --git a/Cql/CqlSdkPrototype/GlobalUsings.cs b/Cql/CqlSdkPrototype/GlobalUsings.cs
index 187b89bd2..d265a5b60 100644
--- a/Cql/CqlSdkPrototype/GlobalUsings.cs
+++ b/Cql/CqlSdkPrototype/GlobalUsings.cs
@@ -13,5 +13,9 @@
global using Microsoft.Extensions.DependencyInjection.Extensions;
global using Microsoft.Extensions.Logging;
global using Microsoft.Extensions.Logging.Abstractions;
-global using CqlApiStateEntryDictionary = System.Collections.Immutable.ImmutableDictionary;
-global using ElmApiStateEntryDictionary = System.Collections.Immutable.ImmutableDictionary;
\ No newline at end of file
+global using CqlToElmTranslationDictionary = System.Collections.Immutable.ImmutableDictionary;
+global using CqlToElmTranslationReadOnlyDictionary = System.Collections.Generic.IReadOnlyDictionary;
+global using ElmToAssemblyCompilationDictionary = System.Collections.Immutable.ImmutableDictionary;
+global using ElmToAssemblyCompilationReadOnlyDictionary = System.Collections.Generic.IReadOnlyDictionary;
+global using AssemblyBinaryHashSet = System.Collections.Immutable.ImmutableHashSet;
+global using AssemblyBinaryReadOnlyHashSet = System.Collections.Generic.IReadOnlySet;
\ No newline at end of file
diff --git a/Cql/CqlSdkPrototype/Internal/InternalExtensions.cs b/Cql/CqlSdkPrototype/Internal/InternalExtensions.cs
index e11a98c4a..c8b358681 100644
--- a/Cql/CqlSdkPrototype/Internal/InternalExtensions.cs
+++ b/Cql/CqlSdkPrototype/Internal/InternalExtensions.cs
@@ -4,23 +4,6 @@ namespace CqlSdkPrototype.Internal;
internal static class InternalExtensions
{
- public static string[] SplitLines(this string multilineString) =>
- multilineString.Split([Environment.NewLine], StringSplitOptions.None);
-
- public static string Join(this IEnumerable lines) =>
- string.Concat(lines);
-
- public static string JoinLines(this IEnumerable lines) =>
- string.Join(Environment.NewLine, lines);
-
- public static string TakeLines(this string multilineString, int count) =>
- multilineString.SplitLines().Take(count).JoinLines();
-
- public static string TrimFileExtension(this string filePath, string extension) =>
- filePath.EndsWith(extension, StringComparison.OrdinalIgnoreCase)
- ? filePath[..^extension.Length]
- : filePath;
-
public static Assembly LoadFromBytes(this AssemblyLoadContext assemblyLoadContext, byte[] assembly, byte[]? symbols = null)
{
using var assemblyStream = new MemoryStream(assembly);
@@ -47,54 +30,6 @@ public static IEnumerable WhereNotNull(this IEnumerable enumerable)
.Select(x => x!);
}
- public static bool IsBetween(
- this Version value,
- Version lowerIncl,
- Version upperExcl)
- {
- return value >= lowerIncl && value < upperExcl;
- }
-
- public static Maybe TryGetFirst(this IEnumerable source)
- {
- if (source == null)
- throw new ArgumentNullException(nameof(source));
-
- if (source is IList list)
- {
- if (list.Count > 0)
- {
- return list[0];
- }
- }
- else
- {
- using IEnumerator e = source.GetEnumerator();
- if (e.MoveNext())
- {
- return e.Current;
- }
- }
-
- return Maybe.NoValue;
- }
-
- public static Maybe TryGetFirst(this IEnumerable source, Func predicate)
- {
- if (source == null)
- throw new ArgumentNullException(nameof(source));
-
- foreach (TSource element in source)
- {
- if (predicate(element))
- {
- return element;
- }
- }
-
- return Maybe.NoValue;
- }
-
public static LogExceptionMessageAction GetLogExceptionMessageAction(
this ILogger logger,
ProcessBatchItemExceptionHandling exceptionHandling) =>
diff --git a/Cql/CqlSdkPrototype/Logging/Internal/LoggingServiceCollectionExtensions.cs b/Cql/CqlSdkPrototype/Internal/LoggingServiceCollectionExtensions.cs
similarity index 90%
rename from Cql/CqlSdkPrototype/Logging/Internal/LoggingServiceCollectionExtensions.cs
rename to Cql/CqlSdkPrototype/Internal/LoggingServiceCollectionExtensions.cs
index 43e3b3dab..289d0026a 100644
--- a/Cql/CqlSdkPrototype/Logging/Internal/LoggingServiceCollectionExtensions.cs
+++ b/Cql/CqlSdkPrototype/Internal/LoggingServiceCollectionExtensions.cs
@@ -1,4 +1,4 @@
-namespace CqlSdkPrototype.Logging.Internal;
+namespace CqlSdkPrototype.Internal;
internal static class LoggingServiceCollectionExtensions
{
diff --git a/Cql/CqlSdkPrototype/Invocation.Fluent/Extensions/FluentCqlToolkitExtensions.cs b/Cql/CqlSdkPrototype/Invocation.Fluent/Extensions/FluentCqlToolkitExtensions.cs
new file mode 100644
index 000000000..39e27ae5c
--- /dev/null
+++ b/Cql/CqlSdkPrototype/Invocation.Fluent/Extensions/FluentCqlToolkitExtensions.cs
@@ -0,0 +1,23 @@
+using CqlSdkPrototype.Cql.Fluent;
+using CqlSdkPrototype.Elm;
+using CqlSdkPrototype.Elm.Fluent.Extensions;
+
+namespace CqlSdkPrototype.Invocation.Fluent.Extensions;
+
+public static class FluentCqlToolkitExtensions
+{
+
+#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
+ public static LibrarySetInvoker ToLibrarySetInvoker(
+#pragma warning restore RS0026
+ this FluentCqlToolkit cqlToolkit,
+ Func? configureElmToAssemblySettings = null,/*
+ Func? configureLibrarySetInvokerBuilderSettings = null,*/
+ string name = "")
+ {
+ return cqlToolkit
+ .TranslateCqlToElm()
+ .ToFluentElmToolkit(configureElmToAssemblySettings)
+ .ToLibrarySetInvoker(name/*, configureLibrarySetInvokerBuilderSettings*/);
+ }
+}
\ No newline at end of file
diff --git a/Cql/CqlSdkPrototype/Invocation.Fluent/Extensions/FluentElmToolkitExtensions.cs b/Cql/CqlSdkPrototype/Invocation.Fluent/Extensions/FluentElmToolkitExtensions.cs
new file mode 100644
index 000000000..93fd1a880
--- /dev/null
+++ b/Cql/CqlSdkPrototype/Invocation.Fluent/Extensions/FluentElmToolkitExtensions.cs
@@ -0,0 +1,38 @@
+using CqlSdkPrototype.Elm.Fluent;
+
+namespace CqlSdkPrototype.Invocation.Fluent.Extensions;
+
+public static class FluentElmToolkitExtensions
+{
+ public static FluentInvocationToolkit ToFluentInvocationToolkit(
+ this FluentElmToolkit elmToolkit/*,
+ Func? configureLibrarySetInvokerBuilderSettings = null*/)
+ {
+ /*var config = LibrarySetInvokerBuilderConfig.Default;
+ if (configureLibrarySetInvokerBuilderSettings is not null) config = configureLibrarySetInvokerBuilderSettings(config);*/
+
+ var assemblyBinaries =
+ from entry in elmToolkit.ElmToAssemblyCompilations
+ let assembly = entry.Value.AssemblyBinary
+ where assembly is not null
+ let debugSymbols = entry.Value.DebugSymbolsBinary
+ let assemblyBinary = new Hl7.Cql.CodeGeneration.NET.AssemblyBinary(assembly, debugSymbols)
+ select assemblyBinary;
+
+ var invocationToolkit = new FluentInvocationToolkit(elmToolkit.LoggerFactory/*, config*/).AddAssemblyBinaries(assemblyBinaries);
+ return invocationToolkit;
+ }
+
+#pragma warning disable RS0026 // Do not add multiple public overloads with optional parameters
+ public static LibrarySetInvoker ToLibrarySetInvoker(
+#pragma warning restore RS0026
+ this FluentElmToolkit elmToolkit,
+ string name = ""/*,
+ Func? configure = null*/)
+ {
+ return elmToolkit
+ .CompileElmToAssemblies()
+ .ToFluentInvocationToolkit(/*configure*/)
+ .ToLibrarySetInvoker(name);
+ }
+}
diff --git a/Cql/CqlSdkPrototype/Invocation.Fluent/Extensions/FluentInvocationToolkitExtensions.cs b/Cql/CqlSdkPrototype/Invocation.Fluent/Extensions/FluentInvocationToolkitExtensions.cs
new file mode 100644
index 000000000..44e6c59c7
--- /dev/null
+++ b/Cql/CqlSdkPrototype/Invocation.Fluent/Extensions/FluentInvocationToolkitExtensions.cs
@@ -0,0 +1,13 @@
+using Hl7.Cql.CodeGeneration.NET;
+
+namespace CqlSdkPrototype.Invocation.Fluent.Extensions;
+
+public static class FluentInvocationToolkitExtensions
+{
+ public static FluentInvocationToolkit AddAssemblyBinaries(
+ this FluentInvocationToolkit invocationToolkit,
+ params AssemblyBinary[] assemblyBinary)
+ {
+ return invocationToolkit.AddAssemblyBinaries(assemblyBinary.AsEnumerable());
+ }
+}
\ No newline at end of file
diff --git a/Cql/CqlSdkPrototype/Invocation.Fluent/FluentInvocationToolkit.cs b/Cql/CqlSdkPrototype/Invocation.Fluent/FluentInvocationToolkit.cs
new file mode 100644
index 000000000..8816e39b1
--- /dev/null
+++ b/Cql/CqlSdkPrototype/Invocation.Fluent/FluentInvocationToolkit.cs
@@ -0,0 +1,59 @@
+using Hl7.Cql.CodeGeneration.NET;
+
+namespace CqlSdkPrototype.Invocation.Fluent;
+
+///
+/// Provides a fluent interface for building and configuring a .
+///
+public sealed class FluentInvocationToolkit
+{
+ private readonly LibrarySetInvokerBuilder librarySetInvokerBuilder;
+
+ ///
+ /// Initializes a new instance of the class with a default logger factory.
+ ///
+ public FluentInvocationToolkit() : this(default(ILoggerFactory)) { }
+
+ ///
+ /// Initializes a new instance of the class with the specified logger factory.
+ ///
+ /// The logger factory to use for logging.
+ public FluentInvocationToolkit(ILoggerFactory? loggerFactory)
+ : this(new LibrarySetInvokerBuilder(loggerFactory))
+ { }
+
+ ///
+ /// Initializes a new instance of the class with the specified library set invoker builder.
+ ///
+ /// The library set invoker builder to use.
+ public FluentInvocationToolkit(LibrarySetInvokerBuilder librarySetInvokerBuilder)
+ {
+ this.librarySetInvokerBuilder = librarySetInvokerBuilder;
+ }
+
+ ///
+ /// Gets the logger factory used by the exceptions.
+ ///
+ public ILoggerFactory LoggerFactory => librarySetInvokerBuilder.LoggerFactory;
+
+ ///
+ /// Adds the specified assembly binaries to the toolkit.
+ ///
+ /// The assembly binaries to add.
+ /// The current instance of .
+ public FluentInvocationToolkit AddAssemblyBinaries(IEnumerable assemblyBinary)
+ {
+ librarySetInvokerBuilder.AddAssemblyBinaries(assemblyBinary);
+ return this;
+ }
+
+ ///
+ /// Builds and returns a with the specified name.
+ ///
+ /// The name of the library set invoker.
+ /// A instance.
+ public LibrarySetInvoker ToLibrarySetInvoker(string name = "")
+ {
+ return librarySetInvokerBuilder.ToLibrarySetInvoker(name);
+ }
+}
diff --git a/Cql/CqlSdkPrototype/Invocation/DefinitionInvoker.cs b/Cql/CqlSdkPrototype/Invocation/DefinitionInvoker.cs
new file mode 100644
index 000000000..cb070d35e
--- /dev/null
+++ b/Cql/CqlSdkPrototype/Invocation/DefinitionInvoker.cs
@@ -0,0 +1,71 @@
+using Hl7.Cql.Abstractions;
+using Hl7.Cql.Runtime;
+
+namespace CqlSdkPrototype.Invocation;
+
+
+///
+/// Abstract class representing a definition invoker.
+///
+/// The name of the definition.
+/// The library containing the definition.
+/// The method information for the definition.
+/// The tag values associated with the definition.
+/// The value set identifier, if any.
+public abstract class DefinitionInvoker(
+ string definitionName,
+ ILibrary library,
+ MethodInfo methodInfo,
+ IReadOnlyDictionary tagValuesByName,
+ string? valueSetId)
+{
+ ///
+ /// Gets the name of the definition.
+ ///
+ public string DefinitionName { get; } = definitionName;
+
+ ///
+ /// Gets the return type of the method.
+ ///
+ public Type ReturnType => MethodInfo.ReturnType;
+
+ ///
+ /// Gets the tag values associated with the definition.
+ ///
+ public IReadOnlyDictionary TagValuesByName { get; } = tagValuesByName;
+
+ ///
+ /// Gets the value set identifier, if any.
+ ///
+ public string? ValueSetId { get; } = valueSetId;
+
+ ///
+ /// Gets the library containing the definition.
+ ///
+ private ILibrary Library { get; } = library; // Might decide to make this public later
+
+ ///
+ /// Gets the method information for the definition.
+ ///
+ private MethodInfo MethodInfo { get; } = methodInfo;
+
+ ///
+ /// Invokes the definition with the given CQL context.
+ ///
+ /// The CQL context.
+ /// The result of the invocation.
+ public abstract object? Invoke(CqlContext cqlContext);
+
+ ///
+ /// Invokes the method with the specified parameters.
+ ///
+ /// The parameters to pass to the method.
+ /// The result of the method invocation.
+ protected object? InvokeMethod(params object?[] parameters)
+ {
+ MethodInfo methodInfo = MethodInfo;
+ ILibrary library = Library;
+ var result = methodInfo.Invoke(library, BindingFlags.DoNotWrapExceptions, null, parameters, CultureInfo.InvariantCulture);
+ return result;
+ }
+}
diff --git a/Cql/CqlSdkPrototype/Invocation/Extensions/LibraryInvokerExtensions.cs b/Cql/CqlSdkPrototype/Invocation/Extensions/LibraryInvokerExtensions.cs
new file mode 100644
index 000000000..e850fd45e
--- /dev/null
+++ b/Cql/CqlSdkPrototype/Invocation/Extensions/LibraryInvokerExtensions.cs
@@ -0,0 +1,25 @@
+using Hl7.Cql.Runtime;
+
+namespace CqlSdkPrototype.Invocation.Extensions;
+
+public static class LibraryInvokerExtensions
+{
+ public static IEnumerable<(DefinitionInvoker definitionInvoker, Func