module Hadolint.Rule.DL3022 (rule) where

import Data.Set qualified as Set
import Data.Text qualified as Text
import Data.Text.Read qualified as Read
import Hadolint.Rule
import Language.Docker.Syntax

data Acc
  = Acc {Acc -> Int
count :: Int, Acc -> Set Text
names :: Set.Set Text.Text}
  | Empty
  deriving (Acc -> Acc -> Bool
(Acc -> Acc -> Bool) -> (Acc -> Acc -> Bool) -> Eq Acc
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Acc -> Acc -> Bool
== :: Acc -> Acc -> Bool
$c/= :: Acc -> Acc -> Bool
/= :: Acc -> Acc -> Bool
Eq)

rule :: Rule args
rule :: forall args. Rule args
rule = (Int -> State Acc -> Instruction args -> State Acc)
-> State Acc -> Rule args
forall a args.
(Int -> State a -> Instruction args -> State a)
-> State a -> Rule args
customRule Int -> State Acc -> Instruction args -> State Acc
forall {args}. Int -> State Acc -> Instruction args -> State Acc
check (Acc -> State Acc
forall a. a -> State a
emptyState Acc
Empty)
  where
    code :: RuleCode
code = RuleCode
"DL3022"
    severity :: DLSeverity
severity = DLSeverity
DLWarningC
    message :: Text
message = Text
"`COPY --from` should reference a previously defined `FROM` alias"

    check :: Int -> State Acc -> Instruction args -> State Acc
check Int
_ State Acc
st (From BaseImage {alias :: BaseImage -> Maybe ImageAlias
alias = Just (ImageAlias Text
als)}) = State Acc
st State Acc -> (State Acc -> State Acc) -> State Acc
forall a b. a -> (a -> b) -> b
|> (Acc -> Acc) -> State Acc -> State Acc
forall a. (a -> a) -> State a -> State a
modify (Text -> Acc -> Acc
incAndAddName Text
als)
    check Int
_ State Acc
st (From BaseImage {}) = State Acc
st State Acc -> (State Acc -> State Acc) -> State Acc
forall a b. a -> (a -> b) -> b
|> (Acc -> Acc) -> State Acc -> State Acc
forall a. (a -> a) -> State a -> State a
modify Acc -> Acc
incCount
    check Int
line State Acc
st (Copy (CopyArgs NonEmpty SourcePath
_ TargetPath
_) (CopyFlags Chown
_ Chmod
_ Link
_ (CopySource Text
s) [Exclude]
_))
      | Text
":" Text -> Text -> Bool
`Text.isInfixOf` Text -> Text
dropQuotes Text
s = State Acc
st
      | Text -> Acc -> Bool
isMember Text
s (State Acc -> Acc
forall a. State a -> a
state State Acc
st) = State Acc
st
      | Bool
otherwise = case Reader Int
forall a. Integral a => Reader a
Read.decimal Text
s of
          Right (Int
v, Text
_) | Int
v Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Acc -> Int
nameCount (State Acc -> Acc
forall a. State a -> a
state State Acc
st) -> State Acc
st
          Either String (Int, Text)
_ -> State Acc
st State Acc -> (State Acc -> State Acc) -> State Acc
forall a b. a -> (a -> b) -> b
|> CheckFailure -> State Acc -> State Acc
forall a. CheckFailure -> State a -> State a
addFail CheckFailure {Int
Text
RuleCode
DLSeverity
code :: RuleCode
severity :: DLSeverity
message :: Text
line :: Int
line :: Int
message :: Text
severity :: DLSeverity
code :: RuleCode
..}
    check Int
_ State Acc
st Instruction args
_ = State Acc
st
{-# INLINEABLE rule #-}

incAndAddName :: Text.Text -> Acc -> Acc
incAndAddName :: Text -> Acc -> Acc
incAndAddName Text
s Acc
Empty = Acc {count :: Int
count = Int
1, names :: Set Text
names = Text -> Set Text
forall a. a -> Set a
Set.singleton Text
s}
incAndAddName Text
s Acc {Int
count :: Acc -> Int
count :: Int
count, Set Text
names :: Acc -> Set Text
names :: Set Text
names} = Acc {count :: Int
count = Int
count Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1, names :: Set Text
names = Text -> Set Text -> Set Text
forall a. Ord a => a -> Set a -> Set a
Set.insert Text
s Set Text
names}

incCount :: Acc -> Acc
incCount :: Acc -> Acc
incCount Acc
Empty = Acc {count :: Int
count = Int
1, names :: Set Text
names = Set Text
forall a. Set a
Set.empty}
incCount Acc {Int
count :: Acc -> Int
count :: Int
count, Set Text
names :: Acc -> Set Text
names :: Set Text
names} = Acc {count :: Int
count = Int
count Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1, names :: Set Text
names = Set Text
names}

isMember :: Text.Text -> Acc -> Bool
isMember :: Text -> Acc -> Bool
isMember Text
_ Acc
Empty = Bool
False
isMember Text
s Acc {Set Text
names :: Acc -> Set Text
names :: Set Text
names} = Text -> Set Text -> Bool
forall a. Ord a => a -> Set a -> Bool
Set.member Text
s Set Text
names

nameCount :: Acc -> Int
nameCount :: Acc -> Int
nameCount Acc
Empty = Int
0
nameCount Acc {Int
count :: Acc -> Int
count :: Int
count} = Int
count