0 | ||| Provides semantic versioning `Version` type and utilities.
  1 | ||| See [semver](https://semver.org/) for proper definition of semantic versioning
  2 | module Libraries.Data.Version
  3 |
  4 | import Data.String
  5 |
  6 | import Libraries.Text.Parser
  7 | import Libraries.Text.Lexer
  8 |
  9 | %default total
 10 |
 11 | ||| Semantic versioning with optional tag
 12 | public export
 13 | record Version where
 14 |   constructor MkVersion
 15 |   ||| Semantic version
 16 |   ||| Should follow the (major, minor, patch) convention
 17 |   semVer : (Nat, Nat, Nat)
 18 |   ||| Optional tag
 19 |   ||| Usually contains git sha1 of this software's build in between releases
 20 |   versionTag : Maybe String
 21 |
 22 | ||| String representation of a Version with optional display of `tag`
 23 | export
 24 | showVersion : Bool -> Version -> String
 25 | showVersion tag (MkVersion (maj,min,patch) versionTag) =
 26 |   concat (intersperse "." (map show [ maj, min, patch])) ++
 27 |          if tag then showTag else ""
 28 |   where
 29 |     showTag : String
 30 |     showTag = case versionTag of
 31 |                 Nothing => ""
 32 |                 Just tag => "-" ++ tag
 33 |
 34 | export
 35 | Show Version where
 36 |   show = showVersion True
 37 |
 38 | export
 39 | Eq Version where
 40 |   (==) (MkVersion ver tag) (MkVersion ver' tag') = ver == ver' && tag == tag'
 41 |
 42 | export
 43 | Ord Version where
 44 |   compare (MkVersion ver tag) (MkVersion ver' tag')  =
 45 |     case compare ver ver' of
 46 |       EQ => compare tag tag'
 47 |       other => other
 48 |
 49 | --------------------------------------------------------------------------------
 50 | -- Parser
 51 | --------------------------------------------------------------------------------
 52 |
 53 | data VersionTokenKind = VersionText | VersionNum | VersionDot | VersionDash
 54 |
 55 | Eq VersionTokenKind where
 56 |   (==) VersionText VersionText = True
 57 |   (==) VersionNum VersionNum = True
 58 |   (==) VersionDot VersionDot = True
 59 |   (==) VersionDash VersionDash = True
 60 |   (==) _ _ = False
 61 |
 62 |
 63 | VersionToken : Type
 64 | VersionToken = Token VersionTokenKind
 65 |
 66 | TokenKind VersionTokenKind where
 67 |   TokType VersionText = String
 68 |   TokType VersionDot = ()
 69 |   TokType VersionDash = ()
 70 |   TokType VersionNum = Nat
 71 |
 72 |   tokValue VersionText x = x
 73 |   tokValue VersionDot _ = ()
 74 |   tokValue VersionDash _ = ()
 75 |   tokValue VersionNum n = stringToNatOrZ n
 76 |
 77 | versionTokenMap : TokenMap VersionToken
 78 | versionTokenMap = toTokenMap $
 79 |   [ (is '.', VersionDot)
 80 |   , (is '-', VersionDash)
 81 |   , (digits, VersionNum)
 82 |   , (some alphaNum, VersionText)
 83 |   ]
 84 |
 85 | lexVersion : String -> List (WithBounds VersionToken)
 86 | lexVersion str =
 87 |   let
 88 |     (tokens, _, _, _) = lex versionTokenMap str
 89 |   in
 90 |     tokens
 91 |
 92 |
 93 | versionParser : Grammar () VersionToken True Version
 94 | versionParser = do
 95 |   maj <- match VersionNum
 96 |   match VersionDot
 97 |   min <- match VersionNum
 98 |   match VersionDot
 99 |   patch <- match VersionNum
100 |   optTag <- optional $ match VersionDash *> match VersionText
101 |   pure $ MkVersion (maj, min, patch) optTag
102 |
103 | ||| Parse given string into a proper `Version` record
104 | |||
105 | ||| Expected format must be:
106 | ||| ```
107 | ||| <major>.<minor>.<patch>(-<tag>)?
108 | ||| ```
109 | ||| where <major>, <minor> and <patch> are natural integers and tag is an optional
110 | ||| alpha-numeric string.
111 | export
112 | parseVersion : String -> Maybe Version
113 | parseVersion str =
114 |   case parse versionParser (lexVersion str) of
115 |     Right (_, version, []) => Just version
116 |     _ => Nothing
117 |