blob: 6859e2cb06930184768e4195176130ed7cd68653 [file] [log] [blame]
// Copyright 2021 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.
/*
Contains the code to sort elements by name according to somewhat specific rules.
See elementSlice below for details.
*/
package summarize
import (
"sort"
"strings"
"go.fuchsia.dev/fuchsia/tools/fidl/lib/fidlgen"
)
// infty is a maximal string.
const infty = "\xff"
// inftyStr maps "" to a maximal string, used to sort the Elements in a specific way.
func inftyStr(s fidlgen.Identifier) fidlgen.Identifier {
if s == "" {
return infty
}
return s
}
// elementSlice is used to sort a slice of Elements.
//
// The identifiers are sorted as follows: compare libraries first; if
// equal, compare declarations; if equal compare members. Exceptions: a
// library always compares last.
type elementSlice []Element
var _ sort.Interface = (*elementSlice)(nil)
func (e elementSlice) Len() int {
return len(e)
}
func (e elementSlice) Less(i, j int) bool {
ni := newFqn(e[i].Name())
nj := newFqn(e[j].Name())
return ni.Less(nj)
}
func (e elementSlice) Swap(i, j int) {
e[j], e[i] = e[i], e[j]
}
// fqn is a fully qualified name for a declaration. "library/decl.name".
// The fully qualified names are sorted as follows: compare libraries first; if
// equal, compare declarations; if equal compare members. Exceptions: a library
// always compares last.
type fqn fidlgen.CompoundIdentifier
// newFqn parses out a fqn from the given string.
func newFqn(name Name) fqn {
ci := fidlgen.ParseCompoundIdentifier(fidlgen.EncodedCompoundIdentifier(name))
// Convert empty strings to a "maximal" marker to get the expected sort ordering.
// If 'name' is a library, it will end up in ci.Name. Otherwise it will end
// up parsed in ci.Library.
ci.Name = inftyStr(ci.Name)
ci.Member = inftyStr(ci.Member)
return fqn(ci)
}
func (f fqn) isLibrary() bool {
// This is apparently how compound identifiers encode libraries.
return len(f.Library) == 1 && f.Library[0] == ""
}
func (f fqn) name() string {
if f.isLibrary() {
return infty
}
return string(f.Name)
}
func (f fqn) library() string {
if f.isLibrary() {
// If f.Library is empty, then this fqn contains a library identifier.
return string(f.Name)
}
// Couldn't find a way to recover the dotted notation from library name.
var ret []string
for _, c := range f.Library {
ret = append(ret, string(c))
}
return strings.Join(ret, ".")
}
func (this fqn) Less(that fqn) bool {
// Libraries compare regularly.
if this.isLibrary() && that.isLibrary() {
return this.Name < that.Name
}
// Special: library is always last if compared to a declaration.
if this.isLibrary() {
return false
}
if that.isLibrary() {
return true
}
// The rest of the comparison is as expected.
thisLib := this.library()
thatLib := that.library()
if thisLib == thatLib {
if this.name() == that.name() {
return this.Member < that.Member
}
return this.name() < that.name()
}
return thisLib < thatLib
}