From c2d900ec74e03e9c782cfe7b7ce06ff62cda6601 Mon Sep 17 00:00:00 2001 From: Ophestra Date: Sat, 29 Aug 2026 18:58:06 +0900 Subject: pkg: move from internal Closes #43. Signed-off-by: Ophestra --- pkg/clean.go | 161 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 161 insertions(+) create mode 100644 pkg/clean.go (limited to 'pkg/clean.go') diff --git a/pkg/clean.go b/pkg/clean.go new file mode 100644 index 00000000..f07706cf --- /dev/null +++ b/pkg/clean.go @@ -0,0 +1,161 @@ +package pkg + +import ( + "errors" + "os" + "unique" +) + +// Clean destroys checksum backing entries without any identifier or substitute +// entry referring to it. If at least one keep [Artifact] is specified, +// identifier and substitute entries not kept alive by them are destroyed first. +func (c *Cache) Clean(dry, inputs bool, keep ...Artifact) ( + []unique.Handle[ID], + []unique.Handle[Checksum], + error, +) { + c.identMu.Lock() + defer c.identMu.Unlock() + + c.checksumMu.Lock() + defer c.checksumMu.Unlock() + + dents, err := os.ReadDir(c.base.Append(dirChecksum).String()) + if err != nil { + return nil, nil, err + } + checksums := make(map[unique.Handle[Checksum]]string, len(dents)) + var buf Checksum + for _, dent := range dents { + name := dent.Name() + if err = Decode(&buf, name); err != nil { + return nil, nil, err + } + checksums[unique.Make(buf)] = name + } + + type identPair struct { + id unique.Handle[ID] + name string + } + dents, err = os.ReadDir(c.base.Append(dirIdentifier).String()) + if err != nil { + return nil, nil, err + } + + keepIdents := make(map[unique.Handle[ID]]struct{}) + if inputs { + for _, id := range Inputs((*Collect)(&keep)) { + keepIdents[id] = struct{}{} + } + } else { + for _, a := range keep { + keepIdents[c.Ident(a)] = struct{}{} + } + } + + idents := make([]identPair, 0, len(dents)) + for _, dent := range dents { + name := dent.Name() + if err = Decode(&buf, name); err != nil { + return nil, nil, err + } + id := unique.Make(ID(buf)) + + if _, ok := keepIdents[id]; len(keep) == 0 || ok { + if err = readlinkChecksum(c.base.Append( + dirIdentifier, + name, + ), &buf); err != nil { + return nil, nil, err + } + delete(checksums, unique.Make(buf)) + continue + } + + c.msg.Verbosef( + "arranging for destruction of %s%s%s...", + c.sgrIdent, name, c.sgrRes, + ) + idents = append(idents, identPair{id, name}) + } + + destroyedIdents := make([]unique.Handle[ID], 0, len(idents)) + for _, pair := range idents { + if !dry { + if err = os.Remove(c.base.Append( + dirStatus, + pair.name, + ).String()); err != nil && !errors.Is(err, os.ErrNotExist) { + return destroyedIdents, nil, err + } + + if err = os.Remove(c.base.Append( + dirIdentifier, + pair.name, + ).String()); err != nil { + return destroyedIdents, nil, err + } + } + destroyedIdents = append(destroyedIdents, pair.id) + } + + destroyedChecksums := make([]unique.Handle[Checksum], 0, len(checksums)) + for checksum, name := range checksums { + if err = c.parent.Err(); err != nil { + return destroyedIdents, destroyedChecksums, err + } + c.msg.Verbosef( + "destroying checksum %s%s%s...", + c.sgrIdent, name, c.sgrRes, + ) + if !dry { + if err = errors.Join(removeAll(c.base.Append( + dirChecksum, + name, + ))); err != nil { + return destroyedIdents, destroyedChecksums, err + } + } + destroyedChecksums = append(destroyedChecksums, checksum) + } + + dents, err = os.ReadDir(c.base.Append(dirSubstitute).String()) + if err != nil { + return destroyedIdents, destroyedChecksums, err + } + for _, dent := range dents { + name := dent.Name() + if err = readlinkChecksum(c.base.Append( + dirSubstitute, + name, + ), &buf); err != nil { + return destroyedIdents, destroyedChecksums, err + } + if _, ok := checksums[unique.Make(buf)]; !ok { + continue + } + + c.msg.Verbosef( + "destroying substitute %s%s%s...", + c.sgrIdent, name, c.sgrRes, + ) + if !dry { + if err = os.Remove(c.base.Append( + dirStatus, + name, + ).String()); err != nil && !errors.Is(err, os.ErrNotExist) { + return destroyedIdents, nil, err + } + + if err = os.Remove(c.base.Append( + dirSubstitute, + name, + ).String()); err != nil { + return destroyedIdents, destroyedChecksums, err + } + } + } + + return destroyedIdents, destroyedChecksums, nil +} -- cgit v1.3.1