go.mway.dev/x@v0.0.0-20240520034138-950aede9a3fb/archive/extract/options.go (about) 1 // Copyright (c) 2024 Matt Way 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to 5 // deal in the Software without restriction, including without limitation the 6 // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 7 // sell copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 19 // IN THE THE SOFTWARE. 20 21 package extract 22 23 import ( 24 "context" 25 "io" 26 "maps" 27 "slices" 28 ) 29 30 var ( 31 _ Option = Callback(nil) 32 _ Option = Options{} 33 ) 34 35 // A Callback is a function called by [Extract] once an archive has been 36 // extracted. When called, the current working directory is the extract 37 // destination, and is also passed as the path parameter. 38 type Callback func(ctx context.Context, path string) error 39 40 func (c Callback) apply(dst *Options) { 41 dst.Callback = c 42 } 43 44 // Options configure the behavior of [Extract]. 45 type Options struct { 46 Callback Callback 47 Output io.Writer 48 StripPrefix string 49 IncludePaths map[string]string 50 ExcludePaths []string 51 Delete bool 52 } 53 54 // With returns a new [Options] with opts merged on top of o. 55 func (o Options) With(opts ...Option) Options { 56 for _, opt := range opts { 57 opt.apply(&o) 58 } 59 return o 60 } 61 62 func (o Options) apply(dst *Options) { 63 if o.Callback != nil { 64 dst.Callback = o.Callback 65 } 66 67 if o.Output != nil { 68 dst.Output = o.Output 69 } 70 71 if len(o.StripPrefix) > 0 { 72 dst.StripPrefix = o.StripPrefix 73 } 74 75 if len(o.IncludePaths) > 0 { 76 dst.IncludePaths = maps.Clone(o.IncludePaths) 77 } 78 79 if len(o.ExcludePaths) > 0 { 80 dst.ExcludePaths = slices.Clone(o.ExcludePaths) 81 } 82 83 if o.Delete { 84 dst.Delete = true 85 } 86 } 87 88 // An Option configures the behavior of [Extract]. 89 type Option interface { 90 apply(*Options) 91 } 92 93 // Output returns a new [Option] that configures [Extract] to write any output 94 // to the given writer. 95 func Output(output io.Writer) Option { 96 return optionFunc(func(dst *Options) { 97 dst.Output = output 98 }) 99 } 100 101 // StripPrefix returns a new [Option] that configures [Extract] to root all 102 // extraction at the given prefix. Note that paths that are not descendants of 103 // the given prefix will not be extracted. 104 func StripPrefix(prefix string) Option { 105 return optionFunc(func(dst *Options) { 106 dst.StripPrefix = prefix 107 }) 108 } 109 110 // IncludePaths returns a new [Option] that configures [Extract] to only 111 // consider the given paths for extraction. The given map's keys should be 112 // relative to either the archive root or the stripped prefix, and may be 113 // globs; the map's values are optional and specify non-default destinations 114 // for any path(s) matched by the corresponding key. 115 func IncludePaths(paths map[string]string) Option { 116 return optionFunc(func(dst *Options) { 117 dst.IncludePaths = maps.Clone(paths) 118 }) 119 } 120 121 // ExcludePaths returns a new [Option] that configures [Extract] to exclude any 122 // matching paths from extraction. The given paths should be relative to either 123 // the archive root or the stripped prefix, and may be globs. 124 func ExcludePaths(paths []string) Option { 125 return optionFunc(func(dst *Options) { 126 dst.ExcludePaths = slices.Clone(paths) 127 }) 128 } 129 130 // Delete returns a new [Option] that configures [Extract] to delete any 131 // archive directories from the destination before extracting them. 132 func Delete(del bool) Option { 133 return optionFunc(func(dst *Options) { 134 dst.Delete = del 135 }) 136 } 137 138 type optionFunc func(*Options) 139 140 func (f optionFunc) apply(dst *Options) { 141 f(dst) 142 }