lib/choir/src/composition/module/variant.zig

daab053ee43316e1809a84551d573ddd1e5bf3d2

  1 const std = @import("std");
  2 const choir = @import("../../root.zig");
  3 const composition = @import("../root.zig");
  4 const callsite = @import("callsite.zig");
  5 
  6 const Allocator = std.mem.Allocator;
  7 const product = choir.product;
  8 const source = composition.source;
  9 
 10 pub const CompositionVariantId = struct {
 11     value: u64,
 12 };
 13 
 14 pub const TargetChoiceSpec = struct {
 15     target: product.ProductRef,
 16     policy: product.ProductRef,
 17 
 18     pub fn eql(self: TargetChoiceSpec, other: TargetChoiceSpec) bool {
 19         return self.target.eql(other.target) and self.policy.eql(other.policy);
 20     }
 21 };
 22 
 23 pub const TargetChoice = struct {
 24     target: product.ProductRef,
 25     policy: product.ProductRef,
 26 
 27     pub fn init(allocator: Allocator, choice_spec: TargetChoiceSpec) Allocator.Error!TargetChoice {
 28         const target = try product.cloneProductRef(allocator, choice_spec.target);
 29         errdefer product.deinitProductRef(allocator, target);
 30         return .{
 31             .target = target,
 32             .policy = try product.cloneProductRef(allocator, choice_spec.policy),
 33         };
 34     }
 35 
 36     pub fn deinit(self: *TargetChoice, allocator: Allocator) void {
 37         product.deinitProductRef(allocator, self.policy);
 38         product.deinitProductRef(allocator, self.target);
 39         self.* = undefined;
 40     }
 41 
 42     pub fn eql(self: TargetChoice, choice_spec: TargetChoiceSpec) bool {
 43         return self.target.eql(choice_spec.target) and self.policy.eql(choice_spec.policy);
 44     }
 45 
 46     pub fn asSpec(self: TargetChoice) TargetChoiceSpec {
 47         return .{
 48             .target = self.target,
 49             .policy = self.policy,
 50         };
 51     }
 52 };
 53 
 54 pub const CompositionVariantSpec = struct {
 55     id: CompositionVariantId,
 56     choice: TargetChoiceSpec,
 57     fragments: []const source.FragmentId,
 58     call_sites: []const callsite.CallSiteSpec,
 59 };
 60 
 61 /// A complete physical-program choice for one target and policy identity.
 62 /// Fragment choices cannot vary independently after this variant is selected.
 63 pub const CompositionVariant = struct {
 64     id: CompositionVariantId,
 65     choice: TargetChoice,
 66     fragments: []const source.FragmentId,
 67     call_sites: []callsite.CallSite,
 68 
 69     pub fn init(allocator: Allocator, spec: CompositionVariantSpec) Allocator.Error!CompositionVariant {
 70         var choice = try TargetChoice.init(allocator, spec.choice);
 71         errdefer choice.deinit(allocator);
 72         const fragments = try allocator.dupe(source.FragmentId, spec.fragments);
 73         errdefer allocator.free(fragments);
 74         const calls = try allocator.alloc(callsite.CallSite, spec.call_sites.len);
 75         var calls_initialized: usize = 0;
 76         errdefer {
 77             var index = calls_initialized;
 78             while (index != 0) {
 79                 index -= 1;
 80                 calls[index].deinit(allocator);
 81             }
 82             allocator.free(calls);
 83         }
 84         for (spec.call_sites, calls) |call_spec, *call_value| {
 85             call_value.* = try callsite.CallSite.init(allocator, call_spec);
 86             calls_initialized += 1;
 87         }
 88         return .{
 89             .id = spec.id,
 90             .choice = choice,
 91             .fragments = fragments,
 92             .call_sites = calls,
 93         };
 94     }
 95 
 96     pub fn deinit(self: *CompositionVariant, allocator: Allocator) void {
 97         var call_index = self.call_sites.len;
 98         while (call_index != 0) {
 99             call_index -= 1;
100             self.call_sites[call_index].deinit(allocator);
101         }
102         allocator.free(self.call_sites);
103         allocator.free(self.fragments);
104         self.choice.deinit(allocator);
105         self.* = undefined;
106     }
107 
108     pub fn containsFragment(self: *const CompositionVariant, id: source.FragmentId) bool {
109         for (self.fragments) |fragment_id| {
110             if (fragment_id.value == id.value) return true;
111         }
112         return false;
113     }
114 
115     pub fn callSite(self: *const CompositionVariant, id: source.CallSiteId) ?*const callsite.CallSite {
116         for (self.call_sites) |*call_site| {
117             if (call_site.id.value == id.value) return call_site;
118         }
119         return null;
120     }
121 };