go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/client/cmd/cas/casimpl/scattergather.go (about) 1 // Copyright 2017 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package casimpl 16 17 import ( 18 "fmt" 19 "path/filepath" 20 "strings" 21 22 "go.chromium.org/luci/common/errors" 23 ) 24 25 // scatterGather represents a mapping of working directories to relative paths. 26 // 27 // The purpose is to represent some notion of "local" vs. "archived" paths. 28 // All relative paths are relative to both their corresponding working 29 // directories as well as the root of an archive. 30 // 31 // filepath.Join(working dir, relative path) == location of file or directory 32 // on the system. 33 // 34 // relative path == location of file or directory in an archive. 35 // 36 // Notably, in such a design, we may not have more than one copy of a relative 37 // path in the archive, because there is a conflict. In order to efficiently 38 // check this case at the expense of extra memory, scatterGather actually 39 // stores a mapping of relative paths to working directories. 40 type scatterGather map[string]string 41 42 // Add adds a (working directory, relative path) pair to the ScatterGather. 43 // 44 // Add returns an error if the relative path was already added. 45 func (sc *scatterGather) Add(wd, rel string) error { 46 cleaned := filepath.Clean(rel) 47 if _, ok := (*sc)[cleaned]; ok { 48 return errors.Reason("name conflict %q", rel).Err() 49 } 50 (*sc)[cleaned] = wd 51 return nil 52 } 53 54 // Set implements the flags.Var interface. 55 func (sc *scatterGather) Set(value string) error { 56 colon := strings.LastIndexByte(value, ':') 57 if colon == -1 { 58 return errors.Reason("malformed input %q", value).Err() 59 } 60 if *sc == nil { 61 *sc = scatterGather{} 62 } 63 return sc.Add(value[:colon], value[colon+1:]) 64 } 65 66 // String implements the Stringer interface. 67 func (sc *scatterGather) String() string { 68 mapping := make(map[string][]string, len(*sc)) 69 for item, wd := range *sc { 70 mapping[wd] = append(mapping[wd], item) 71 } 72 return fmt.Sprintf("%v", mapping) 73 }