Last active
June 27, 2021 11:50
-
-
Save SamWhited/488e732e127629212af53016ea7c780f to your computer and use it in GitHub Desktop.
XEP Deps
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module mellium.im/xepdeps | |
go 1.17 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// The xepdeps command prints a list of dependencies for the input XEP. | |
// You can use this file by doing something like "go run xepdeps.go myxep.xml | | |
// dot -Tpng -omyxep.png". | |
package main | |
import ( | |
"encoding/xml" | |
"flag" | |
"fmt" | |
"io" | |
"log" | |
"os" | |
"strings" | |
) | |
type xep struct { | |
XMLName xml.Name `xml:"xep"` | |
Deps []string `xml:"header>dependencies>spec"` | |
} | |
func main() { | |
logger := log.New(os.Stderr, "", 0) | |
debug := log.New(io.Discard, "DEBUG ", 0) | |
flags := flag.NewFlagSet(os.Args[0], flag.ContinueOnError) | |
var ( | |
xepOnly bool | |
verbose bool | |
) | |
flags.BoolVar(&xepOnly, "xeponly", xepOnly, "hide RFCs from output graph") | |
flags.BoolVar(&verbose, "v", verbose, "print debug output") | |
err := flags.Parse(os.Args[1:]) | |
if err != nil { | |
logger.Fatalf("error parsing flags: %v", err) | |
} | |
if len(flags.Args()) < 1 { | |
logger.Fatal("usage: xepdeps xep.xml") | |
} | |
if verbose { | |
debug.SetOutput(os.Stderr) | |
} | |
fmt.Println("digraph {") | |
_, err = printDeps(flags.Args()[0], xepOnly, debug, nil) | |
if err != nil { | |
logger.Fatalf("error parsing XEP: %v", err) | |
} | |
fmt.Println("}") | |
} | |
func printDeps(filename string, xepOnly bool, debug *log.Logger, done []string) ([]string, error) { | |
for _, d := range done { | |
if filename == d { | |
debug.Printf("skipping already completed path: %v", filename) | |
return done, nil | |
} | |
} | |
var x xep | |
err := func() error { | |
debug.Printf("opening %q…", filename) | |
fd, err := os.Open(filename) | |
if err != nil { | |
return fmt.Errorf("error opening xep: %v", err) | |
} | |
/* #nosec */ | |
defer fd.Close() | |
d := xml.NewDecoder(fd) | |
d.Strict = false | |
var start xml.StartElement | |
for { | |
tok, err := d.Token() | |
if err != nil { | |
return fmt.Errorf("error decoding XEP token: %v", err) | |
} | |
var ok bool | |
if start, ok = tok.(xml.StartElement); ok && start.Name.Local == "xep" { | |
break | |
} | |
} | |
err = d.DecodeElement(&x, &start) | |
if err != nil { | |
return fmt.Errorf("error decoding XEP: %v", err) | |
} | |
return nil | |
}() | |
if err != nil { | |
return done, nil | |
} | |
if xepOnly { | |
// Filter out non-XEP deps. | |
// We do this here instead of in the loop below to ensure that we can know | |
// there are no deps and add a node with no edges if so. | |
b := x.Deps[:0] | |
for _, dep := range x.Deps { | |
if strings.HasPrefix(dep, "XEP-") { | |
b = append(b, dep) | |
continue | |
} | |
debug.Printf("skipping non-XEP dep on %q…", dep) | |
} | |
x.Deps = b | |
} | |
if len(x.Deps) == 0 { | |
debug.Printf("no deps found.") | |
fmt.Printf("%q\n", strings.ToUpper(strings.TrimSuffix(filename, ".xml"))) | |
return done, nil | |
} | |
for _, dep := range x.Deps { | |
fmt.Printf("%q -> %q\n", strings.ToUpper(strings.TrimSuffix(filename, ".xml")), normalize(dep)) | |
if strings.HasPrefix(dep, "XEP-") { | |
done, err = printDeps(toFile(dep), xepOnly, debug, append(done, filename)) | |
if err != nil { | |
return done, nil | |
} | |
} | |
} | |
return done, nil | |
} | |
func normalize(dep string) string { | |
// Trim out any " (required)" or " (optional)" or the like after the dep name. | |
idx := strings.IndexByte(dep, ' ') | |
if idx == -1 { | |
idx = len(dep) | |
} | |
return dep[:idx] | |
} | |
func toFile(dep string) string { | |
return strings.ToLower(normalize(dep)) + ".xml" | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment