11package cli
22
33import (
4- "encoding/csv "
4+ "io "
55 "log/slog"
66 "os"
77 "path/filepath"
8- "sort"
98 "strings"
109
1110 "github.com/project-chip/alchemy/zapdiff"
@@ -16,17 +15,19 @@ type ZAPDiff struct {
1615 XmlRoot2 string `help:"root of second set of ZAP XMLs" group:"SDK Commands:" required:"true"`
1716 Label1 string `default:"ZapXML-1" help:"label for first set of ZAP XMLs" group:"SDK Commands:"`
1817 Label2 string `default:"ZapXML-2" help:"label for second set of ZAP XMLs" group:"SDK Commands:"`
19- Out string `default:"." help:"path to output mismatch.csv file" group:"SDK Commands:"`
18+ Out string `default:"." help:"path to output mismatch files" group:"SDK Commands:"`
19+ Format string `default:"both" help:"output format: csv, html, or both" group:"SDK Commands:"`
2020 MismatchLevel int `default:"3" help:"the minimum mismatch level to report (1-3)" group:"SDK Commands:"`
2121}
2222
2323func (z * ZAPDiff ) Run (cc * Context ) (err error ) {
24+
2425 var mismatchPrintLevel zapdiff.XmlMismatchLevel
2526 if z .MismatchLevel < 1 || z .MismatchLevel > 3 {
2627 slog .Warn ("invalid mismatch level. must be between 1 and 3." , "level" , z .MismatchLevel )
2728 mismatchPrintLevel = zapdiff .MismatchLevel3 // Default
2829 } else {
29- mismatchPrintLevel = zapdiff .XmlMismatchLevel (z . MismatchLevel - 1 ) // Convert 1-3 to 0-2
30+ mismatchPrintLevel = zapdiff .XmlMismatchLevel (3 - z . MismatchLevel )
3031 }
3132
3233 ff1 , err := listXMLFiles (z .XmlRoot1 )
@@ -43,16 +44,37 @@ func (z *ZAPDiff) Run(cc *Context) (err error) {
4344
4445 mm := zapdiff .Pipeline (ff1 , ff2 , z .Label1 , z .Label2 )
4546
46- csvOutputPath := filepath .Join (z .Out , "mismatches.csv" )
47- err = writeMismatchesToCSV (csvOutputPath , mm , mismatchPrintLevel )
48- if err != nil {
49- slog .Error ("Failed to write CSV output" , "error" , err )
47+ generateCSV := z .Format == "csv" || z .Format == "both" || z .Format == ""
48+ if generateCSV {
49+ err = writeMismatchesFile (z .Out , "mismatches.csv" , "CSV" , func (w io.Writer ) error {
50+ return zapdiff .WriteMismatchesToCSV (w , mm , mismatchPrintLevel )
51+ })
52+ if err != nil {
53+ return err
54+ }
5055 }
5156
52- return
57+ generateHTML := z .Format == "html" || z .Format == "both" || z .Format == ""
58+ if generateHTML {
59+ err = writeMismatchesFile (z .Out , "mismatches.html" , "HTML" , func (w io.Writer ) error {
60+ return zapdiff .WriteMismatchesToHTML (w , mm , mismatchPrintLevel , z .XmlRoot1 , z .XmlRoot2 )
61+ })
62+ if err != nil {
63+ return err
64+ }
65+ }
66+
67+ return nil
5368}
5469
5570func listXMLFiles (p string ) (paths []string , err error ) {
71+ fi , err := os .Stat (p )
72+ if err != nil {
73+ return nil , err
74+ }
75+ if ! fi .IsDir () {
76+ return []string {p }, nil
77+ }
5678 var entries []os.DirEntry
5779 entries , err = os .ReadDir (p )
5880 if err != nil {
@@ -68,63 +90,19 @@ func listXMLFiles(p string) (paths []string, err error) {
6890 return
6991}
7092
71- func writeMismatchesToCSV (p string , mm []zapdiff.XmlMismatch , l zapdiff.XmlMismatchLevel ) (err error ) {
72- f , err := os .Create (p )
93+ func writeMismatchesFile (outDir string , filename string , formatName string , writeFn func (io.Writer ) error ) error {
94+ outputPath := filepath .Join (outDir , filename )
95+ f , err := os .Create (outputPath )
7396 if err != nil {
74- slog .Error ("failed to create file" , "path" , p , "error" , err )
97+ slog .Error ("failed to create " + formatName + " file" , "path" , outputPath , "error" , err )
7598 return err
7699 }
77100 defer f .Close ()
78-
79- w := csv .NewWriter (f )
80- defer func () {
81- w .Flush ()
82- if err == nil {
83- err = w .Error ()
84- }
85- }()
86-
87- // Write header
88- header := []string {"Level" , "Type" , "File" , "Element Xpath" , "Details" }
89- if err = w .Write (header ); err != nil {
90- slog .Error ("failed to write CSV header" , "error" , err )
91- return
92- }
93-
94- sort .Slice (mm , func (i , j int ) bool {
95- // Level (Descending), Path, Type, ElementID, Details
96- if mm [i ].Level () != mm [j ].Level () {
97- return mm [i ].Level () > mm [j ].Level ()
98- }
99- if mm [i ].Path != mm [j ].Path {
100- return mm [i ].Path < mm [j ].Path
101- }
102- if mm [i ].Type != mm [j ].Type {
103- return mm [i ].Type .String () < mm [j ].Type .String ()
104- }
105- if mm [i ].ElementID != mm [j ].ElementID {
106- return mm [i ].ElementID < mm [j ].ElementID
107- }
108- return mm [i ].Details < mm [j ].Details
109- })
110-
111- // Write mismatches
112- for _ , m := range mm {
113- if m .Level () >= l {
114- row := []string {
115- m .Level ().String (),
116- m .Type .String (),
117- m .Path ,
118- m .ElementID ,
119- m .Details ,
120- }
121- if err = w .Write (row ); err != nil {
122- slog .Error ("Warning: failed to write row to CSV" , "err" , err )
123- return
124- }
125- }
101+ err = writeFn (f )
102+ if err != nil {
103+ slog .Error ("Failed to write " + formatName + " output" , "error" , err )
104+ } else {
105+ slog .Info ("Successfully wrote mismatches to " + formatName , "dir" , outputPath )
126106 }
127-
128- slog .Info ("Successfully wrote mismatches to CSV" , "dir" , p )
129- return
107+ return nil
130108}
0 commit comments