{-# 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 Int
lockfileIkProxy = Proxy Int
forall k (t :: k). Proxy t
Proxy

-- | Key that indexes package for a specific version.
data PackageKey = PackageKey
  { PackageKey -> PackageKeyName
name           :: PackageKeyName -- ^ package name
  , PackageKey -> Text
npmVersionSpec :: Text
  -- ^ tring that specifies the version of a package;
  -- sometimes a npm semver, sometimes an arbitrary string
  } deriving (Int -> PackageKey -> ShowS
[PackageKey] -> ShowS
PackageKey -> String
(Int -> PackageKey -> ShowS)
-> (PackageKey -> String)
-> ([PackageKey] -> ShowS)
-> Show PackageKey
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [PackageKey] -> ShowS
$cshowList :: [PackageKey] -> ShowS
show :: PackageKey -> String
$cshow :: PackageKey -> String
showsPrec :: Int -> PackageKey -> ShowS
$cshowsPrec :: Int -> PackageKey -> ShowS
Show, PackageKey -> PackageKey -> Bool
(PackageKey -> PackageKey -> Bool)
-> (PackageKey -> PackageKey -> Bool) -> Eq PackageKey
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: PackageKey -> PackageKey -> Bool
$c/= :: PackageKey -> PackageKey -> Bool
== :: PackageKey -> PackageKey -> Bool
$c== :: PackageKey -> PackageKey -> Bool
Eq, Eq PackageKey
Eq PackageKey
-> (PackageKey -> PackageKey -> Ordering)
-> (PackageKey -> PackageKey -> Bool)
-> (PackageKey -> PackageKey -> Bool)
-> (PackageKey -> PackageKey -> Bool)
-> (PackageKey -> PackageKey -> Bool)
-> (PackageKey -> PackageKey -> PackageKey)
-> (PackageKey -> PackageKey -> PackageKey)
-> Ord PackageKey
PackageKey -> PackageKey -> Bool
PackageKey -> PackageKey -> Ordering
PackageKey -> PackageKey -> PackageKey
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: PackageKey -> PackageKey -> PackageKey
$cmin :: PackageKey -> PackageKey -> PackageKey
max :: PackageKey -> PackageKey -> PackageKey
$cmax :: PackageKey -> PackageKey -> PackageKey
>= :: PackageKey -> PackageKey -> Bool
$c>= :: PackageKey -> PackageKey -> Bool
> :: PackageKey -> PackageKey -> Bool
$c> :: PackageKey -> PackageKey -> Bool
<= :: PackageKey -> PackageKey -> Bool
$c<= :: PackageKey -> PackageKey -> Bool
< :: PackageKey -> PackageKey -> Bool
$c< :: PackageKey -> PackageKey -> Bool
compare :: PackageKey -> PackageKey -> Ordering
$ccompare :: PackageKey -> PackageKey -> Ordering
$cp1Ord :: Eq PackageKey
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 (Int -> PackageKeyName -> ShowS
[PackageKeyName] -> ShowS
PackageKeyName -> String
(Int -> PackageKeyName -> ShowS)
-> (PackageKeyName -> String)
-> ([PackageKeyName] -> ShowS)
-> Show PackageKeyName
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [PackageKeyName] -> ShowS
$cshowList :: [PackageKeyName] -> ShowS
show :: PackageKeyName -> String
$cshow :: PackageKeyName -> String
showsPrec :: Int -> PackageKeyName -> ShowS
$cshowsPrec :: Int -> PackageKeyName -> ShowS
Show, PackageKeyName -> PackageKeyName -> Bool
(PackageKeyName -> PackageKeyName -> Bool)
-> (PackageKeyName -> PackageKeyName -> Bool) -> Eq PackageKeyName
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: PackageKeyName -> PackageKeyName -> Bool
$c/= :: PackageKeyName -> PackageKeyName -> Bool
== :: PackageKeyName -> PackageKeyName -> Bool
$c== :: PackageKeyName -> PackageKeyName -> Bool
Eq, Eq PackageKeyName
Eq PackageKeyName
-> (PackageKeyName -> PackageKeyName -> Ordering)
-> (PackageKeyName -> PackageKeyName -> Bool)
-> (PackageKeyName -> PackageKeyName -> Bool)
-> (PackageKeyName -> PackageKeyName -> Bool)
-> (PackageKeyName -> PackageKeyName -> Bool)
-> (PackageKeyName -> PackageKeyName -> PackageKeyName)
-> (PackageKeyName -> PackageKeyName -> PackageKeyName)
-> Ord PackageKeyName
PackageKeyName -> PackageKeyName -> Bool
PackageKeyName -> PackageKeyName -> Ordering
PackageKeyName -> PackageKeyName -> PackageKeyName
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: PackageKeyName -> PackageKeyName -> PackageKeyName
$cmin :: PackageKeyName -> PackageKeyName -> PackageKeyName
max :: PackageKeyName -> PackageKeyName -> PackageKeyName
$cmax :: PackageKeyName -> PackageKeyName -> PackageKeyName
>= :: PackageKeyName -> PackageKeyName -> Bool
$c>= :: PackageKeyName -> PackageKeyName -> Bool
> :: PackageKeyName -> PackageKeyName -> Bool
$c> :: PackageKeyName -> PackageKeyName -> Bool
<= :: PackageKeyName -> PackageKeyName -> Bool
$c<= :: PackageKeyName -> PackageKeyName -> Bool
< :: PackageKeyName -> PackageKeyName -> Bool
$c< :: PackageKeyName -> PackageKeyName -> Bool
compare :: PackageKeyName -> PackageKeyName -> Ordering
$ccompare :: PackageKeyName -> PackageKeyName -> Ordering
$cp1Ord :: Eq PackageKeyName
Ord)

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

-- | Something with a list of 'PackageKey's pointing to it.
data Keyed a = Keyed (NE.NonEmpty PackageKey) a
  deriving (Int -> Keyed a -> ShowS
[Keyed a] -> ShowS
Keyed a -> String
(Int -> Keyed a -> ShowS)
-> (Keyed a -> String) -> ([Keyed a] -> ShowS) -> Show (Keyed a)
forall a. Show a => Int -> Keyed a -> ShowS
forall a. Show a => [Keyed a] -> ShowS
forall a. Show a => Keyed a -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Keyed a] -> ShowS
$cshowList :: forall a. Show a => [Keyed a] -> ShowS
show :: Keyed a -> String
$cshow :: forall a. Show a => Keyed a -> String
showsPrec :: Int -> Keyed a -> ShowS
$cshowsPrec :: forall a. Show a => Int -> Keyed a -> ShowS
Show, Keyed a -> Keyed a -> Bool
(Keyed a -> Keyed a -> Bool)
-> (Keyed a -> Keyed a -> Bool) -> Eq (Keyed a)
forall a. Eq a => Keyed a -> Keyed a -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Keyed a -> Keyed a -> Bool
$c/= :: forall a. Eq a => Keyed a -> Keyed a -> Bool
== :: Keyed a -> Keyed a -> Bool
$c== :: forall a. Eq a => Keyed a -> Keyed a -> Bool
Eq, Eq (Keyed a)
Eq (Keyed a)
-> (Keyed a -> Keyed a -> Ordering)
-> (Keyed a -> Keyed a -> Bool)
-> (Keyed a -> Keyed a -> Bool)
-> (Keyed a -> Keyed a -> Bool)
-> (Keyed a -> Keyed a -> Bool)
-> (Keyed a -> Keyed a -> Keyed a)
-> (Keyed a -> Keyed a -> Keyed a)
-> Ord (Keyed a)
Keyed a -> Keyed a -> Bool
Keyed a -> Keyed a -> Ordering
Keyed a -> Keyed a -> Keyed a
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
forall a. Ord a => Eq (Keyed a)
forall a. Ord a => Keyed a -> Keyed a -> Bool
forall a. Ord a => Keyed a -> Keyed a -> Ordering
forall a. Ord a => Keyed a -> Keyed a -> Keyed a
min :: Keyed a -> Keyed a -> Keyed a
$cmin :: forall a. Ord a => Keyed a -> Keyed a -> Keyed a
max :: Keyed a -> Keyed a -> Keyed a
$cmax :: forall a. Ord a => Keyed a -> Keyed a -> Keyed a
>= :: Keyed a -> Keyed a -> Bool
$c>= :: forall a. Ord a => Keyed a -> Keyed a -> Bool
> :: Keyed a -> Keyed a -> Bool
$c> :: forall a. Ord a => Keyed a -> Keyed a -> Bool
<= :: Keyed a -> Keyed a -> Bool
$c<= :: forall a. Ord a => Keyed a -> Keyed a -> Bool
< :: Keyed a -> Keyed a -> Bool
$c< :: forall a. Ord a => Keyed a -> Keyed a -> Bool
compare :: Keyed a -> Keyed a -> Ordering
$ccompare :: forall a. Ord a => Keyed a -> Keyed a -> Ordering
$cp1Ord :: forall a. Ord a => Eq (Keyed a)
Ord, a -> Keyed b -> Keyed a
(a -> b) -> Keyed a -> Keyed b
(forall a b. (a -> b) -> Keyed a -> Keyed b)
-> (forall a b. a -> Keyed b -> Keyed a) -> Functor Keyed
forall a b. a -> Keyed b -> Keyed a
forall a b. (a -> b) -> Keyed a -> Keyed b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: a -> Keyed b -> Keyed a
$c<$ :: forall a b. a -> Keyed b -> Keyed a
fmap :: (a -> b) -> Keyed a -> Keyed b
$cfmap :: forall a b. (a -> b) -> Keyed a -> Keyed b
Functor)

-- | The actual npm package with dependencies and a way to download.
data Package = Package
  { Package -> Text
version              :: Text         -- ^ resolved, specific version
  , Package -> Remote
remote               :: Remote
  , Package -> [PackageKey]
dependencies         :: [PackageKey] -- ^ list of dependencies
  , Package -> [PackageKey]
optionalDependencies :: [PackageKey] -- ^ list of optional dependencies
  } deriving (Package -> Package -> Bool
(Package -> Package -> Bool)
-> (Package -> Package -> Bool) -> Eq Package
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Package -> Package -> Bool
$c/= :: Package -> Package -> Bool
== :: Package -> Package -> Bool
$c== :: Package -> Package -> Bool
Eq, Int -> Package -> ShowS
[Package] -> ShowS
Package -> String
(Int -> Package -> ShowS)
-> (Package -> String) -> ([Package] -> ShowS) -> Show Package
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Package] -> ShowS
$cshowList :: [Package] -> ShowS
show :: Package -> String
$cshow :: Package -> String
showsPrec :: Int -> Package -> ShowS
$cshowsPrec :: Int -> Package -> ShowS
Show)

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

  }
  | FileRemoteNoIntegrity
  { Remote -> Text
fileNoIntegrityUrl :: Text -- ^ URL to a remote file
  }
  | GitRemote
  { Remote -> Text
gitRepoUrl  :: Text -- ^ valid git remote URL
  , Remote -> Text
gitRev      :: Text -- ^ git tree-ish (commit, branch, &c.)
  }
  -- this is a bit of an oddidity, but what isn’t
  | FileLocal
  { Remote -> Text
fileLocalPath :: Text -- ^ (relative) path to file on the local machine
  , Remote -> Text
fileLocalSha1 :: Text -- ^ sha1 hash of the file (attached to the link)
  }
  | FileLocalNoIntegrity
  { Remote -> Text
fileLocalNoIntegrityPath :: Text -- ^ (relative) path to file on the local machine
  } deriving (Remote -> Remote -> Bool
(Remote -> Remote -> Bool)
-> (Remote -> Remote -> Bool) -> Eq Remote
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Remote -> Remote -> Bool
$c/= :: Remote -> Remote -> Bool
== :: Remote -> Remote -> Bool
$c== :: Remote -> Remote -> Bool
Eq, Int -> Remote -> ShowS
[Remote] -> ShowS
Remote -> String
(Int -> Remote -> ShowS)
-> (Remote -> String) -> ([Remote] -> ShowS) -> Show Remote
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Remote] -> ShowS
$cshowList :: [Remote] -> ShowS
show :: Remote -> String
$cshow :: Remote -> String
showsPrec :: Int -> Remote -> ShowS
$cshowsPrec :: Int -> Remote -> ShowS
Show)