{-# LANGUAGE DeriveFunctor, OverloadedStrings #-}
{-|
Module : Yarn.Lock.Types
Description : Types for yarn.lock files
Maintainer : Profpatsch
Stability : experimental
-}
module Yarn.Lock.Types where

import Protolude hiding (try)
import qualified Data.Text as T
import qualified Data.MultiKeyedMap as MKM
import qualified Data.List.NonEmpty as NE

-- | Yarn lockfile.
--
-- It is a multi-keyed map (each value can be referenced by multiple keys).
-- This is achieved by using an intermediate key @ik@.
--
-- Attention: Might be changed to a newtype in a future release.
type Lockfile = MKM.MKMap PackageKey Package
-- TODO newtype Lockfile = Lockfile (MKM.MKMap PackageKey Package)

-- | Proxy type for our MKMap intermediate key
lockfileIkProxy :: Proxy Int
lockfileIkProxy = Proxy

-- | Key that indexes package for a specific version.
data PackageKey = PackageKey
  { name           :: PackageKeyName -- ^ package name
  , npmVersionSpec :: Text
  -- ^ tring that specifies the version of a package;
  -- sometimes a npm semver, sometimes an arbitrary string
  } deriving (Show, Eq, Ord)

-- | The name of a package. They can be scoped, see
-- | <https://docs.npmjs.com/misc/scope> for an explanation.
data PackageKeyName
  = SimplePackageKey Text
  -- ^ just a package name
  | ScopedPackageKey Text Text
  -- ^ a scope and a package name (e.g. @types/foobar)
  deriving (Show, Eq, Ord)

-- | Try to parse a string into a package key name (scoped or not).
parsePackageKeyName :: Text -> Maybe PackageKeyName
parsePackageKeyName n = case T.stripPrefix "@" n of
  Nothing -> Just $ SimplePackageKey n
  Just sc -> case T.breakOn "/" sc of
    (_, "") -> Nothing
    (scope, pkg) -> Just $ ScopedPackageKey scope (T.drop 1 pkg)

-- | Something with a list of 'PackageKey's pointing to it.
data Keyed a = Keyed (NE.NonEmpty PackageKey) a
  deriving (Show, Eq, Ord, Functor)

-- | The actual npm package with dependencies and a way to download.
data Package = Package
  { version              :: Text         -- ^ resolved, specific version
  , remote               :: Remote
  , dependencies         :: [PackageKey] -- ^ list of dependencies
  , optionalDependencies :: [PackageKey] -- ^ list of optional dependencies
  } deriving (Eq, Show)

-- | Information on where to download the package.
data Remote
  = FileRemote
  { fileUrl     :: Text -- ^ URL to a remote file
  , fileSha1    :: Text -- ^ sha1 hash of the file (attached to the link)

  }
  | FileRemoteNoIntegrity
  { fileNoIntegrityUrl :: Text -- ^ URL to a remote file
  }
  | GitRemote
  { gitRepoUrl  :: Text -- ^ valid git remote URL
  , gitRev      :: Text -- ^ git tree-ish (commit, branch, &c.)
  }
  -- this is a bit of an oddidity, but what isn’t
  | FileLocal
  { fileLocalPath :: Text -- ^ (relative) path to file on the local machine
  , fileLocalSha1 :: Text -- ^ sha1 hash of the file (attached to the link)
  }
  | FileLocalNoIntegrity
  { fileLocalNoIntegrityPath :: Text -- ^ (relative) path to file on the local machine
  } deriving (Eq, Show)