nip/src/nimpak/grafting.nim

163 lines
5.7 KiB
Nim

# SPDX-License-Identifier: LSL-1.0
# Copyright (c) 2026 Markus Maiwald
# Stewardship: Self Sovereign Society Foundation
#
# This file is part of the Nexus Sovereign Core.
# See legal/LICENSE_SOVEREIGN.md for license terms.
# nimpak/grafting_simple.nim
# Simplified grafting infrastructure for external package integration
import std/[tables, sets, strutils, json, os, times, sequtils, hashes, options]
import ./types
import types/grafting_types
export grafting_types
type
# Core grafting engine types
GraftingEngine* = object
adapters*: Table[string, PackageAdapter]
cache*: GraftingCache
config*: GraftingConfig
transactions*: seq[GraftTransaction]
# Core grafting engine procedures
proc initGraftingEngine*(configPath: string = ""): Result[GraftingEngine, string] =
## Initialize the grafting engine with configuration
var engine = GraftingEngine(
adapters: initTable[string, PackageAdapter](),
cache: GraftingCache(
cacheDir: getHomeDir() / ".nip" / "graft-cache",
metadata: initTable[string, GraftedPackageMetadata](),
archives: initTable[string, string]()
),
config: GraftingConfig(
enabled: true,
verifyGraftedPackages: true,
convertToNpkAutomatically: false,
adapters: initTable[string, AdapterConfig]()
),
transactions: @[]
)
# Create cache directory
if not dirExists(engine.cache.cacheDir):
try:
createDir(engine.cache.cacheDir)
except OSError as e:
return Result[GraftingEngine, string](isOk: false, errValue: "Failed to create cache directory: " & e.msg)
return Result[GraftingEngine, string](isOk: true, okValue: engine)
proc registerAdapter*(engine: var GraftingEngine, adapter: PackageAdapter): Result[bool, string] =
## Register a package adapter with the grafting engine
if adapter.name in engine.adapters:
return Result[bool, string](isOk: false, errValue: "Adapter already registered: " & adapter.name)
engine.adapters[adapter.name] = adapter
echo "Registered grafting adapter: " & adapter.name
return Result[bool, string](isOk: true, okValue: true)
proc graftPackage*(engine: var GraftingEngine, source: string, packageName: string): Result[GraftResult, string] =
## Graft a package from an external source
if not engine.config.enabled:
return Result[GraftResult, string](isOk: false, errValue: "Grafting is disabled in configuration")
if source notin engine.adapters:
return Result[GraftResult, string](isOk: false, errValue: "Unknown grafting source: " & source)
let adapter = engine.adapters[source]
if not adapter.enabled:
return Result[GraftResult, string](isOk: false, errValue: "Adapter disabled: " & source)
# Create a simple result for now
let graftRes = GraftResult(
success: true,
packageId: packageName,
metadata: GraftedPackageMetadata(
packageName: packageName,
version: "1.0.0",
source: source,
graftedAt: now(),
originalHash: "placeholder-hash",
graftHash: "graft-hash",
buildLog: "Build log placeholder",
provenance: ProvenanceInfo(
originalSource: source,
downloadUrl: "",
archivePath: "",
extractedPath: "",
conversionLog: ""
)
),
npkPath: none(string),
errors: @[]
)
echo "Successfully grafted package: " & packageName
return Result[GraftResult, string](isOk: true, okValue: graftRes)
proc listGraftedPackages*(engine: GraftingEngine): seq[GraftedPackageMetadata] =
## List all grafted packages in cache
result = @[]
for metadata in engine.cache.metadata.values:
result.add(metadata)
proc getGraftingStatus*(engine: GraftingEngine): JsonNode =
## Get current grafting engine status
result = %*{
"enabled": engine.config.enabled,
"adapters": {},
"cache": {
"directory": engine.cache.cacheDir,
"packages": engine.cache.metadata.len,
"archives": engine.cache.archives.len
},
"transactions": engine.transactions.len
}
for name, adapter in engine.adapters:
result["adapters"][name] = %*{
"enabled": adapter.enabled,
"priority": adapter.priority
}
# Base adapter methods (to be overridden by specific adapters)
method graftPackage*(adapter: PackageAdapter, packageName: string, cache: GraftingCache): GraftResult {.base.} =
## Base method for grafting a package - must be overridden by specific adapters
GraftResult(
success: false,
packageId: packageName,
metadata: GraftedPackageMetadata(),
npkPath: none(string),
errors: @["Base adapter method not implemented"]
)
method validatePackage*(adapter: PackageAdapter, packageName: string): Result[bool, string] {.base.} =
## Base method for validating a package - can be overridden
return Result[bool, string](isOk: true, okValue: true)
method getPackageInfo*(adapter: PackageAdapter, packageName: string): Result[JsonNode, string] {.base.} =
## Base method for getting package information - can be overridden
return Result[JsonNode, string](isOk: true, okValue: %*{"name": packageName, "adapter": adapter.name})
# Utility functions
proc calculateGraftHash*(packageName: string, source: string, timestamp: DateTime): string =
## Calculate a unique hash for a grafted package
let input = packageName & "|" & source & "|" & $timestamp.toTime().toUnix()
# TODO: Use proper BLAKE3 hashing when available
"graft-" & $hash(input)
proc findExtractedPath(cacheDir: string, packageName: string): string =
## Find the extracted path for a grafted package
let extractedDir = cacheDir / "extracted" / packageName
if dirExists(extractedDir):
return extractedDir
# Try alternative locations
let builtDir = cacheDir / "built" / packageName
if dirExists(builtDir):
return builtDir
return ""