11package main
22
33import (
4- "encoding/json"
5- "fmt"
6- "io"
7- "os"
8- "sort"
9- "strings"
10- "time"
11-
12- "gopkg.in/yaml.v3"
13-
144 "github.com/plexusone/omnivault/internal/config"
155 "github.com/plexusone/omnivault/internal/daemon"
6+ "github.com/plexusone/omnivault/internal/output"
167)
178
18- // OutputFormat represents the output format type .
19- type OutputFormat int
9+ // OutputFormat is an alias to internal/ output.Format for backwards compatibility .
10+ type OutputFormat = output. Format
2011
12+ // Format constants for backwards compatibility.
2113const (
22- // FormatText is the default human-readable format.
23- FormatText OutputFormat = iota
24- // FormatJSON outputs JSON.
25- FormatJSON
26- // FormatYAML outputs YAML.
27- FormatYAML
28- // FormatShell outputs shell-sourceable format.
29- FormatShell
14+ FormatText = output .FormatText
15+ FormatJSON = output .FormatJSON
16+ FormatYAML = output .FormatYAML
17+ FormatShell = output .FormatShell
3018)
3119
32- // ParseFormat parses a format string into an OutputFormat .
20+ // ParseFormat delegates to internal/output.ParseFormat .
3321func ParseFormat (s string ) OutputFormat {
34- switch strings .ToLower (s ) {
35- case "json" :
36- return FormatJSON
37- case "yaml" , "yml" :
38- return FormatYAML
39- case "shell" , "sh" , "bash" :
40- return FormatShell
41- default :
42- return FormatText
43- }
22+ return output .ParseFormat (s )
4423}
4524
46- // OutputWriter writes formatted output.
25+ // OutputWriter wraps internal/ output.Writer with cmd-specific functionality .
4726type OutputWriter struct {
48- format OutputFormat
49- w io.Writer
27+ * output.Writer
5028}
5129
5230// NewOutputWriter creates a new OutputWriter.
5331func NewOutputWriter (format OutputFormat ) * OutputWriter {
5432 return & OutputWriter {
55- format : format ,
56- w : os .Stdout ,
33+ Writer : output .NewWriter (format ),
5734 }
5835}
5936
@@ -72,231 +49,21 @@ func NewOutputWriterFromFlags(flags *ParsedFlags) *OutputWriter {
7249}
7350
7451// WriteSecret writes a secret in the configured format.
75- // If fieldName is non-empty, only that field is output.
7652func (o * OutputWriter ) WriteSecret (secret * daemon.SecretResponse , fieldName string ) error {
77- // Field extraction mode
78- if fieldName != "" {
79- return o .writeFieldValue (secret , fieldName )
80- }
81-
82- switch o .format {
83- case FormatJSON :
84- return o .writeJSON (secret )
85- case FormatYAML :
86- return o .writeYAML (secret )
87- case FormatShell :
88- return o .writeShellSecret (secret )
89- default :
90- return o .writeTextSecret (secret )
91- }
92- }
93-
94- // writeFieldValue outputs only a specific field value.
95- func (o * OutputWriter ) writeFieldValue (secret * daemon.SecretResponse , fieldName string ) error {
96- var value string
97-
98- if fieldName == "value" || fieldName == "" {
99- value = secret .Value
100- } else if secret .Fields != nil {
101- value = secret .Fields [fieldName ]
102- }
103-
104- switch o .format {
105- case FormatJSON :
106- return o .writeJSON (map [string ]string {fieldName : value })
107- case FormatYAML :
108- return o .writeYAML (map [string ]string {fieldName : value })
109- case FormatShell :
110- fmt .Fprintf (o .w , "export %s=%s\n " , shellSafeKey (fieldName ), shellQuote (value ))
111- return nil
112- default :
113- fmt .Fprintln (o .w , value )
114- return nil
115- }
116- }
117-
118- // writeTextSecret writes a secret in human-readable text format.
119- func (o * OutputWriter ) writeTextSecret (secret * daemon.SecretResponse ) error {
120- if secret .Value != "" {
121- fmt .Fprintln (o .w , secret .Value )
122- }
123-
124- if len (secret .Fields ) > 0 {
125- keys := sortedKeys (secret .Fields )
126- for _ , k := range keys {
127- fmt .Fprintf (o .w , "%s: %s\n " , k , secret .Fields [k ])
128- }
129- }
130-
131- return nil
132- }
133-
134- // writeShellSecret writes a secret as shell-sourceable export statements.
135- func (o * OutputWriter ) writeShellSecret (secret * daemon.SecretResponse ) error {
136- // Use path as variable name prefix
137- prefix := shellSafeKey (secret .Path )
138-
139- if secret .Value != "" {
140- fmt .Fprintf (o .w , "export %s=%s\n " , prefix , shellQuote (secret .Value ))
141- }
142-
143- if len (secret .Fields ) > 0 {
144- keys := sortedKeys (secret .Fields )
145- for _ , k := range keys {
146- varName := prefix + "_" + shellSafeKey (k )
147- fmt .Fprintf (o .w , "export %s=%s\n " , varName , shellQuote (secret .Fields [k ]))
148- }
149- }
150-
151- return nil
53+ return o .Writer .WriteSecret (secret , fieldName )
15254}
15355
15456// WriteList writes a list response in the configured format.
15557func (o * OutputWriter ) WriteList (resp * daemon.ListResponse , showMetadata bool ) error {
156- switch o .format {
157- case FormatJSON :
158- return o .writeJSON (resp )
159- case FormatYAML :
160- return o .writeYAML (resp )
161- default :
162- return o .writeTextList (resp , showMetadata )
163- }
164- }
165-
166- // writeTextList writes a list in human-readable text format.
167- func (o * OutputWriter ) writeTextList (resp * daemon.ListResponse , showMetadata bool ) error {
168- if resp .Count == 0 {
169- fmt .Fprintln (o .w , "No secrets found" )
170- return nil
171- }
172-
173- for _ , item := range resp .Secrets {
174- typeIndicator := ""
175- if item .HasValue && item .HasFields {
176- typeIndicator = " (value+fields)"
177- } else if item .HasFields {
178- typeIndicator = " (fields)"
179- }
180-
181- tagStr := ""
182- if len (item .Tags ) > 0 {
183- if showMetadata && len (item .TagsMap ) > 0 {
184- // Show full key=value pairs
185- var pairs []string
186- for k , v := range item .TagsMap {
187- pairs = append (pairs , fmt .Sprintf ("%s=%s" , k , v ))
188- }
189- sort .Strings (pairs )
190- tagStr = fmt .Sprintf (" [%s]" , strings .Join (pairs , ", " ))
191- } else {
192- tagStr = fmt .Sprintf (" [%s]" , strings .Join (item .Tags , ", " ))
193- }
194- }
195-
196- fmt .Fprintf (o .w , "%s%s%s\n " , item .Path , typeIndicator , tagStr )
197-
198- if showMetadata {
199- if ! item .CreatedAt .IsZero () {
200- fmt .Fprintf (o .w , " Created: %s\n " , item .CreatedAt .Format (time .RFC3339 ))
201- }
202- if ! item .UpdatedAt .IsZero () {
203- fmt .Fprintf (o .w , " Updated: %s\n " , item .UpdatedAt .Format (time .RFC3339 ))
204- }
205- if ! item .ExpiresAt .IsZero () {
206- fmt .Fprintf (o .w , " Expires: %s\n " , item .ExpiresAt .Format (time .RFC3339 ))
207- }
208- }
209- }
210-
211- fmt .Fprintf (o .w , "\n %d secret(s)\n " , resp .Count )
212- return nil
58+ return o .Writer .WriteList (resp , showMetadata )
21359}
21460
21561// WriteStatus writes a status response in the configured format.
21662func (o * OutputWriter ) WriteStatus (status * daemon.StatusResponse , daemonRunning bool ) error {
217- switch o .format {
218- case FormatJSON :
219- return o .writeJSON (status )
220- case FormatYAML :
221- return o .writeYAML (status )
222- default :
223- return o .writeTextStatus (status , daemonRunning )
224- }
63+ return o .Writer .WriteStatus (status , daemonRunning )
22564}
22665
227- // writeTextStatus writes status in human-readable text format.
228- func (o * OutputWriter ) writeTextStatus (status * daemon.StatusResponse , daemonRunning bool ) error {
229- if ! daemonRunning {
230- fmt .Fprintln (o .w , "Daemon: not running" )
231- return nil
232- }
233-
234- fmt .Fprintln (o .w , "Daemon: running" )
235- fmt .Fprintf (o .w , "Uptime: %s\n " , status .Uptime )
236-
237- if ! status .VaultExists {
238- fmt .Fprintln (o .w , "Vault: not initialized" )
239- return nil
240- }
241-
242- if status .Locked {
243- fmt .Fprintln (o .w , "Vault: locked" )
244- } else {
245- fmt .Fprintln (o .w , "Vault: unlocked" )
246- fmt .Fprintf (o .w , "Secrets: %d\n " , status .SecretCount )
247- if ! status .UnlockedAt .IsZero () {
248- fmt .Fprintf (o .w , "Unlocked at: %s\n " , status .UnlockedAt .Format ("2006-01-02 15:04:05" ))
249- }
250- }
251-
252- return nil
253- }
254-
255- // writeJSON writes data as JSON.
256- func (o * OutputWriter ) writeJSON (data any ) error {
257- enc := json .NewEncoder (o .w )
258- enc .SetIndent ("" , " " )
259- return enc .Encode (data )
260- }
261-
262- // writeYAML writes data as YAML.
263- func (o * OutputWriter ) writeYAML (data any ) error {
264- enc := yaml .NewEncoder (o .w )
265- enc .SetIndent (2 )
266- return enc .Encode (data )
267- }
268-
269- // shellSafeKey converts a string to a safe shell variable name.
270- func shellSafeKey (s string ) string {
271- var result strings.Builder
272- for i , r := range s {
273- if (r >= 'A' && r <= 'Z' ) || (r >= 'a' && r <= 'z' ) || r == '_' {
274- result .WriteRune (r )
275- } else if r >= '0' && r <= '9' {
276- if i == 0 {
277- result .WriteRune ('_' )
278- }
279- result .WriteRune (r )
280- } else {
281- result .WriteRune ('_' )
282- }
283- }
284- return strings .ToUpper (result .String ())
285- }
286-
287- // shellQuote quotes a string for safe shell use.
288- func shellQuote (s string ) string {
289- // Use single quotes and escape any single quotes in the string
290- escaped := strings .ReplaceAll (s , "'" , "'\" '\" '" )
291- return "'" + escaped + "'"
292- }
293-
294- // sortedKeys returns the sorted keys of a map.
295- func sortedKeys (m map [string ]string ) []string {
296- keys := make ([]string , 0 , len (m ))
297- for k := range m {
298- keys = append (keys , k )
299- }
300- sort .Strings (keys )
301- return keys
66+ // WriteSearch writes a search response in the configured format.
67+ func (o * OutputWriter ) WriteSearch (resp * daemon.SearchResponse ) error {
68+ return o .Writer .WriteSearch (resp )
30269}
0 commit comments