# 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 ""