0 | ||| We work a lot with Strings of distinct semantics.
  1 | ||| Since I've been bitten by this more than once, we wrap
  2 | ||| the in single field record types to drastically increase
  3 | ||| type safety.
  4 | module Pack.Core.Types
  5 |
  6 | import public Data.FilePath.File
  7 |
  8 | import Data.Either
  9 | import Data.Maybe
 10 | import Derive.Prelude
 11 | import Idris.Package.Types
 12 | import System.File
 13 |
 14 | %default total
 15 | %language ElabReflection
 16 |
 17 | ----------------------------------------------------------------------------------
 18 | ----          Quoted Strings
 19 | ----------------------------------------------------------------------------------
 20 |
 21 | ||| This puts a value in quotes during interpolation.
 22 | |||
 23 | ||| Note: If the interpolated string contains additional quote
 24 | ||| characters, these will *not* be escaped.
 25 | export
 26 | quote : Interpolation a => a -> String
 27 | quote v = "\"\{v}\""
 28 |
 29 | ----------------------------------------------------------------------------------
 30 | ----          Paths
 31 | ----------------------------------------------------------------------------------
 32 |
 33 | ||| Returns the second path, relative to the first one
 34 | ||| For instance, `relativeTo /foo/bar/baz /foo/quux` will return
 35 | ||| `../../quux`.
 36 | export
 37 | relativeTo : (origin, target : Path Abs) -> Path Rel
 38 | relativeTo (PAbs _ sx) (PAbs _ sy) = PRel $ go (sx <>> []) (sy <>> [])
 39 |
 40 |   where
 41 |     go : (o,t : List Body) -> SnocList Body
 42 |     go [>]       t  = Lin <>< t
 43 |     go xs        [] = Lin <>< xs $> ".."
 44 |     go (x :: xs) (y :: ys) = case x == y of
 45 |       True  => go xs ys
 46 |       False => Lin <>< (((x :: xs) $> "..") ++ (y :: ys))
 47 |
 48 | ||| True if the given file path body ends on `.ipkg`
 49 | export
 50 | isIpkgBody : Body -> Bool
 51 | isIpkgBody = (Just "ipkg" ==) . extension
 52 |
 53 | ||| True if the given file path body ends on `.toml`
 54 | export
 55 | isTomlBody : Body -> Bool
 56 | isTomlBody = (Just "toml" ==) . extension
 57 |
 58 | ||| True if the given file path body ends on `.html`
 59 | export
 60 | isHtmlBody : Body -> Bool
 61 | isHtmlBody = (Just "html" ==) . extension
 62 |
 63 | toRelPath : String -> Path Rel
 64 | toRelPath s = case the FilePath (fromString s) of
 65 |   FP (PAbs _ sx) => PRel sx
 66 |   FP (PRel sx)   => PRel sx
 67 |
 68 | ||| Converts a `FilePath` - which might hold a relative
 69 | ||| or an absolute path - to an absolute path by interpreting
 70 | ||| a relative path being relative to the given parent directory.
 71 | export
 72 | toAbsPath : (parent : Path Abs) -> FilePath -> Path Abs
 73 | toAbsPath parent (FP $ PAbs d sx) = PAbs d sx
 74 | toAbsPath parent (FP $ PRel sx) = parent </> PRel sx
 75 |
 76 | ||| Parses a string, converting it to either a relative
 77 | ||| or absolute path and using `toAbsPath` to convert the result
 78 | ||| to an absolute path.
 79 | export
 80 | parse : String -> (parentDir : Path Abs) -> Path Abs
 81 | parse s parentDir = toAbsPath parentDir (fromString s)
 82 |
 83 | export %inline
 84 | Cast String (Path Rel) where
 85 |   cast = toRelPath
 86 |
 87 | export infixl 5 <//>
 88 |
 89 | ||| More flexible version of `</>` (path concatenation).
 90 | export %inline
 91 | (<//>) : Cast a (Path Rel) => Path t -> a -> Path t
 92 | p <//> v = p </> cast v
 93 |
 94 | export infixl 5 //>
 95 |
 96 | ||| More flexible version of `//>`
 97 | ||| (appending a file path body to an absolute path)
 98 | export %inline
 99 | (//>) : Cast a Body => Path t -> a -> Path t
100 | p //> v = p /> cast v
101 |
102 | export infixl 8 <->
103 |
104 | ||| Concatenate two file path bodies with a hyphen inbetween.
105 | export
106 | (<->) : Cast a Body => Cast b Body => a -> b -> Body
107 | x <-> y = the Body (cast x) <+> "-" <+> cast y
108 |
109 | ||| Convert a package version to a file path body.
110 | export
111 | Cast PkgVersion Body where
112 |   cast = fromMaybe "0" . parse . show
113 |
114 | ||| Convert a package version to a file path body.
115 | export
116 | Cast (Maybe PkgVersion) Body where
117 |   cast Nothing  = "0"
118 |   cast (Just v) = cast v
119 |
120 | ||| Convert a relative file path to an absolute one by appending
121 | ||| it to the given parent directory.
122 | export %inline
123 | toAbsFile : Path Abs -> File Rel -> File Abs
124 | toAbsFile parentDir (MkF p f) = MkF (parentDir </> p) f
125 |
126 | export
127 | Cast (File t) (Path t) where cast = toPath
128 |
129 | ----------------------------------------------------------------------------------
130 | ----          CurDir, PackDir, and TmpDir
131 | ----------------------------------------------------------------------------------
132 |
133 | ||| The directory where package collections, global user settings,
134 | ||| and cached `.ipkg` files are stored.
135 | public export
136 | record PackDirs where
137 |   [noHints]
138 |   constructor PD
139 |
140 |   ||| Directory with user settings.
141 |   |||
142 |   ||| Depending on what environment variables are set, this defaults to
143 |   ||| (in decreasing order of priority):
144 |   |||
145 |   ||| * `PACK_USER_DIR`
146 |   ||| * `$XDG_CONFIG_HOME/pack`
147 |   ||| * `$HOME/.config/pack`
148 |   user    : Path Abs
149 |
150 |   ||| Directory with application state (installed compiler and libraries).
151 |   |||
152 |   ||| Depending on what environment variables are set, this defaults to
153 |   ||| (in decreasing order of priority):
154 |   |||
155 |   ||| * `PACK_STATE_DIR`
156 |   ||| * `$XDG_STATE_HOME/pack`
157 |   ||| * `$HOME/.local/state/pack`
158 |   state   : Path Abs
159 |
160 |   ||| Directory with cached data such as cached `.ipkg` files, git repos,
161 |   ||| and temporary files used during installation of libs.
162 |   |||
163 |   ||| Depending on what environment variables are set, this defaults to
164 |   ||| (in decreasing order of priority):
165 |   |||
166 |   ||| * `PACK_CACHE_DIR`
167 |   ||| * `$XDG_CACHE_HOME/pack`
168 |   ||| * `$HOME/.cache/pack`
169 |   cache   : Path Abs
170 |
171 |   ||| Directory where executables will be installed.
172 |   |||
173 |   ||| Depending on what environment variables are set, this defaults to
174 |   ||| (in decreasing order of priority):
175 |   |||
176 |   ||| * `PACK_BIN_DIR`
177 |   ||| * `$HOME/.local/bin`
178 |   bin     : Path Abs
179 |
180 | ||| The directory from which the pack application was invoked.
181 | public export
182 | data CurDir : Type where
183 |   [noHints]
184 |   CD : (dir : Path Abs) -> CurDir
185 |
186 | export %inline
187 | Interpolation CurDir where
188 |   interpolate (CD dir) = interpolate dir
189 |
190 | ||| Use this when you need access to the current directory's path with
191 | ||| only a value of type `CurDir` in scope.
192 | export %inline
193 | curDir : (cd : CurDir) => Path Abs
194 | curDir {cd = CD dir} = dir
195 |
196 | ||| The directory where temporary files and git repos will be
197 | ||| kept.
198 | public export
199 | data TmpDir : Type where
200 |   [noHints]
201 |   TD : (dir : Path Abs) -> TmpDir
202 |
203 | export %inline
204 | Interpolation TmpDir where
205 |   interpolate (TD dir) = interpolate dir
206 |
207 | ||| Use this when you need access to the `PACK_DIR` path with
208 | ||| only a value of type `PackDir` in scope.
209 | export %inline
210 | tmpDir : (td : TmpDir) => Path Abs
211 | tmpDir {td = TD dir} = dir
212 |
213 | ----------------------------------------------------------------------------------
214 | ----          TTC-Version
215 | ----------------------------------------------------------------------------------
216 |
217 | ||| The TTC-Version currently used by the Idris compiler
218 | public export
219 | record TTCVersion where
220 |   [noHints]
221 |   constructor TTCV
222 |   version : Maybe Body
223 |
224 | ||| Use this when you need access to the current TTC version
225 | ||| only a value of type `TTCVersion` in scope.
226 | export %inline
227 | ttcVersion : (ttc : TTCVersion) => Maybe Body
228 | ttcVersion = ttc.version
229 |
230 | ----------------------------------------------------------------------------------
231 | ----          Interpolation
232 | ----------------------------------------------------------------------------------
233 |
234 | ||| Convenience implementation for printing file errors in string
235 | ||| interpolation
236 | export
237 | Interpolation FileError where interpolate = show
238 |
239 | ||| Convenience implementation for printing package versions in string
240 | ||| interpolation
241 | export
242 | Interpolation PkgVersion where interpolate = show
243 |
244 | --------------------------------------------------------------------------------
245 | --          URL
246 | --------------------------------------------------------------------------------
247 |
248 | ||| URL mostly used to represent Git repositories.
249 | public export
250 | record URL where
251 |   constructor MkURL
252 |   value : String
253 |
254 | %runElab derive "URL" [Show,Eq,FromString]
255 |
256 | export %inline
257 | Interpolation URL where interpolate = value
258 |
259 | export
260 | Cast URL (Path Rel) where
261 |   cast (MkURL s) = case unpack s of
262 |     'h'::'t'::'t'::'p'::'s'::':'::'/'::'/':: t => cast (pack t)
263 |     'h'::'t'::'t'::'p'::':'::'/'::'/':: t      => cast (pack t)
264 |     t                                          => cast (pack t)
265 |
266 | --------------------------------------------------------------------------------
267 | --          Commits and Hashes
268 | --------------------------------------------------------------------------------
269 |
270 | ||| A git commit hash or tag.
271 | public export
272 | record Commit where
273 |   constructor MkCommit
274 |   value : String
275 |
276 | %runElab derive "Commit" [Show,Eq,FromString]
277 |
278 | export %inline
279 | Interpolation Commit where interpolate = value
280 |
281 | export %inline
282 | Cast Commit (Path Rel) where
283 |   cast = toRelPath . value
284 |
285 | ||| A hash generated by pack
286 | public export
287 | record Hash where
288 |   constructor MkHash
289 |   value : String
290 |
291 | %runElab derive "Hash" [Show,Eq,FromString]
292 |
293 | export %inline
294 | Interpolation Hash where interpolate = value
295 |
296 | export %inline
297 | Cast Hash (Path Rel) where
298 |   cast = toRelPath . value
299 |
300 | --------------------------------------------------------------------------------
301 | --          Branches
302 | --------------------------------------------------------------------------------
303 |
304 | ||| A branch in a git repo.
305 | public export
306 | record Branch where
307 |   constructor MkBranch
308 |   value : String
309 |
310 | %runElab derive "Branch" [Show,Eq,FromString]
311 |
312 | export %inline
313 | Interpolation Branch where interpolate = value
314 |
315 | export %inline
316 | Cast Branch (Path Rel) where
317 |   cast = toRelPath . value
318 |
319 | --------------------------------------------------------------------------------
320 | --          Package Name
321 | --------------------------------------------------------------------------------
322 |
323 | ||| Name of an Idris package
324 | public export
325 | record PkgName where
326 |   constructor MkPkgName
327 |   value : String
328 |
329 | %runElab derive "PkgName" [Show,Eq,Ord,FromString]
330 |
331 | export %inline
332 | Interpolation PkgName where interpolate = value
333 |
334 | export %inline
335 | Cast PkgName (Path Rel) where
336 |   cast = toRelPath . value
337 |
338 | --------------------------------------------------------------------------------
339 | --          PkgOrIpkg
340 | --------------------------------------------------------------------------------
341 |
342 | ||| Several pack commands operat either on a pack package or a local
343 | ||| `.ipkg` file. This data type represents such command line arguments.
344 | public export
345 | data PkgOrIpkg : Type where
346 |   Pkg :  PkgName -> PkgOrIpkg
347 |   Ipkg : File Abs -> PkgOrIpkg
348 |
349 | --------------------------------------------------------------------------------
350 | --          Package Type
351 | --------------------------------------------------------------------------------
352 |
353 | ||| Type of an Idris package (either a library or a binary).
354 | public export
355 | data PkgType = PLib | PApp
356 |
357 | export
358 | Interpolation PkgType where
359 |   interpolate PLib = "lib"
360 |   interpolate PApp = "app"
361 |
362 | export
363 | Eq PkgType where
364 |   PLib == PLib = True
365 |   PApp == PApp = True
366 |   _    == _    = False
367 |
368 | export
369 | Ord PkgType where
370 |   compare PLib PApp = LT
371 |   compare PApp PLib = GT
372 |   compare _    _    = EQ
373 |
374 | --------------------------------------------------------------------------------
375 | --          Install Type
376 | --------------------------------------------------------------------------------
377 |
378 | ||| What we want to install: A library, an application to
379 | ||| run it from within pack, or an application, which should
380 | ||| be available via the `PATH` variable.
381 | public export
382 | data InstallType : Type where
383 |   Library : InstallType
384 |   App     : (withWrapperScript : Bool) -> InstallType
385 |
386 | export %inline
387 | Interpolation InstallType where
388 |   interpolate Library = "library"
389 |   interpolate (App b) = "app"
390 |
391 | export
392 | Eq InstallType where
393 |   Library == Library = True
394 |   App b1  == App b2  = b1 == b2
395 |   _       == _       = False
396 |
397 | export
398 | Ord InstallType where
399 |   compare Library  (App _)  = LT
400 |   compare (App _)  Library  = GT
401 |   compare Library  Library  = EQ
402 |   compare (App b1) (App b2) = compare b1 b2
403 |
404 | --------------------------------------------------------------------------------
405 | --          DBName
406 | --------------------------------------------------------------------------------
407 |
408 | ||| Name of a package collection. This must be a valid
409 | ||| file path body.
410 | public export
411 | record DBName where
412 |   constructor MkDBName
413 |   value : Body
414 |
415 | export %inline
416 | Eq DBName where (==) = (==) `on` value
417 |
418 | export %inline
419 | Ord DBName where compare = compare `on` value
420 |
421 | export %inline
422 | Interpolation DBName where interpolate = interpolate . value
423 |
424 | export %inline
425 | Cast DBName Body where
426 |   cast = value
427 |
428 | export %inline
429 | Head : DBName
430 | Head = MkDBName "HEAD"
431 |
432 | export %inline
433 | All : DBName
434 | All = MkDBName "all"
435 |
436 | --------------------------------------------------------------------------------
437 | --          Desc
438 | --------------------------------------------------------------------------------
439 |
440 | ||| A tagged package desc. We use the tag mainly to make sure that
441 | ||| the package desc in question has been checked for safety issues.
442 | ||| Since the tag is parameterized by a `PkgDesc`, we make sure
443 | ||| we will not inadvertently use a tag for a `PkgDesc` different to
444 | ||| the one we wrapped.
445 | public export
446 | record Desc (t : PkgDesc -> Type) where
447 |   constructor MkDesc
448 |   ||| The parsed package desc
449 |   desc : PkgDesc
450 |
451 |   ||| String content of the `.ipkg` file used when reading the package desc
452 |   cont : String
453 |
454 |   ||| Path to the file use when reading the package desc
455 |   path : File Abs
456 |
457 |   ||| Security tag. See `Pack.Runner.Database.check`
458 |   0 tag : t desc
459 |
460 | ||| This is used as a tag for unchecked `Desc`s.
461 | public export
462 | 0 U : PkgDesc -> Type
463 | U d = Unit
464 |
465 | namespace PkgDesc
466 |   ||| Lists the dependencies of a package.
467 |   export
468 |   dependencies : PkgDesc -> List PkgName
469 |   dependencies d = map (MkPkgName . pkgname) $ d.depends
470 |
471 | ||| Lists the dependencies of a package.
472 | export
473 | dependencies : Desc t -> List PkgName
474 | dependencies d = dependencies d.desc
475 |
476 | --------------------------------------------------------------------------------
477 | --          CmdArg
478 | --------------------------------------------------------------------------------
479 |
480 | ||| A command line argument of a call to the system shell.
481 | public export
482 | data CmdArg : Type where
483 |
484 |   ||| Escapable alternative represents a string that should be understood as a
485 |   ||| single argument disregarding its contents, i.e. the whole string should
486 |   ||| be passed as an argument even if it contains spaces or characters that
487 |   ||| are normally understood specially by shell.
488 |   Escapable : String -> CmdArg
489 |
490 |   ||| No escape alternative represents a raw string that is used "as is" at the
491 |   ||| shell call, thus allowing to pass commands to the shell itself.
492 |   ||| Be aware that spaces inside these strings would form several actual
493 |   ||| arguments and quotation marks and backslash symbols may interfere with
494 |   ||| escaping of other arguments.
495 |   NoEscape : String -> CmdArg
496 |
497 | ||| Interface that is used to mark which types can be used as a source for
498 | ||| command line arguments when building lists of such.
499 | ||| This allows to be able to pass different literals and expressions without
500 | ||| explicit wrapping them into the `CmdArg` type.
501 | public export
502 | interface CmdArgable a where
503 |   toCmdArg : a -> CmdArg
504 |
505 | export %inline
506 | CmdArgable CmdArg where
507 |   toCmdArg = id
508 |
509 | ||| Implementation that allows any type implementing `Interpolation` interface
510 | ||| to be used as an escapable command line argument.
511 | export %inline
512 | Interpolation a => CmdArgable a where
513 |   toCmdArg = Escapable . interpolate
514 |
515 | ||| A list of command line arguments.
516 | |||
517 | ||| This type is meant to look syntacitcally as a simple list, however
518 | ||| containing possibly values of different types which can form a command
519 | ||| line argument.
520 | |||
521 | ||| For example, a call to some command with redirection may look like this:
522 | ||| `sys ["echo", "a", NoEscape ">", file]`,
523 | ||| when, say, `file` is a value of type `File Abs`.
524 | public export
525 | data CmdArgList : Type where
526 |   Nil  : CmdArgList
527 |   (::) : CmdArgable a => a -> CmdArgList -> CmdArgList
528 |
529 | cmdArgList : CmdArgList -> List CmdArg
530 | cmdArgList []      = []
531 | cmdArgList (x::xs) = toCmdArg x :: cmdArgList xs
532 |
533 | ||| Converts a list of command line arguments to a single string
534 | ||| while putting spaces between arguments and escaping appropriate ones.
535 | |||
536 | ||| For example, call `escapeCmd ["echo", "&&", NoEscape "&&", "echo", "yes"]`
537 | ||| would return a string equivalent to the literal
538 | ||| `#""echo" "&&" && "echo" "yes""#`.
539 | export
540 | escapeCmd : CmdArgList -> String
541 | escapeCmd = unwords . map manageArg . cmdArgList where
542 |   manageArg : CmdArg -> String
543 |   manageArg $ Escapable s = escapeArg s
544 |   manageArg $ NoEscape s  = s
545 |
546 | namespace CmdArg
547 |
548 |   ||| Concatenate two command line arguments into one.
549 |   |||
550 |   ||| This operation respects meaning of the contents of each argument,
551 |   ||| whether they should be escaped or not.
552 |   ||| This allows, say, to form an argument which partially contains something
553 |   ||| escapable and partially contains a special shell argument, for example
554 |   ||| `sys ["cp", "-r", Escapable "\{dirName}/" ++ NoEscape "*", dest]` would
555 |   ||| list files with shell's `*` even if `dirName` contains spaces.
556 |   export
557 |   (++) : CmdArg -> CmdArg -> CmdArg
558 |   Escapable x ++ Escapable y = Escapable $ x ++ y
559 |   Escapable x ++ NoEscape y  = NoEscape $ escapeArg x ++ y
560 |   NoEscape x  ++ Escapable y = NoEscape $ x ++ escapeArg y
561 |   NoEscape x  ++ NoEscape y  = NoEscape $ x ++ y
562 |
563 | namespace CmdArgList
564 |
565 |   ||| Concatenation operation for command line argument lists.
566 |   export
567 |   (++) : CmdArgList -> CmdArgList -> CmdArgList
568 |   []      ++ ys = ys
569 |   (x::xs) ++ ys = x :: xs ++ ys
570 |
571 |   export
572 |   Semigroup CmdArgList where
573 |     (<+>) = (++)
574 |
575 |   export
576 |   Monoid CmdArgList where
577 |     neutral = []
578 |
579 |   ||| Specialised version of `concatMap` from `Foldable` for `CmdArgList`,
580 |   ||| since `CmdArgList` cannot implement `Foldable`.
581 |   export
582 |   concatMap : Monoid m => (CmdArg -> m) -> CmdArgList -> m
583 |   concatMap f []      = neutral
584 |   concatMap f (x::xs) = f (toCmdArg x) <+> concatMap f xs
585 |
586 |   ||| Function that forms a command line arguments list from a raw list of
587 |   ||| strings, treating each string as a single escapable argument.
588 |   export
589 |   fromStrList : List String -> CmdArgList
590 |   fromStrList = foldr (\x, xs => x::xs) Nil
591 |
592 | --------------------------------------------------------------------------------
593 | --          LineBufferingCmd
594 | --------------------------------------------------------------------------------
595 |
596 | ||| A wrapper for a command list of a shell command which allows to do
597 | ||| line buffering.
598 | public export
599 | record LineBufferingCmd where
600 |   [noHints]
601 |   constructor MkLineBufferingCmd
602 |   lineBufferingCmd : CmdArgList
603 |
604 | --------------------------------------------------------------------------------
605 | --          Logging
606 | --------------------------------------------------------------------------------
607 |
608 | ||| Level used during logging.
609 | public export
610 | data LogLevel : Type where
611 |   [noHints]
612 |   Debug   : LogLevel
613 |   Build   : LogLevel
614 |   Info    : LogLevel
615 |   Cache   : LogLevel
616 |   Warning : LogLevel
617 |   Silence : LogLevel
618 |
619 | llToNat : LogLevel -> Nat
620 | llToNat Debug   = 0
621 | llToNat Build   = 1
622 | llToNat Info    = 2
623 | llToNat Cache   = 3
624 | llToNat Warning = 4
625 | llToNat Silence = 5
626 |
627 | export
628 | Eq LogLevel where (==) = (==) `on` llToNat
629 |
630 | export
631 | Ord LogLevel where compare = compare `on` llToNat
632 |
633 | export
634 | Interpolation LogLevel where
635 |   interpolate Debug   = "debug"
636 |   interpolate Build   = "build"
637 |   interpolate Info    = "info"
638 |   interpolate Cache   = "cache"
639 |   interpolate Warning = "warning"
640 |   interpolate Silence = ""
641 |
642 | export
643 | logLevels : List (String, LogLevel)
644 | logLevels =
645 |   [ ("debug"  , Debug  )
646 |   , ("build"  , Build  )
647 |   , ("info"   , Info   )
648 |   , ("cache"  , Cache  )
649 |   , ("warning", Warning)
650 |   , ("silence", Silence)
651 |   ]
652 |
653 | ||| Reference `LogLevel` to be used as an auto implicit
654 | public export
655 | record LogRef where
656 |   [noHints]
657 |   constructor MkLogRef
658 |   level : LogLevel
659 |
660 | --------------------------------------------------------------------------------
661 | --          Errors
662 | --------------------------------------------------------------------------------
663 |
664 | ||| Error during marshalling from TOML to an Idris type.
665 | public export
666 | data TOMLErr : Type where
667 |   ||| A mandatory key/value pair in a toml file is
668 |   ||| missing
669 |   MissingKey : (path : List String) -> TOMLErr
670 |
671 |   ||| A value in a toml file has the wrong type
672 |   WrongType : (path  : List String) -> (type  : String) -> TOMLErr
673 |
674 | tomlPath : List String -> String
675 | tomlPath = concat . intersperse "."
676 |
677 | printTOMLErr : TOMLErr -> String
678 | printTOMLErr (MissingKey path) = "Missing toml key: \{tomlPath path}."
679 | printTOMLErr (WrongType path type) =
680 |   "Wrong type at \{tomlPath path}. Expect \{type}."
681 |
682 | ||| Prefix the given TOML key to an error message. This allows us to
683 | ||| specify exactly where in a TOML structure things went wrong.
684 | export
685 | prefixKey : (key : String) -> Either TOMLErr a -> Either TOMLErr a
686 | prefixKey k = mapFst $ \case
687 |   MissingKey p => MissingKey (k :: p)
688 |   WrongType p t => WrongType (k :: p) t
689 |
690 | ||| Errors that can occur when running pack programs.
691 | public export
692 | data PackErr : Type where
693 |   ||| Failed to get the path of the current directory.
694 |   NoCurDir   : PackErr
695 |
696 |   ||| Failed to get package directory path
697 |   NoPackDir  : PackErr
698 |
699 |   ||| Failed to create temporary directory
700 |   NoTmpDir  : PackErr
701 |
702 |   ||| Failed to create the given directory
703 |   MkDir      : (path : Path Abs) -> (err : FileError) -> PackErr
704 |
705 |   ||| Failed to read the given file
706 |   ReadFile   : (path : File Abs) -> (err : FileError) -> PackErr
707 |
708 |   ||| Failed to write to the given file
709 |   WriteFile  : (path : File Abs) -> (err : FileError) -> PackErr
710 |
711 |   ||| Failed to read the content of a directory
712 |   DirEntries : (path : Path Abs) -> (err : FileError) -> PackErr
713 |
714 |   ||| Error when running the given system command
715 |   Sys        : (cmd : CmdArgList) -> (err : Int) -> PackErr
716 |
717 |   ||| Error when changing into the given directory
718 |   ChangeDir  : (path : Path Abs) -> PackErr
719 |
720 |   ||| The given package is not in the package data base
721 |   UnknownPkg : (name : PkgName) -> PackErr
722 |
723 |   ||| The given package is not a local package
724 |   NotLocalPkg : (name : PkgName) -> PackErr
725 |
726 |   ||| The given package is not an applicatio
727 |   ||| (No executable name set in the `.ipkg` file)
728 |   NoApp      : (rep : PkgName) -> PackErr
729 |
730 |   ||| The given package is not an application
731 |   ||| (No executable name set in the `.ipkg` file)
732 |   NoAppIpkg  : (path : File Abs) -> PackErr
733 |
734 |   ||| An erroneous package description in a package DB file
735 |   InvalidPackageDesc : (s : String) -> PackErr
736 |
737 |   ||| The package database is empty (no header)
738 |   EmptyPkgDB : PackErr
739 |
740 |   ||| Invalid package database header
741 |   InvalidDBHeader : (s : String) -> PackErr
742 |
743 |   ||| Invalid package database header
744 |   InvalidDBName : (s : String) -> PackErr
745 |
746 |   ||| Invalid package type
747 |   InvalidPkgType : (s : String) -> PackErr
748 |
749 |   ||| Invalid package version
750 |   InvalidPkgVersion : (s : String) -> PackErr
751 |
752 |   ||| Invalid log level
753 |   InvalidLogLevel : (s : String) -> PackErr
754 |
755 |   ||| Failed to parse an .ipkg file
756 |   InvalidIpkgFile  : (path : File Abs) -> PackErr
757 |
758 |   ||| Invalid file path body
759 |   InvalidBody  : (s : String) -> PackErr
760 |
761 |   ||| Failed to parse a file path
762 |   NoFilePath : (str : String) -> PackErr
763 |
764 |   ||| The given core package (base, contrib, etc.)
765 |   ||| is missing from the Idris installation.
766 |   MissingCorePackage :
767 |        (name    : PkgName)
768 |     -> (version : PkgVersion)
769 |     -> (commit  : Commit)
770 |     -> PackErr
771 |
772 |   ||| Unknown command line argument
773 |   UnknownArg : (arg : String) -> PackErr
774 |
775 |   ||| Invalid command line arguments (in micropack)
776 |   InvalidArgs : (args : List String) -> PackErr
777 |
778 |   ||| Erroneous command line argument
779 |   ErroneousArg : (err : String) -> PackErr
780 |
781 |   ||| Trying to run zero or more than one local package
782 |   ||| (or something that isn't a local package).
783 |   BuildMany : List Body -> PackErr
784 |
785 |   ||| Unknown pack command
786 |   UnknownCommand : String -> (usage : String) -> PackErr
787 |
788 |   ||| Unknown pack command
789 |   InvalidCmdArgs :
790 |        (cmd   : String)
791 |     -> (args  : List String)
792 |     -> (usage : String)
793 |     -> PackErr
794 |
795 |   ||| Trying to clone a repository into an existing
796 |   ||| directory.
797 |   DirExists : Path Abs -> PackErr
798 |
799 |   ||| Error in a toml file.
800 |   TOMLFile :  (file : File Abs) -> (err : TOMLErr) -> PackErr
801 |
802 |   ||| Error in a toml file.
803 |   TOMLParse : (err : String) -> PackErr
804 |
805 |   ||| Number of failures when building packages.
806 |   BuildFailures : Nat -> PackErr
807 |
808 |   ||| User tried to manually install the pack application
809 |   ManualInstallPackApp : PackErr
810 |
811 |   ||| User aborted installation of a lib/app with custom build hooks.
812 |   SafetyAbort : PackErr
813 |
814 |   CyclicDeps : List PkgName -> PackErr
815 |
816 | ||| Prints an error that occured during program execution.
817 | export
818 | printErr : PackErr -> String
819 | printErr NoCurDir = "Failed to get current directory."
820 |
821 | printErr NoPackDir = """
822 |   Failed to figure out package directory.
823 |   This means, that neither environment variable \"PACK_DIR\"
824 |   nor environment varaible \"HOME\" was set.
825 |   """
826 |
827 | printErr NoTmpDir = """
828 |   Failed to create temporary directory.
829 |   Please check directory `PACK_DIR` and make sure to remove
830 |   all `.tmpXY` directories you no longer need.
831 |   """
832 |
833 | printErr (MkDir path err) =
834 |   "Error when creating directory \{quote path}: \{err}."
835 |
836 | printErr (ReadFile path err) =
837 |   "Error when reading file \{quote path}: \{err}."
838 |
839 | printErr (WriteFile path err) =
840 |   "Error when writing to file \{quote path}: \{err}."
841 |
842 | printErr (DirEntries path err) =
843 |   "Error when reading directory \{quote path}: \{err}."
844 |
845 | printErr (Sys cmd err) = """
846 |   Error when executing system command.
847 |   Command: \{escapeCmd cmd}
848 |   Error code: \{show err}
849 |   """
850 |
851 | printErr (ChangeDir path) =
852 |   "Failed to change to directory \{quote path}."
853 |
854 | printErr (InvalidPackageDesc s) = """
855 |   Invalid package description: \{quote s}.
856 |   This should be of the format \"name,url,commit hash,ipkg file\".
857 |   """
858 |
859 | printErr (InvalidDBHeader s) = """
860 |   Invalid data base header: \{quote s}.
861 |   This should be of the format \"idris2 commit hash,idris2 version\".
862 |   """
863 |
864 | printErr (InvalidDBName s) = """
865 |   Invalid data collection name: \{quote s}.
866 |   This should be a non-empty string without path separators.
867 |   """
868 |
869 | printErr (InvalidBody s) = "Invalid file path body: \{quote s}."
870 |
871 | printErr (InvalidPkgType s) = """
872 |   Invalid package type: \{quote s}.
873 |   Valid types are `lib` and `bin`.
874 |   """
875 |
876 | printErr (InvalidPkgVersion s) = "Invalid package version: \{quote s}."
877 |
878 | printErr (InvalidLogLevel s) = """
879 |   Invalid log level: \{quote s}. Valid values are
880 |   \{joinBy "\n" $ ("- " ++) . fst <$> logLevels}
881 |   """
882 |
883 | printErr (UnknownPkg name) = "Unknown package: \{name}"
884 |
885 | printErr (NotLocalPkg name) = "Not a local package: \{name}"
886 |
887 | printErr (NoApp rep) = "Package \{rep} is not an application"
888 |
889 | printErr (NoAppIpkg p) = "Package \{p} is not an application"
890 |
891 | printErr EmptyPkgDB = "Empty package data base"
892 |
893 | printErr (InvalidIpkgFile path) =
894 |   "Failed to parse .ipkg file: \{path}"
895 |
896 | printErr (MissingCorePackage nm v c) =
897 |   "Core package \{quote nm} missing for Idris2 version \{v} (commit: \{c})"
898 |
899 | printErr (UnknownArg arg) = "Unknown command line arg: \{arg}"
900 |
901 | printErr (InvalidArgs args) = "Invalid command line args: \{unwords args}"
902 |
903 | printErr (ErroneousArg err) = err
904 |
905 | printErr (UnknownCommand cmd usage) =
906 |   """
907 |   Unknown command: "\{cmd}"
908 |
909 |   \{usage}
910 |   """
911 |
912 | printErr (InvalidCmdArgs cmd args usage) =
913 |   """
914 |   Invalid argument(s) for command `\{cmd}`.
915 |
916 |   \{usage}
917 |   """
918 |
919 | printErr (BuildMany []) = "No local `.ipkg` files found."
920 | printErr (BuildMany fs@(_::_)) = """
921 |   Ambiguous `.ipkg` files:
922 |   \{joinBy "\n" $ ("- " ++) . interpolate <$> fs}
923 |   Please choose only one.
924 |   """
925 |
926 | printErr (NoFilePath s) = "Not a file path : \{s}"
927 |
928 | printErr (DirExists path) = """
929 |   Failed to clone Git repository into \{path}.
930 |   Directory already exists.
931 |   """
932 |
933 | printErr (TOMLFile file err) =
934 |   "Error in file \{file}: \{printTOMLErr err}."
935 |
936 | printErr (TOMLParse err) = err
937 |
938 | printErr (BuildFailures 1) = "1 package failed to build."
939 |
940 | printErr (BuildFailures n) = "\{show n} packages failed to build."
941 |
942 | printErr ManualInstallPackApp = """
943 |   You are not supposed to manually install or remove the pack
944 |   application. In order to update pack to the latest version on
945 |   GitHub, run `pack update`.
946 |
947 |   Note: If you didn't run `pack install-app pack` or a similar
948 |   operation, "pack" might be listed as an auto-install application
949 |   in one of your pack.toml files. Please remove it from there.
950 |   """
951 |
952 | printErr (CyclicDeps ps) =
953 |  let cycle = fastConcat $ intersperse " -> " (map value ps)
954 |   in """
955 |      Dependency cylce detected:
956 |      \{cycle}
957 |      """
958 |
959 | printErr SafetyAbort = "Aborted."
960 |
961 | ||| Tries to convert a string to a `DBName` by first converting
962 | ||| it to a valid file path body.
963 | export
964 | readDBName : String -> Either PackErr DBName
965 | readDBName s = case Body.parse s of
966 |   Just b  => Right $ MkDBName b
967 |   Nothing => Left (InvalidDBName s)
968 |
969 | ||| Tries to convert a string to a file path body.
970 | export
971 | readBody : String -> Either PackErr Body
972 | readBody s = case Body.parse s of
973 |   Just b  => Right b
974 |   Nothing => Left (InvalidBody s)
975 |
976 | ||| Tries to convert a string to a package type.
977 | export
978 | readPkgType : String -> Either PackErr PkgType
979 | readPkgType "lib" = Right PLib
980 | readPkgType "bin" = Right PApp
981 | readPkgType "app" = Right PApp
982 | readPkgType s     = Left (InvalidPkgType s)
983 |
984 | ||| Tries to convert a string representing a relative or absolute
985 | ||| file path. This uses `toAbsPath` internally, so it is somewhat
986 | ||| tolerant w.r.t. to dubious file paths. It fails, however, if the
987 | ||| given path does not contain at least one file path body.
988 | export
989 | readAbsFile : (curdir : Path Abs) -> String -> Either PackErr (File Abs)
990 | readAbsFile cd s = case split $ toAbsPath cd (fromString s) of
991 |   Just (p,b) => Right $ MkF p b
992 |   Nothing    => Left (NoFilePath s)
993 |
994 | export
995 | readLogLevel : String -> Either PackErr LogLevel
996 | readLogLevel str = maybeToEither (InvalidLogLevel str) $ lookup str logLevels
997 |