| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package codifier |
| |
| import ( |
| "bufio" |
| "fmt" |
| "io" |
| "os" |
| "strings" |
| ) |
| |
| // orderedStrings provides list functions that maintain an unsorted, ordered |
| // slice of strings, allowing additions of non-present, non-blank strings. That |
| // is, the list will be an ordered set provided that the list these functions |
| // are applied to is already an ordered set. File reading and appending |
| // functions are provided. |
| type orderedStrings []string |
| |
| // contains returns whether the list contains the item. |
| func (o orderedStrings) contains(item string) bool { |
| for _, l := range o { |
| if l == item { |
| return true |
| } |
| } |
| return false |
| } |
| |
| // Add appends the item(s) to the list if they are neither blank nor already |
| // present. Returns true iff all of the items are added. |
| // |
| // TODO(gboone@): This function is exported currently so that files can be added |
| // to changedFiles list by external functions. But changes to internal data |
| // should be made through Proc operators. Add operators to wrap these functions, |
| // eg (p *Proc) AddChangedFile(f string), to ordered_list_ops.go for managing |
| // the changedFiles list. Then unexport this function. |
| func (o *orderedStrings) Add(items ...string) bool { |
| if len(items) == 0 { |
| return false |
| } |
| allAdded := true |
| for _, item := range items { |
| if item == "" || o.contains(item) { |
| allAdded = false |
| continue |
| } |
| *o = append(*o, item) |
| } |
| return allAdded |
| } |
| |
| func orderedStringsLoad(r io.Reader) (orderedStrings, error) { |
| scanner := bufio.NewScanner(r) |
| var o orderedStrings |
| for scanner.Scan() { |
| line := scanner.Text() |
| if l := strings.Index(line, "#"); l != -1 { |
| line = line[:l] |
| } |
| if line = strings.Trim(line, "\t "); line != "" { |
| if !o.Add(line) { |
| return nil, fmt.Errorf("found duplicate entry %q", line) |
| } |
| } |
| } |
| return o, scanner.Err() |
| } |
| |
| // orderedStringsFromFile reads the lines into a string slice, stripping comment lines |
| // starting with '#' and text after '#' on lines. |
| func orderedStringsFromFile(filename string) (orderedStrings, error) { |
| filePath, err := resolveGnPath("", filename) |
| if err != nil { |
| return nil, fmt.Errorf("can't resolve path for filename %q: %w", filename, err) |
| } |
| f, err := os.Open(filePath) |
| if err != nil { |
| return nil, err |
| } |
| defer f.Close() |
| |
| return orderedStringsLoad(f) |
| } |
| |
| // findMissing returns the first candidate that isn't present in the named file. |
| // Return the index and the picked value, a boolean indicating whether one was |
| // found, and any error. |
| func findMissing(candidates []string, fileName string) (int, string, bool, error) { |
| skiplist, err := orderedStringsFromFile(fileName) |
| if err != nil { |
| return 0, "", false, err |
| } |
| for i, candidate := range candidates { |
| if !skiplist.contains(candidate) { |
| return i, candidate, true, nil |
| } |
| } |
| return 0, "", false, nil |
| } |
| |
| // GoString serializes the string. Use like `fmt.Printf("%#v", o)`. |
| func (o orderedStrings) GoString() string { |
| out := "" |
| for i, item := range o { |
| out += fmt.Sprintf(" %d: %q", i, item) |
| if len(o)-1 != i { |
| out += "\n" |
| } |
| } |
| return out |
| } |