package main
import "C"
import (
"encoding/json"
"fmt"
"net/http"
"os"
"path/filepath"
"runtime"
"strings"
"unsafe"
"cuelang.org/go/cue"
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/build"
"cuelang.org/go/cue/cuecontext"
"cuelang.org/go/cue/load"
"cuelang.org/go/mod/modconfig"
)
const BridgeVersion = "bridge/1"
const (
ErrorCodeInvalidInput = "INVALID_INPUT"
ErrorCodeLoadInstance = "LOAD_INSTANCE"
ErrorCodeBuildValue = "BUILD_VALUE"
ErrorCodeOrderedJSON = "ORDERED_JSON"
ErrorCodePanicRecover = "PANIC_RECOVER"
ErrorCodeJSONMarshal = "JSON_MARSHAL_ERROR"
ErrorCodeRegistryInit = "REGISTRY_INIT"
ErrorCodeDependencyRes = "DEPENDENCY_RESOLUTION"
)
type BridgeError struct {
Code string `json:"code"`
Message string `json:"message"`
Hint *string `json:"hint,omitempty"`
}
type BridgeResponse struct {
Version string `json:"version"`
Ok *json.RawMessage `json:"ok,omitempty"`
Error *BridgeError `json:"error,omitempty"`
}
func cue_free_string(s *C.char) {
C.free(unsafe.Pointer(s))
}
func cue_bridge_version() *C.char {
versionInfo := fmt.Sprintf("%s (Go %s)", BridgeVersion, runtime.Version())
return C.CString(versionInfo)
}
func createErrorResponse(code, message string, hint *string) *C.char {
error := &BridgeError{
Code: code,
Message: message,
Hint: hint,
}
response := &BridgeResponse{
Version: BridgeVersion,
Error: error,
}
responseBytes, err := json.Marshal(response)
if err != nil {
fallbackResponse := fmt.Sprintf(`{"version":"%s","error":{"code":"%s","message":"Failed to marshal error response: %s"}}`, BridgeVersion, ErrorCodeJSONMarshal, err.Error())
return C.CString(fallbackResponse)
}
return C.CString(string(responseBytes))
}
func createSuccessResponse(data string) *C.char {
rawData := json.RawMessage(data)
response := &BridgeResponse{
Version: BridgeVersion,
Ok: &rawData,
}
responseBytes, err := json.Marshal(response)
if err != nil {
msg := fmt.Sprintf("Failed to marshal success response: %s", err.Error())
return createErrorResponse(ErrorCodeJSONMarshal, msg, nil)
}
return C.CString(string(responseBytes))
}
type TaskSourcePos struct {
File string
Line int
Column int
}
func extractTaskPositions(inst *build.Instance, moduleRoot string) map[string]TaskSourcePos {
positions := make(map[string]TaskSourcePos)
for _, f := range inst.Files {
for _, decl := range f.Decls {
field, ok := decl.(*ast.Field)
if !ok {
continue
}
label, _, _ := ast.LabelName(field.Label)
if label != "tasks" {
continue
}
st, ok := field.Value.(*ast.StructLit)
if !ok {
continue
}
extractTaskPositionsFromStruct(st, "", f.Filename, moduleRoot, positions)
}
}
return positions
}
func extractTaskPositionsFromStruct(st *ast.StructLit, prefix, filename, moduleRoot string, positions map[string]TaskSourcePos) {
for _, elem := range st.Elts {
taskField, ok := elem.(*ast.Field)
if !ok {
continue
}
taskLabel, _, _ := ast.LabelName(taskField.Label)
fullName := taskLabel
if prefix != "" {
fullName = prefix + "." + taskLabel
}
relPath := filename
if moduleRoot != "" && strings.HasPrefix(filename, moduleRoot) {
relPath = strings.TrimPrefix(filename, moduleRoot)
relPath = strings.TrimPrefix(relPath, string(filepath.Separator))
}
if relPath == "" {
relPath = "env.cue"
}
pos := taskField.Pos()
positions[fullName] = TaskSourcePos{
File: relPath,
Line: pos.Line(),
Column: pos.Column(),
}
if nestedSt, ok := taskField.Value.(*ast.StructLit); ok {
for _, nestedElem := range nestedSt.Elts {
if nestedField, ok := nestedElem.(*ast.Field); ok {
nestedLabel, _, _ := ast.LabelName(nestedField.Label)
if nestedLabel == "tasks" {
if nestedTasksSt, ok := nestedField.Value.(*ast.StructLit); ok {
extractTaskPositionsFromStruct(nestedTasksSt, fullName, filename, moduleRoot, positions)
}
}
}
}
}
}
}
func buildJSONWithHidden(v cue.Value, moduleRoot string, taskPositions map[string]TaskSourcePos) ([]byte, error) {
result := make(map[string]interface{})
iter, err := v.Fields(cue.Hidden(true))
if err != nil {
return nil, err
}
for iter.Next() {
sel := iter.Selector()
fieldName := sel.String()
fieldValue := iter.Value()
var val interface{}
if err := fieldValue.Decode(&val); err != nil {
return nil, fmt.Errorf("failed to decode field %s: %w", fieldName, err)
}
if fieldName == "tasks" {
val = enrichTasksWithSource(val, "", taskPositions)
}
result[fieldName] = val
}
return json.Marshal(result)
}
func enrichTasksWithSource(decoded interface{}, prefix string, positions map[string]TaskSourcePos) interface{} {
tasksMap, ok := decoded.(map[string]interface{})
if !ok {
return decoded
}
for taskName, taskDef := range tasksMap {
fullName := taskName
if prefix != "" {
fullName = prefix + "." + taskName
}
enrichTaskWithSource(taskDef, fullName, positions)
}
return tasksMap
}
func enrichTaskWithSource(taskDef interface{}, fullName string, positions map[string]TaskSourcePos) {
taskObj, ok := taskDef.(map[string]interface{})
if !ok {
return
}
if nested, ok := taskObj["tasks"].(map[string]interface{}); ok {
for childName, childDef := range nested {
childFullName := fullName + "." + childName
enrichTaskWithSource(childDef, childFullName, positions)
}
return
}
if _, isArray := taskDef.([]interface{}); isArray {
return
}
_, hasCommand := taskObj["command"]
_, hasScript := taskObj["script"]
_, hasTaskRef := taskObj["task_ref"]
if !hasCommand && !hasScript && !hasTaskRef {
return
}
if pos, ok := positions[fullName]; ok {
taskObj["_source"] = map[string]interface{}{
"file": pos.File,
"line": pos.Line,
"column": pos.Column,
}
}
}
func cue_eval_package(dirPath *C.char, packageName *C.char) *C.char {
var result *C.char
defer func() {
if r := recover(); r != nil {
panic_msg := fmt.Sprintf("Internal panic: %v", r)
result = createErrorResponse(ErrorCodePanicRecover, panic_msg, nil)
}
}()
goDir := C.GoString(dirPath)
goPackageName := C.GoString(packageName)
if goDir == "" {
result = createErrorResponse(ErrorCodeInvalidInput, "Directory path cannot be empty", nil)
return result
}
if goPackageName == "" {
result = createErrorResponse(ErrorCodeInvalidInput, "Package name cannot be empty", nil)
return result
}
ctx := cuecontext.New()
registry, err := modconfig.NewRegistry(&modconfig.Config{
Transport: http.DefaultTransport,
ClientType: "cuenv",
})
if err != nil {
hint := "Check CUE registry configuration (CUE_REGISTRY env var) and network access"
result = createErrorResponse(ErrorCodeRegistryInit,
fmt.Sprintf("Failed to initialize CUE registry: %v", err), &hint)
return result
}
moduleRoot := resolveCueModuleRoot(goDir)
cfg := &load.Config{
Dir: goDir,
Registry: registry,
}
if moduleRoot != "" {
cfg.ModuleRoot = moduleRoot
}
var instances []*build.Instance
packagePath := ".:" + goPackageName
instances = load.Instances([]string{packagePath}, cfg)
if len(instances) == 0 {
hint := "Check that the package name exists and CUE files are present"
result = createErrorResponse(ErrorCodeLoadInstance, "No CUE instances found", &hint)
return result
}
inst := instances[0]
if inst.Err != nil {
msg := fmt.Sprintf("Failed to load CUE instance: %v", inst.Err)
hint := "Check CUE syntax and import statements"
result = createErrorResponse(ErrorCodeLoadInstance, msg, &hint)
return result
}
v := ctx.BuildInstance(inst)
if v.Err() != nil {
msg := fmt.Sprintf("Failed to build CUE value: %v", v.Err())
hint := "Check CUE constraints and value definitions"
result = createErrorResponse(ErrorCodeBuildValue, msg, &hint)
return result
}
taskPositions := extractTaskPositions(inst, moduleRoot)
jsonBytes, err := buildJSONWithHidden(v, moduleRoot, taskPositions)
if err != nil {
msg := fmt.Sprintf("Failed to marshal JSON: %v", err)
result = createErrorResponse(ErrorCodeOrderedJSON, msg, nil)
return result
}
result = createSuccessResponse(string(jsonBytes))
return result
}
func resolveCueModuleRoot(startDir string) string {
if envRoot := os.Getenv("CUENV_CUE_MODULE_ROOT"); envRoot != "" {
if info, err := os.Stat(filepath.Join(envRoot, "cue.mod", "module.cue")); err == nil && !info.IsDir() {
return envRoot
}
}
dir, err := filepath.Abs(startDir)
if err != nil {
dir = startDir
}
for {
moduleFile := filepath.Join(dir, "cue.mod", "module.cue")
if info, err := os.Stat(moduleFile); err == nil && !info.IsDir() {
return dir
}
parent := filepath.Dir(dir)
if parent == dir {
break
}
dir = parent
}
return ""
}
func main() {}