nip/src/nimpak/variant_migration.nim

322 lines
9.5 KiB
Nim
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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.
## variant_migration.nim
## Migration utilities for transitioning from legacy USE flags to variant domains
## Task 15: Legacy flag translation and migration warnings
import std/[tables, strutils, os, strformat]
import variant_domains
import config
type
MigrationResult* = object
## Result of flag migration
success*: bool
translatedFlags*: Table[string, seq[string]] # domain -> flags
warnings*: seq[string]
errors*: seq[string]
skippedFlags*: seq[string] # Flags that couldn't be migrated
LegacyFlagInfo* = object
## Information about a legacy flag
name*: string
category*: string
enabled*: bool
suggestedDomain*: string
suggestedFlag*: string
# #############################################################################
# Legacy Flag Detection
# #############################################################################
proc detectLegacyFlags*(flags: seq[UseFlag]): seq[LegacyFlagInfo] =
## Detect legacy USE flags and provide migration suggestions
result = @[]
for flag in flags:
if isLegacyCategory(flag.category):
let suggestedDomain = mapLegacyCategoryToDomain(flag.category)
var info = LegacyFlagInfo(
name: flag.name,
category: flag.category,
enabled: flag.enabled,
suggestedDomain: suggestedDomain,
suggestedFlag: flag.name
)
result.add(info)
proc isLegacyFlagString*(flagStr: string): bool =
## Check if a flag string uses legacy syntax
## Legacy: category/flag or just flag
## New: +domain=flag
if flagStr.startsWith("+"):
return false # New syntax
if '/' in flagStr:
return true # Old category/flag syntax
# Could be either - assume legacy if no domain marker
return true
# #############################################################################
# Flag Translation
# #############################################################################
proc translateLegacyFlag*(
flagName: string,
category: string
): tuple[domain: string, flag: string, success: bool] =
## Translate a single legacy flag to new domain syntax
if not isLegacyCategory(category):
return ("", "", false)
let domain = mapLegacyCategoryToDomain(category)
# Special cases that shouldn't be migrated (returns empty string)
if domain.len == 0:
return ("", "", false)
return (domain, flagName, true)
proc translateLegacyFlags*(flags: seq[UseFlag]): MigrationResult =
## Translate a list of legacy USE flags to domain-scoped flags
result = MigrationResult(
success: true,
translatedFlags: initTable[string, seq[string]](),
warnings: @[],
errors: @[],
skippedFlags: @[]
)
for flag in flags:
if not flag.enabled:
continue # Skip disabled flags
if not isLegacyCategory(flag.category):
# Not a legacy flag - skip
result.warnings.add(fmt"Skipping non-legacy flag: {flag.name} (category: {flag.category})")
continue
let (domain, translatedFlag, success) = translateLegacyFlag(flag.name, flag.category)
if not success:
result.skippedFlags.add(fmt"{flag.category}/{flag.name}")
result.warnings.add(fmt"Cannot migrate {flag.category}/{flag.name} - special category")
continue
# Add to translated flags
if not result.translatedFlags.hasKey(domain):
result.translatedFlags[domain] = @[]
if translatedFlag notin result.translatedFlags[domain]:
result.translatedFlags[domain].add(translatedFlag)
proc translateFlagString*(flagStr: string): string =
## Translate a single flag string from legacy to new syntax
## Examples:
## "gui/wayland" -> "+graphics=wayland"
## "optimization/lto" -> "+optimization=lto"
if flagStr.startsWith("+"):
return flagStr # Already new syntax
if '/' in flagStr:
let parts = flagStr.split('/', 1)
if parts.len == 2:
let category = parts[0]
let flag = parts[1]
if isLegacyCategory(category):
let domain = mapLegacyCategoryToDomain(category)
if domain notin ["profile", "build-mode"]:
return fmt"+{domain}={flag}"
# Couldn't translate - return as-is with warning marker
return flagStr
# #############################################################################
# Migration Warnings
# #############################################################################
proc generateMigrationWarning*(flag: LegacyFlagInfo): string =
## Generate a deprecation warning for a legacy flag
if flag.suggestedDomain in ["profile", "build-mode"]:
return fmt"⚠️ Legacy flag '{flag.category}/{flag.name}' uses deprecated category. " &
fmt"This should be handled as a {flag.suggestedDomain} instead."
return fmt"⚠️ Legacy flag '{flag.category}/{flag.name}' is deprecated. " &
fmt"Use: +{flag.suggestedDomain}={flag.suggestedFlag}"
proc generateMigrationSummary*(migrationResult: MigrationResult): string =
## Generate a human-readable summary of migration results
var lines: seq[string] = @[]
lines.add("🔄 Legacy Flag Migration Summary")
lines.add("")
if migrationResult.translatedFlags.len > 0:
lines.add("✅ Translated Flags:")
for domain, flags in migrationResult.translatedFlags.pairs:
let flagsStr = flags.join(", ")
lines.add(fmt" {domain}: {flagsStr}")
lines.add("")
if migrationResult.skippedFlags.len > 0:
lines.add("⏭️ Skipped Flags:")
for flag in migrationResult.skippedFlags:
lines.add(fmt" {flag}")
lines.add("")
if migrationResult.warnings.len > 0:
lines.add("⚠️ Warnings:")
for warning in migrationResult.warnings:
lines.add(fmt" {warning}")
lines.add("")
if migrationResult.errors.len > 0:
lines.add("❌ Errors:")
for error in migrationResult.errors:
lines.add(fmt" {error}")
lines.add("")
return lines.join("\n")
# #############################################################################
# Config File Migration
# #############################################################################
proc migrateConfigFile*(
inputPath: string,
outputPath: string = ""
): tuple[success: bool, message: string] =
## Migrate a config file from legacy USE flags to domain syntax
## If outputPath is empty, overwrites the input file
let actualOutputPath = if outputPath.len > 0: outputPath else: inputPath
if not fileExists(inputPath):
return (false, fmt"Input file not found: {inputPath}")
try:
let content = readFile(inputPath)
var newLines: seq[string] = @[]
var migrationCount = 0
for line in content.splitLines():
let trimmed = line.strip()
# Skip comments and empty lines
if trimmed.len == 0 or trimmed.startsWith("#"):
newLines.add(line)
continue
# Check if line contains legacy flag syntax
if '/' in trimmed and not trimmed.startsWith("+"):
# Try to translate
let translated = translateFlagString(trimmed)
if translated != trimmed:
newLines.add(translated)
migrationCount += 1
continue
# Keep line as-is
newLines.add(line)
# Write output
writeFile(actualOutputPath, newLines.join("\n"))
if migrationCount > 0:
return (true, fmt"✅ Migrated {migrationCount} flag(s) in {actualOutputPath}")
else:
return (true, fmt" No legacy flags found in {inputPath}")
except IOError as e:
return (false, fmt"Failed to migrate config: {e.msg}")
proc createMigrationBackup*(filePath: string): bool =
## Create a backup of a file before migration
if not fileExists(filePath):
return false
let backupPath = filePath & ".backup"
try:
copyFile(filePath, backupPath)
return true
except:
return false
# #############################################################################
# CLI Helper Functions
# #############################################################################
proc suggestDomainFlags*(legacyFlags: seq[string]): seq[string] =
## Suggest domain-scoped equivalents for legacy flags
result = @[]
for flagStr in legacyFlags:
let translated = translateFlagString(flagStr)
if translated != flagStr:
result.add(translated)
else:
result.add(flagStr) # Keep as-is if can't translate
proc printMigrationHelp*() =
## Print help for migration command
echo """
🔄 NIP Flag Migration Utility
USAGE:
nip migrate-flags [options] [file]
OPTIONS:
--dry-run Show what would be migrated without making changes
--backup Create backup before migration (default: true)
--output <file> Write to different file instead of overwriting
--help Show this help
EXAMPLES:
# Migrate config file
nip migrate-flags ~/.nip/config
# Dry run to see changes
nip migrate-flags --dry-run ~/.nip/config
# Migrate to new file
nip migrate-flags --output new-config.conf old-config.conf
LEGACY CATEGORIES → NEW DOMAINS:
gui → graphics
gaming → graphics
container → integration
virtualization → integration
mesh → network
ai-ml → runtime
bindings → runtime
features → runtime
init → init (unchanged)
audio → audio (unchanged)
optimization → optimization (unchanged)
security → security (unchanged)
SYNTAX CHANGES:
OLD: gui/wayland
NEW: +graphics=wayland
OLD: optimization/lto
NEW: +optimization=lto
"""