module Stackctl.FilterOption ( FilterOption , defaultFilterOption , HasFilterOption (..) , envFilterOption , filterOption , filterOptionFromPaths , filterOptionFromText , filterOptionToPaths , filterStackSpecs ) where import Stackctl.Prelude import qualified Data.List.NonEmpty as NE import Data.Semigroup (Last (..)) import qualified Data.Text as T import qualified Env import Options.Applicative import Stackctl.AWS.CloudFormation (StackName (..)) import Stackctl.StackSpec import System.FilePath (hasExtension) import System.FilePath.Glob newtype FilterOption = FilterOption { FilterOption -> NonEmpty Pattern unFilterOption :: NonEmpty Pattern } deriving (NonEmpty FilterOption -> FilterOption FilterOption -> FilterOption -> FilterOption forall b. Integral b => b -> FilterOption -> FilterOption forall a. (a -> a -> a) -> (NonEmpty a -> a) -> (forall b. Integral b => b -> a -> a) -> Semigroup a stimes :: forall b. Integral b => b -> FilterOption -> FilterOption $cstimes :: forall b. Integral b => b -> FilterOption -> FilterOption sconcat :: NonEmpty FilterOption -> FilterOption $csconcat :: NonEmpty FilterOption -> FilterOption <> :: FilterOption -> FilterOption -> FilterOption $c<> :: FilterOption -> FilterOption -> FilterOption Semigroup) via Last FilterOption instance ToJSON FilterOption where toJSON :: FilterOption -> Value toJSON = forall a. ToJSON a => a -> Value toJSON forall b c a. (b -> c) -> (a -> b) -> a -> c . FilterOption -> String showFilterOption toEncoding :: FilterOption -> Encoding toEncoding = forall a. ToJSON a => a -> Encoding toEncoding forall b c a. (b -> c) -> (a -> b) -> a -> c . FilterOption -> String showFilterOption class HasFilterOption env where filterOptionL :: Lens' env FilterOption instance HasFilterOption FilterOption where filterOptionL :: Lens' FilterOption FilterOption filterOptionL = forall a. a -> a id envFilterOption :: String -> Env.Parser Env.Error FilterOption envFilterOption :: String -> Parser Error FilterOption envFilterOption String items = String -> Parser Error FilterOption var String "FILTERS" forall (f :: * -> *) a. Alternative f => f a -> f a -> f a <|> String -> Parser Error FilterOption var String "FILTER" where var :: String -> Parser Error FilterOption var String name = forall e a. AsUnset e => Reader e a -> String -> Mod Var a -> Parser e a Env.var (forall (p :: * -> * -> *) a b c. Bifunctor p => (a -> b) -> p a c -> p b c first String -> Error Env.UnreadError forall b c a. (b -> c) -> (a -> b) -> a -> c . String -> Either String FilterOption readFilterOption) String name forall a b. (a -> b) -> a -> b $ forall (t :: * -> *) a. HasHelp t => String -> Mod t a Env.help forall a b. (a -> b) -> a -> b $ String "Filter " forall a. Semigroup a => a -> a -> a <> String items forall a. Semigroup a => a -> a -> a <> String " by patterns" filterOption :: String -> Parser FilterOption filterOption :: String -> Parser FilterOption filterOption String items = forall a. ReadM a -> Mod OptionFields a -> Parser a option (forall a. (String -> Either String a) -> ReadM a eitherReader String -> Either String FilterOption readFilterOption) forall a b. (a -> b) -> a -> b $ forall a. Monoid a => [a] -> a mconcat [ forall (f :: * -> *) a. HasName f => String -> Mod f a long String "filter" , forall (f :: * -> *) a. HasMetavar f => String -> Mod f a metavar String "PATTERN[,PATTERN]" , forall (f :: * -> *) a. String -> Mod f a help forall a b. (a -> b) -> a -> b $ String "Filter " forall a. Semigroup a => a -> a -> a <> String items forall a. Semigroup a => a -> a -> a <> String " to match PATTERN(s)" ] filterOptionFromPaths :: NonEmpty FilePath -> FilterOption filterOptionFromPaths :: NonEmpty String -> FilterOption filterOptionFromPaths = NonEmpty Pattern -> FilterOption FilterOption forall b c a. (b -> c) -> (a -> b) -> a -> c . forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b fmap String -> Pattern compile filterOptionFromText :: Text -> Maybe FilterOption filterOptionFromText :: Text -> Maybe FilterOption filterOptionFromText = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b fmap NonEmpty Pattern -> FilterOption FilterOption forall b c a. (b -> c) -> (a -> b) -> a -> c . forall a. [a] -> Maybe (NonEmpty a) NE.nonEmpty forall b c a. (b -> c) -> (a -> b) -> a -> c . forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b] concatMap Text -> [Pattern] expandPatterns forall b c a. (b -> c) -> (a -> b) -> a -> c . forall a. (a -> Bool) -> [a] -> [a] filter (Bool -> Bool not forall b c a. (b -> c) -> (a -> b) -> a -> c . Text -> Bool T.null) forall b c a. (b -> c) -> (a -> b) -> a -> c . forall a b. (a -> b) -> [a] -> [b] map Text -> Text T.strip forall b c a. (b -> c) -> (a -> b) -> a -> c . Text -> Text -> [Text] T.splitOn Text "," expandPatterns :: Text -> [Pattern] expandPatterns :: Text -> [Pattern] expandPatterns Text t = forall a b. (a -> b) -> [a] -> [b] map String -> Pattern compile forall a b. (a -> b) -> a -> b $ String s forall a. a -> [a] -> [a] : [String] expanded where expanded :: [String] expanded | Text "**" Text -> Text -> Bool `T.isPrefixOf` Text t = [String] suffixed | Bool otherwise = forall a b. (a -> b) -> [a] -> [b] map (String "**" String -> String -> String </>) forall a b. (a -> b) -> a -> b $ String s forall a. a -> [a] -> [a] : [String] suffixed suffixed :: [String] suffixed | Text "*" Text -> Text -> Bool `T.isSuffixOf` Text t Bool -> Bool -> Bool || String -> Bool hasExtension String s = [] | Bool otherwise = (String s String -> String -> String </> String "*") forall a. a -> [a] -> [a] : forall a b. (a -> b) -> [a] -> [b] map (String s String -> String -> String <.>) forall {a}. IsString a => [a] extensions extensions :: [a] extensions = [a "json", a "yaml"] s :: String s = Text -> String unpack Text t readFilterOption :: String -> Either String FilterOption readFilterOption :: String -> Either String FilterOption readFilterOption = forall a b. a -> Maybe b -> Either a b note forall {a}. IsString a => a err forall b c a. (b -> c) -> (a -> b) -> a -> c . Text -> Maybe FilterOption filterOptionFromText forall b c a. (b -> c) -> (a -> b) -> a -> c . String -> Text pack where err :: a err = a "Must be non-empty, comma-separated list of non-empty patterns" showFilterOption :: FilterOption -> String showFilterOption :: FilterOption -> String showFilterOption = Text -> String unpack forall b c a. (b -> c) -> (a -> b) -> a -> c . Text -> [Text] -> Text T.intercalate Text "," forall b c a. (b -> c) -> (a -> b) -> a -> c . forall a b. (a -> b) -> [a] -> [b] map (String -> Text pack forall b c a. (b -> c) -> (a -> b) -> a -> c . Pattern -> String decompile) forall b c a. (b -> c) -> (a -> b) -> a -> c . forall a. NonEmpty a -> [a] NE.toList forall b c a. (b -> c) -> (a -> b) -> a -> c . FilterOption -> NonEmpty Pattern unFilterOption defaultFilterOption :: FilterOption defaultFilterOption :: FilterOption defaultFilterOption = NonEmpty String -> FilterOption filterOptionFromPaths forall a b. (a -> b) -> a -> b $ forall (f :: * -> *) a. Applicative f => a -> f a pure String "**/*" filterOptionToPaths :: FilterOption -> [FilePath] filterOptionToPaths :: FilterOption -> [String] filterOptionToPaths = forall a b. (a -> b) -> [a] -> [b] map Pattern -> String decompile forall b c a. (b -> c) -> (a -> b) -> a -> c . forall a. NonEmpty a -> [a] NE.toList forall b c a. (b -> c) -> (a -> b) -> a -> c . FilterOption -> NonEmpty Pattern unFilterOption filterStackSpecs :: FilterOption -> [StackSpec] -> [StackSpec] filterStackSpecs :: FilterOption -> [StackSpec] -> [StackSpec] filterStackSpecs FilterOption fo = forall a. (a -> Bool) -> [a] -> [a] filter forall a b. (a -> b) -> a -> b $ \StackSpec spec -> forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool any (Pattern -> StackSpec -> Bool `matchStackSpec` StackSpec spec) forall a b. (a -> b) -> a -> b $ FilterOption -> NonEmpty Pattern unFilterOption FilterOption fo matchStackSpec :: Pattern -> StackSpec -> Bool matchStackSpec :: Pattern -> StackSpec -> Bool matchStackSpec Pattern p StackSpec spec = forall (t :: * -> *). Foldable t => t Bool -> Bool or [ Pattern -> String -> Bool match Pattern p forall a b. (a -> b) -> a -> b $ Text -> String unpack forall a b. (a -> b) -> a -> b $ StackName -> Text unStackName forall a b. (a -> b) -> a -> b $ StackSpec -> StackName stackSpecStackName StackSpec spec , Pattern -> String -> Bool match Pattern p forall a b. (a -> b) -> a -> b $ StackSpec -> String stackSpecStackFile StackSpec spec , Pattern -> String -> Bool match Pattern p forall a b. (a -> b) -> a -> b $ StackSpec -> String stackSpecTemplateFile StackSpec spec ]