package resolver import ( "fmt" "strings" "sync" "time" "zsvo/pkg/cache" "zsvo/pkg/deps" ) // FastResolver provides high-performance dependency resolution using cached indices type FastResolver struct { indexCache *cache.IndexCache packageIndex *PackageIndex mu sync.RWMutex } // PackageIndex wraps the cache PackageIndex with additional resolver functionality type PackageIndex struct { *cache.PackageIndex } // NewFastResolver creates a new fast dependency resolver func NewFastResolver(cacheDir string) *FastResolver { return &FastResolver{ indexCache: cache.NewIndexCache(cacheDir), } } // LoadIndex loads the package index for the given repository configuration func (r *FastResolver) LoadIndex(mirror, suite, component string) error { r.mu.Lock() defer r.mu.Unlock() idx, err := r.indexCache.GetIndex(mirror, suite, component) if err != nil { return err } r.packageIndex = &PackageIndex{PackageIndex: idx} return nil } // ResolvePackage resolves a package name to its source information in O(1) time func (r *FastResolver) ResolvePackage(name string) (*SourcePackage, error) { r.mu.RLock() defer r.mu.RUnlock() if r.packageIndex == nil { return nil, fmt.Errorf("no package index loaded") } // Try direct package name lookup first entry, found := r.packageIndex.LookupPackage(name) if found { return r.entryToSourcePackage(entry), nil } // Try binary package lookup source, found := r.packageIndex.LookupBinary(name) if found { entry, found = r.packageIndex.LookupPackage(source) if found { return r.entryToSourcePackage(entry), nil } } return nil, fmt.Errorf("package %s not found", name) } // ResolveDependencies recursively resolves all dependencies for a package func (r *FastResolver) ResolveDependencies(rootPackage string) (*DependencyGraph, error) { r.mu.RLock() defer r.mu.RUnlock() if r.packageIndex == nil { return nil, fmt.Errorf("no package index loaded") } graph := NewDependencyGraph() visited := make(map[string]bool) // Start recursive resolution if err := r.resolveDependenciesRecursive(rootPackage, graph, visited, nil); err != nil { return nil, err } // Calculate build order if err := graph.CalculateBuildOrder(); err != nil { return nil, err } return graph, nil } // resolveDependenciesRecursive builds the dependency graph recursively func (r *FastResolver) resolveDependenciesRecursive(pkgName string, graph *DependencyGraph, visited map[string]bool, path []string) error { // Check for cycles for _, p := range path { if p == pkgName { return fmt.Errorf("dependency cycle detected: %s -> %s", strings.Join(path, " -> "), pkgName) } } // Skip if already visited if visited[pkgName] { return nil } visited[pkgName] = true // Resolve the package pkg, err := r.ResolvePackage(pkgName) if err != nil { return err } // Add to graph graph.AddPackage(pkg) // Recursively resolve dependencies for _, dep := range pkg.BuildDepends { depName := extractPackageName(dep) if depName == "" { continue } // Skip if it's a system package that doesn't need building if isSystemPackage(depName) { continue } // Add dependency relationship graph.AddDependency(pkgName, depName) // Recursively resolve newPath := append(path, pkgName) if err := r.resolveDependenciesRecursive(depName, graph, visited, newPath); err != nil { return err } } return nil } // GetBuildOrder returns the packages in build order (dependencies first) func (r *FastResolver) GetBuildOrder(rootPackage string) ([]*SourcePackage, error) { graph, err := r.ResolveDependencies(rootPackage) if err != nil { return nil, err } return graph.GetBuildOrder(), nil } // entryToSourcePackage converts a cache entry to a SourcePackage func (r *FastResolver) entryToSourcePackage(entry *cache.PackageEntry) *SourcePackage { return &SourcePackage{ Name: entry.Package, Version: entry.Version, Directory: entry.Directory, DSCName: entry.DSCName, DSCSHA256: entry.DSCSHA256, Binaries: entry.Binaries, BuildDepends: entry.BuildDepends, } } // AddPackage adds a package to the current index (for updating cache) func (r *FastResolver) AddPackage(entry *cache.PackageEntry) error { r.mu.Lock() defer r.mu.Unlock() if r.packageIndex == nil { return fmt.Errorf("no package index loaded") } r.packageIndex.AddPackage(entry) return nil } // GetStats returns resolver statistics func (r *FastResolver) GetStats() (int, int, bool) { r.mu.RLock() defer r.mu.RUnlock() if r.packageIndex == nil { return 0, 0, false } pkgCount, binCount := r.packageIndex.GetStats() expired := r.packageIndex.IsExpired(24 * time.Hour) // Consider expired after 24 hours return pkgCount, binCount, expired } // SaveIndex persists the current index to disk func (r *FastResolver) SaveIndex(mirror, suite, component string) error { r.mu.RLock() defer r.mu.RUnlock() if r.packageIndex == nil { return fmt.Errorf("no package index loaded") } cacheFile := r.indexCache.CacheFilePath(mirror, suite, component) return r.packageIndex.Save(cacheFile) } // SourcePackage represents a Debian source package type SourcePackage struct { Name string Version string Directory string DSCName string DSCSHA256 string Binaries []string BuildDepends []string } // extractPackageName extracts package name from dependency string func extractPackageName(dep string) string { // Parse dependency constraints using existing deps package req, err := deps.ParseRequirement(dep) if err != nil { return "" } if len(req.Alternatives) > 0 { return req.Alternatives[0].Name // Take first alternative } return "" } // isSystemPackage checks if a package is a system package that doesn't need building func isSystemPackage(name string) bool { systemPackages := map[string]bool{ "gcc": true, "g++": true, "make": true, "bash": true, "glibc": true, "libc6": true, "libc-bin": true, "base-files": true, "base-passwd": true, "coreutils": true, "dash": true, "debianutils": true, "diffutils": true, "dpkg": true, "findutils": true, "grep": true, "gzip": true, "hostname": true, "init-system-helpers": true, "login": true, "ncurses-base": true, "ncurses-bin": true, "perl-base": true, "sed": true, "sysvinit": true, "util-linux": true, } return systemPackages[name] }