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...", name) 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( 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...", name) 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...", name) if !dry { if err = os.Remove(c.base.Append( dirSubstitute, name, ).String()); err != nil { return destroyedIdents, destroyedChecksums, err } } } return destroyedIdents, destroyedChecksums, nil }