1 {-# LANGUAGE DataKinds #-}
2 {-# LANGUAGE DeriveFunctor #-}
3 {-# LANGUAGE FlexibleInstances #-}
5 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
6 {-# LANGUAGE MultiParamTypeClasses #-}
7 {-# LANGUAGE OverloadedStrings #-}
8 {-# LANGUAGE TypeFamilies #-}
9 {-# OPTIONS_GHC -fno-warn-tabs #-}
10 {-# OPTIONS_GHC -fno-warn-orphans #-}
12 module Hadmin.Lib.System.File.Path where
14 import Data.Foldable (foldMap)
15 import Data.Function (($), (.))
16 import Data.Functor (Functor(..), (<$>))
17 import qualified Data.List as List
18 import Data.Maybe (Maybe(..))
19 import Data.Monoid (Monoid(..), (<>))
20 import Data.String (IsString(..))
21 import Data.Text (Text)
22 import Data.Text.Buildable (Buildable(..))
23 import GHC.Exts (IsList(..))
24 import Prelude (undefined)
25 import qualified System.FilePath.Posix as FP
27 import Hadmin.Lib.Data.Text
30 type Path pos a = InPos pos (InDir a)
33 data Position = Absolute | Relative
36 -- | Singleton type for 'Position'.
43 -- | Implicit class for 'Position'.
46 instance IPos 'Absolute where pos = Abs
47 instance IPos 'Relative where pos = Rel
50 data InPos pos a = InPos (SPos pos) a
52 instance Buildable a => Buildable (InPos pos a) where
53 build (InPos Abs a) = build FP.pathSeparator <> build a
54 build (InPos Rel a) = build a
55 instance (IsString a, IPos pos) => IsString (InPos pos a) where
56 fromString = InPos pos . fromString
59 type family PosOf x :: Position
60 type instance PosOf (InPos pos a) = pos
64 newtype Dir = Dir [Dir_Seg]
67 type AbsDir = InPos 'Absolute Dir
68 type RelDir = InPos 'Relative Dir
69 instance IsString Dir where
70 fromString = Dir . (fromString <$>) . splitOnChar FP.pathSeparator
71 instance IsList Dir where
72 type Item Dir = Dir_Seg
73 fromList = Dir . foldMap (splitOnChar FP.pathSeparator)
74 toList (Dir d) = toList d
75 instance Buildable Dir where
80 (build FP.pathSeparator)
84 absDir :: InPos pos a -> InPos 'Absolute a
85 absDir (InPos _p a) = InPos Abs a
87 relDir :: InPos pos a -> InPos 'Relative a
88 relDir (InPos _p a) = InPos Rel a
92 data InDir a = InDir Dir a
94 instance IsString (a -> InDir a) where
95 fromString = InDir . fromString
96 instance IsString a => IsString (InDir a) where
98 case splitOnChar FP.pathSeparator s of
99 [] -> InDir (Dir []) $ fromString ""
100 l -> InDir (Dir $ fromString <$> List.init l) $ fromString (List.last l)
101 instance IsList (a -> InDir a) where
102 type Item (a -> InDir a) = Dir_Seg
103 fromList = InDir . fromList
105 instance Buildable a => Buildable (InDir a) where
106 build (InDir d a) = build d <> build FP.pathSeparator <> build a
108 -- ** Class 'Dir_Parent'
110 -- | Return the parent 'Dir' of given 'Dir'
111 class Dir_Parent d where
112 type Dir_Parent_Dir d
113 dir_parent :: d -> Maybe (Dir_Parent_Dir d)
115 instance Dir_Parent Dir where
116 type Dir_Parent_Dir Dir = Dir
120 _ -> Just $ Dir (List.init p)
121 instance Dir_Parent a => Dir_Parent (InPos pos a) where
122 type Dir_Parent_Dir (InPos pos a) = InPos pos (Dir_Parent_Dir a)
123 dir_parent (InPos p a) = InPos p <$> dir_parent a
124 instance Dir_Parent (InDir a) where
125 type Dir_Parent_Dir (InDir a) = Dir
126 dir_parent (InDir d _a) = Just d
128 instance Dir_Parent File where
129 type Dir_Parent_Dir File = Dir
130 dir_parent (File _f) = Just $ Dir []
133 -- ** Class 'Dir_Ancestors'
135 -- | Return self and parents 'Dir' of given 'Dir', in topological order.
136 class Dir_Ancestors d where
137 type Dir_Ancestors_Dir d
138 dir_ancestors :: d -> [Dir_Parent_Dir d]
140 instance Dir_Ancestors Dir where
141 type Dir_Ancestors_Dir Dir = Dir
142 dir_ancestors (Dir p) =
144 List.foldl' (\acc seg ->
147 Dir d:_ -> Dir (d<>[seg]):acc
149 instance Dir_Ancestors a => Dir_Ancestors (InPos pos a) where
150 type Dir_Ancestors_Dir (InPos pos a) = InPos pos (Dir_Ancestors_Dir a)
151 dir_ancestors (InPos p a) = InPos p <$> dir_ancestors a
152 instance Dir_Ancestors (InDir a) where
153 type Dir_Ancestors_Dir (InDir a) = Dir
154 dir_ancestors (InDir d _a) = dir_ancestors d
156 instance Dir_Ancestors File where
157 type Dir_Ancestors_Dir File = Dir
158 dir_ancestors (File _f) = [Dir []]
161 -- ** Class 'Dir_Append'
162 class Dir_Append p q where
163 type Dir_Append_Dir p q
164 (</>) :: p -> q -> Dir_Append_Dir p q
165 instance Dir_Append (InPos p Dir) (InPos 'Relative Dir) where
166 type Dir_Append_Dir (InPos p Dir) (InPos 'Relative Dir) = InPos p Dir
167 (</>) (InPos p x) (InPos _q y) = InPos p (x <> y)
168 instance Dir_Append (InPos p Dir) File where
169 type Dir_Append_Dir (InPos p Dir) File = InPos p (InDir File)
170 (</>) (InPos p d) f = InPos p (InDir d f)
171 instance Dir_Append (InPos p Dir) (InPos 'Relative (InDir a)) where
172 type Dir_Append_Dir (InPos p Dir) (InPos 'Relative (InDir a)) = InPos p (InDir a)
173 (</>) (InPos p x) (InPos _q (InDir y a)) = InPos p (InDir (x <> y) a)
176 newtype File = File [Text]
177 instance IsString File where
178 fromString = File . (fromString <$>) . splitOnCharWithEmpty FP.extSeparator
179 instance Buildable File where
183 (build FP.extSeparator)
186 type RelFile = InPos 'Relative (InDir File)