content: -- Copyright (c) 2025 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
  -- SPDX-License-Identifier: Apache-2.0
  
  module DA.Daml.Compiler.ExtractDar
    ( ExtractedDar(..)
    , extractDar
    , getEntry
    , edDeps
    , mainPackageIdFromPaths
    ) where
  
  import qualified "zip-archive" Codec.Archive.Zip as ZipArchive
  import Control.Monad.Extra
  import qualified Data.ByteString.Lazy as BSL
  import qualified Data.ByteString.UTF8 as BSUTF8
  import Data.List.Extra
  import qualified Data.Text as T
  import System.FilePath
  import qualified DA.Daml.LF.Ast as LF
  import DA.Daml.LF.Reader
  
  data ExtractedDar = ExtractedDar
      { edSdkVersions :: String
      , edMain :: ZipArchive.Entry
      , edConfFiles :: [ZipArchive.Entry]
      , edDalfs :: [ZipArchive.Entry]
      , edSrcs :: [ZipArchive.Entry]
      }
  
  edDeps :: ExtractedDar -> [ZipArchive.Entry]
  edDeps ExtractedDar {edDalfs, edMain} =
    filter (\edDalf -> ZipArchive.eRelativePath edDalf /= ZipArchive.eRelativePath edMain) edDalfs
  
  -- | Derives the main packages id from its path within the Dar, which will always be <package-name>-<package-version>-<package-id>.dalf
  mainPackageIdFromPaths :: ExtractedDar -> LF.PackageId
  mainPackageIdFromPaths ed = LF.PackageId $ T.pack $ last $ linesBy (=='-') $ takeBaseName $ ZipArchive.eRelativePath $ edMain ed
  
  -- | Extract a dar archive
  extractDar :: FilePath -> IO ExtractedDar
  extractDar fp = do
      bs <- BSL.readFile fp
      let archive = ZipArchive.toArchive bs
      manifest <- getEntry manifestPath archive
      dalfManifest <- either fail pure $ readDalfManifest archive
      mainDalfEntry <- getEntry (mainDalfPath dalfManifest) archive
      sdkVersion <-
          case parseManifestFile $ BSL.toStrict $ ZipArchive.fromEntry manifest of
              Left err -> fail err
              Right manifest ->
                  case lookup "Sdk-Version" manifest of
                      Nothing -> fail "No Sdk-Version entry in manifest"
                      Just version -> pure $! trim $ BSUTF8.toString version
      let confFiles =
              [ e
              | e <- ZipArchive.zEntries archive
              , ".conf" `isExtensionOf` ZipArchive.eRelativePath e
              ]
      let srcs =
              [ e
              | e <- ZipArchive.zEntries archive
              , takeExtension (ZipArchive.eRelativePath e) `elem`
                    [".daml", ".hie", ".hi"]
              ]
      dalfs <- forM (dalfPaths dalfManifest) $ \p -> getEntry p archive
      pure (ExtractedDar sdkVersion mainDalfEntry confFiles dalfs srcs)
  
  -- | Get an entry from a dar or fail.
  getEntry :: FilePath -> ZipArchive.Archive -> IO ZipArchive.Entry
  getEntry fp dar =
      maybe (fail $ "Package does not contain " <> fp) pure $
      ZipArchive.findEntryByPath fp dar
  