1package formatter
2
3import (
4	"fmt"
5	"sort"
6	"strings"
7	"time"
8
9	"github.com/docker/docker/api/types"
10	"github.com/docker/docker/pkg/stringid"
11	"github.com/docker/go-units"
12)
13
14const (
15	defaultBuildCacheTableFormat = "table {{.ID}}\t{{.Type}}\t{{.Size}}\t{{.CreatedSince}}\t{{.LastUsedSince}}\t{{.UsageCount}}\t{{.Shared}}\t{{.Description}}"
16
17	cacheIDHeader       = "CACHE ID"
18	cacheTypeHeader     = "CACHE TYPE"
19	parentHeader        = "PARENT"
20	lastUsedSinceHeader = "LAST USED"
21	usageCountHeader    = "USAGE"
22	inUseHeader         = "IN USE"
23	sharedHeader        = "SHARED"
24)
25
26// NewBuildCacheFormat returns a Format for rendering using a Context
27func NewBuildCacheFormat(source string, quiet bool) Format {
28	switch source {
29	case TableFormatKey:
30		if quiet {
31			return defaultQuietFormat
32		}
33		return Format(defaultBuildCacheTableFormat)
34	case RawFormatKey:
35		if quiet {
36			return `build_cache_id: {{.ID}}`
37		}
38		format := `build_cache_id: {{.ID}}
39parent_id: {{.Parent}}
40build_cache_type: {{.CacheType}}
41description: {{.Description}}
42created_at: {{.CreatedAt}}
43created_since: {{.CreatedSince}}
44last_used_at: {{.LastUsedAt}}
45last_used_since: {{.LastUsedSince}}
46usage_count: {{.UsageCount}}
47in_use: {{.InUse}}
48shared: {{.Shared}}
49`
50		return Format(format)
51	}
52	return Format(source)
53}
54
55func buildCacheSort(buildCache []*types.BuildCache) {
56	sort.Slice(buildCache, func(i, j int) bool {
57		lui, luj := buildCache[i].LastUsedAt, buildCache[j].LastUsedAt
58		switch {
59		case lui == nil && luj == nil:
60			return strings.Compare(buildCache[i].ID, buildCache[j].ID) < 0
61		case lui == nil:
62			return true
63		case luj == nil:
64			return false
65		case lui.Equal(*luj):
66			return strings.Compare(buildCache[i].ID, buildCache[j].ID) < 0
67		default:
68			return lui.Before(*luj)
69		}
70	})
71}
72
73// BuildCacheWrite renders the context for a list of containers
74func BuildCacheWrite(ctx Context, buildCaches []*types.BuildCache) error {
75	render := func(format func(subContext subContext) error) error {
76		buildCacheSort(buildCaches)
77		for _, bc := range buildCaches {
78			err := format(&buildCacheContext{trunc: ctx.Trunc, v: bc})
79			if err != nil {
80				return err
81			}
82		}
83		return nil
84	}
85	return ctx.Write(newBuildCacheContext(), render)
86}
87
88type buildCacheHeaderContext map[string]string
89
90type buildCacheContext struct {
91	HeaderContext
92	trunc bool
93	v     *types.BuildCache
94}
95
96func newBuildCacheContext() *buildCacheContext {
97	buildCacheCtx := buildCacheContext{}
98	buildCacheCtx.header = buildCacheHeaderContext{
99		"ID":            cacheIDHeader,
100		"Parent":        parentHeader,
101		"CacheType":     cacheTypeHeader,
102		"Size":          sizeHeader,
103		"CreatedSince":  createdSinceHeader,
104		"LastUsedSince": lastUsedSinceHeader,
105		"UsageCount":    usageCountHeader,
106		"InUse":         inUseHeader,
107		"Shared":        sharedHeader,
108		"Description":   descriptionHeader,
109	}
110	return &buildCacheCtx
111}
112
113func (c *buildCacheContext) MarshalJSON() ([]byte, error) {
114	return marshalJSON(c)
115}
116
117func (c *buildCacheContext) ID() string {
118	id := c.v.ID
119	if c.trunc {
120		id = stringid.TruncateID(c.v.ID)
121	}
122	if c.v.InUse {
123		return id + "*"
124	}
125	return id
126}
127
128func (c *buildCacheContext) Parent() string {
129	if c.trunc {
130		return stringid.TruncateID(c.v.Parent)
131	}
132	return c.v.Parent
133}
134
135func (c *buildCacheContext) CacheType() string {
136	return c.v.Type
137}
138
139func (c *buildCacheContext) Description() string {
140	return c.v.Description
141}
142
143func (c *buildCacheContext) Size() string {
144	return units.HumanSizeWithPrecision(float64(c.v.Size), 3)
145}
146
147func (c *buildCacheContext) CreatedAt() string {
148	return c.v.CreatedAt.String()
149}
150
151func (c *buildCacheContext) CreatedSince() string {
152	return units.HumanDuration(time.Now().UTC().Sub(c.v.CreatedAt)) + " ago"
153}
154
155func (c *buildCacheContext) LastUsedAt() string {
156	if c.v.LastUsedAt == nil {
157		return ""
158	}
159	return c.v.LastUsedAt.String()
160}
161
162func (c *buildCacheContext) LastUsedSince() string {
163	if c.v.LastUsedAt == nil {
164		return ""
165	}
166	return units.HumanDuration(time.Now().UTC().Sub(*c.v.LastUsedAt)) + " ago"
167}
168
169func (c *buildCacheContext) UsageCount() string {
170	return fmt.Sprintf("%d", c.v.UsageCount)
171}
172
173func (c *buildCacheContext) InUse() string {
174	return fmt.Sprintf("%t", c.v.InUse)
175}
176
177func (c *buildCacheContext) Shared() string {
178	return fmt.Sprintf("%t", c.v.Shared)
179}
180