package archive import ( "strings" ) // Resolve parses the .archive request filename and returns the server-relative // redirect target URL (no leading slash) within the named project. // // Project is the top-level segment of the .archive contextPath // (//.../.archive/). An empty project — i.e. a request // against /.archive/ at the very root — returns ("", false): stable refs // must be project-rooted to avoid cross-project tracking-number collisions. // // Supported URL filename patterns (after stripping .html suffix): // - trackingNumber → highest base revision of trackingNumber // - trackingNumber_rev → base revision file for rev // - trackingNumber_rev+C1 → modifier file (C1, B1, N1, Q1) // // Returns ("", false) if project is empty, the filename cannot be parsed, or // no match exists in the project. func Resolve(idx *Index, project, filename string) (string, bool) { if project == "" { return "", false } // Strip .html suffix stem := strings.TrimSuffix(filename, ".html") if stem == filename { // No .html suffix — not a valid archive request return "", false } idx.mu.RLock() defer idx.mu.RUnlock() pe, ok := idx.ByProject[project] if !ok { return "", false } // Try to split off revision part (last _ segment) lastUnderscore := strings.LastIndex(stem, "_") if lastUnderscore < 0 { // No underscore — treat entire stem as tracking number tracking := stem te, ok := pe.ByTracking[tracking] if !ok || te.HighestBaseRev == "" { return "", false } re, ok := te.ByRevision[te.HighestBaseRev] if !ok || re.BasePath == "" { return "", false } return re.BasePath, true } tracking := stem[:lastUnderscore] revPart := stem[lastUnderscore+1:] // Split revPart on "+" to separate baseRev from modifier plusIdx := strings.Index(revPart, "+") var baseRev, modifier string if plusIdx < 0 { baseRev = revPart modifier = "" } else { baseRev = revPart[:plusIdx] modifier = revPart[plusIdx+1:] } te, ok := pe.ByTracking[tracking] if !ok { return "", false } re, ok := te.ByRevision[baseRev] if !ok { return "", false } if modifier == "" { if re.BasePath == "" { return "", false } return re.BasePath, true } // Modifier lookup path, ok := re.Modifiers[modifier] if !ok { return "", false } return path, true }