1 package organ
2 3 import (
4 "fmt"
5 )
6 7 // HostFunc is a function that performs the same operation as an organ,
8 // implemented in host-side Go. This is the fallback when no organ is
9 // available or all organs fail.
10 type HostFunc func(input []byte) ([]byte, error)
11 12 // Dispatcher routes operations to the best available organ, falling
13 // back to host-side code if no organ is loaded or all organs fail.
14 //
15 // The fallback hierarchy:
16 // 1. Best WASM organ of the type (highest fitness)
17 // 2. Second-best WASM organ (if any)
18 // 3. Host-side Go implementation (always available)
19 type Dispatcher struct {
20 Registry *Registry
21 Type OrganType
22 Opcode byte
23 Host HostFunc // always-available fallback
24 }
25 26 // Dispatch sends input through the fallback hierarchy.
27 // Returns the result and which tier produced it (0 = best organ,
28 // 1 = second-best organ, 2 = host fallback).
29 func (d *Dispatcher) Dispatch(input []byte) (result []byte, tier int, err error) {
30 if d.Registry != nil {
31 organs := d.Registry.All(d.Type)
32 for i, org := range organs {
33 result, err = d.Registry.Call(org.Manifest.ID, d.Opcode, input)
34 if err == nil {
35 return result, i, nil
36 }
37 // Organ failed — try next.
38 }
39 }
40 41 // All organs failed or none loaded — use host fallback.
42 if d.Host == nil {
43 return nil, -1, fmt.Errorf("no organ or host fallback for %s", d.Type)
44 }
45 46 result, err = d.Host(input)
47 if err != nil {
48 return nil, -1, fmt.Errorf("host fallback: %w", err)
49 }
50 51 tier = 2
52 if d.Registry != nil && len(d.Registry.All(d.Type)) > 0 {
53 // Organs existed but all failed.
54 tier = len(d.Registry.All(d.Type))
55 }
56 57 return result, tier, nil
58 }
59 60 // CanDispatch checks whether any organ can handle this input.
61 // Uses the CanDigest opcode (0x01) for enzyme organs.
62 // Returns true if any organ or host reports capability.
63 func (d *Dispatcher) CanDispatch(input []byte) bool {
64 if d.Registry != nil {
65 organs := d.Registry.All(d.Type)
66 for _, org := range organs {
67 result, err := d.Registry.Call(org.Manifest.ID, OpCanDigest, input)
68 if err == nil && len(result) > 0 && result[0] == 0x01 {
69 return true
70 }
71 }
72 }
73 // No organ can handle it — host decides.
74 return d.Host != nil
75 }
76