163 lines
5.7 KiB
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 "" |