Skip to content

Commit c6112ce

Browse files
committed
use new {split,join}Name for alterExtension and extention definitions
1 parent 6dc7471 commit c6112ce

File tree

3 files changed

+118
-30
lines changed

3 files changed

+118
-30
lines changed

src/Pathy/Gen.purs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ module Pathy.Gen
55
, genRelDirPath
66
, genRelFilePath
77
, genRelAnyPath
8+
, genName
89
) where
910

1011
import Prelude
@@ -18,27 +19,27 @@ import Data.Foldable (foldr)
1819
import Data.List as L
1920
import Data.NonEmpty ((:|))
2021
import Data.String.Gen as SG
21-
import Data.String.NonEmpty (NonEmptyString, cons)
22-
import Pathy (AbsPath, AbsFile, AbsDir, RelDir, RelFile, RelPath, (</>))
22+
import Data.String.NonEmpty (cons)
23+
import Pathy (AbsDir, AbsFile, AbsPath, Dir, RelDir, RelFile, RelPath, (</>))
2324
import Pathy as P
2425

25-
genName m. MonadGen m MonadRec m m NonEmptyString
26-
genName = cons <$> genChar <*> SG.genString genChar
26+
genName m a. MonadGen m MonadRec m m (P.Name a)
27+
genName = map P.Name $ cons <$> genChar <*> SG.genString genChar
2728
where
2829
genChar = Gen.oneOf $ CG.genDigitChar :| [CG.genAlpha]
2930

3031
genAbsDirPath :: forall m. MonadGen m => MonadRec m => m AbsDir
3132
genAbsDirPath = Gen.sized \size → do
3233
newSize ← Gen.chooseInt 0 size
3334
Gen.resize (const newSize) do
34-
parts L.List NonEmptyStringGen.unfoldable genName
35-
pure $ foldr (flip P.appendPath <<< P.dir' <<< P.Name) P.rootDir parts
35+
parts L.List (P.Name Dir)Gen.unfoldable genName
36+
pure $ foldr (flip P.appendPath <<< P.dir') P.rootDir parts
3637

3738
genAbsFilePath :: forall m. MonadGen m => MonadRec m => m AbsFile
3839
genAbsFilePath = do
3940
dir ← genAbsDirPath
4041
file ← genName
41-
pure $ dir </> P.file' (P.Name file)
42+
pure $ dir </> P.file' file
4243

4344
genAbsAnyPath :: forall m. MonadGen m => MonadRec m => m AbsPath
4445
genAbsAnyPath = Gen.oneOf $ (Left <$> genAbsDirPath) :| [Right <$> genAbsFilePath]
@@ -47,14 +48,14 @@ genRelDirPath :: forall m. MonadGen m => MonadRec m => m RelDir
4748
genRelDirPath = Gen.sized \size → do
4849
newSize ← Gen.chooseInt 0 size
4950
Gen.resize (const newSize) do
50-
parts L.List NonEmptyStringGen.unfoldable genName
51-
pure $ foldr (flip P.appendPath <<< P.dir' <<< P.Name) P.currentDir parts
51+
parts L.List (P.Name Dir)Gen.unfoldable genName
52+
pure $ foldr (flip P.appendPath <<< P.dir') P.currentDir parts
5253

5354
genRelFilePath :: forall m. MonadGen m => MonadRec m => m RelFile
5455
genRelFilePath = do
5556
dir ← genRelDirPath
5657
file ← genName
57-
pure $ dir </> P.file' (P.Name file)
58+
pure $ dir </> P.file' file
5859

5960
genRelAnyPath :: forall m. MonadGen m => MonadRec m => m RelPath
6061
genRelAnyPath = Gen.oneOf $ (Left <$> genRelDirPath) :| [Right <$> genRelFilePath]

src/Pathy/Name.purs

Lines changed: 59 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ module Pathy.Name where
22

33
import Prelude
44

5-
import Data.Maybe (Maybe(..), maybe)
5+
import Data.Maybe (Maybe(..), fromMaybe)
66
import Data.Newtype (class Newtype)
77
import Data.String as S
88
import Data.String.NonEmpty (NonEmptyString)
@@ -24,32 +24,72 @@ derive newtype instance ordName :: Ord (Name a)
2424
instance showName :: Show (Name a) where
2525
show (Name name) = "(Name " <> show name <> ")"
2626

27-
-- | Retrieves the extension of a name.
27+
-- | Splits `Name` in name and extension part.
28+
-- |
29+
-- | ```purescript
30+
-- | splitName (Name ".foo") == { name: ".foo", extension: Nothing }
31+
-- | splitName (Name "foo.") == { name: "foo.", extension: Nothing }
32+
-- | splitName (Name "foo") == { name: "foo", extension: Nothing }
33+
-- | splitName (Name ".") == { name: ".", extension: Nothing }
34+
-- | splitName (Name "foo.baz") == { name: "foo", extension: Just "baz" }
35+
-- | ```
36+
-- | _Note, in real code all strings from this examples would be `NonEmptyString`._
37+
-- |
38+
-- | Also for any `Name` this property holds:
39+
-- | ```purescript
40+
-- | joinName <<< splitName = id
41+
-- | ````
42+
-- | see [`joinName`](#v:joinName).
43+
splitName :: forall n. Name n -> { name :: NonEmptyString, ext :: Maybe NonEmptyString }
44+
splitName (Name nameIn) =
45+
fromMaybe { name: nameIn, ext: Nothing } do
46+
idx <- NES.lastIndexOf (S.Pattern ".") nameIn
47+
name <- NES.take idx nameIn
48+
ext <- NES.drop (idx + 1) nameIn
49+
pure $ { name, ext: Just ext }
50+
51+
-- | Joins name and extension part into one `Name`.
52+
-- |
53+
-- | Also for any `Name` this property holds:
54+
-- | ```purescript
55+
-- | joinName <<< splitName = id
56+
-- | ````
57+
-- | see [`splitName`](#v:splitName).
58+
joinName :: forall n. { name :: NonEmptyString, ext :: Maybe NonEmptyString } -> Name n
59+
joinName { name, ext } = Name $ case ext of
60+
Nothing -> name
61+
Just ext -> name <> NES.singleton '.' <> ext
62+
63+
-- | Retrieves the extension of a name. also see [`splitName`](#v:splitName)
64+
-- |
65+
-- | ```purescript
66+
-- | extension (Name ".foo") == Nothing
67+
-- | extension (Name "foo.") == Nothing
68+
-- | extension (Name ".") == Nothing
69+
-- | extension (Name "foo.baz") == Just "baz"
70+
-- | ````
71+
-- | _Note, in real code all strings from this examples would be `NonEmptyString`._
2872
extension :: forall n. Name n -> Maybe NonEmptyString
29-
extension (Name name) =
30-
flip NES.drop name <<< (_ + 1) =<< NES.lastIndexOf (S.Pattern ".") name
73+
extension = splitName >>> _.ext
3174

3275
-- | Alters an extension of a name. This allows extensions to be added, removed,
33-
-- | or modified.
76+
-- | or modified. see [`splitName`](#v:splitName) and [`joinName`](#v:joinName)
77+
-- | for how a `Name` is split into name and extention part and joined back
78+
-- | into a `Name`.
79+
-- |
80+
-- | Also for any `Name` this property holds:
81+
-- | ```purescript
82+
-- | alterExtension id = id
83+
-- | ````
3484
alterExtension
3585
:: forall n
3686
. (Maybe NonEmptyString -> Maybe NonEmptyString)
3787
-> Name n
3888
-> Name n
39-
alterExtension f (Name name) =
40-
case NES.lastIndexOf (S.Pattern ".") name of
41-
Nothing -> extend name Nothing
42-
Just i ->
43-
case NES.splitAt i name of
44-
Just { before: Just n, after } -> extend n (NES.drop 1 =<< after)
45-
_ -> extend name Nothing
46-
where
47-
extend name' ext =
48-
maybe
49-
(Name name')
50-
(\ext' -> Name (name' <> NES.singleton '.' <> ext'))
51-
(f ext)
52-
89+
alterExtension f n =
90+
let spn = splitName n
91+
in joinName spn{ext = f spn.ext}
92+
5393
-- | A class for creating `Name` values from type-level strings. This allows us
5494
-- | to guarantee that a name is not empty at compile-time.
5595
class IsName sym where

test/Main.purs

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,15 @@ import Control.Monad.Eff (Eff)
66
import Control.Monad.Eff.Console (CONSOLE, info)
77
import Control.Monad.Eff.Exception (EXCEPTION, throw)
88
import Data.Maybe (Maybe(..))
9+
import Data.Newtype (un)
10+
import Data.NonEmpty ((:|))
911
import Data.String as Str
1012
import Data.String.NonEmpty (NonEmptyString)
13+
import Data.String.NonEmpty as NES
1114
import Data.Symbol (SProxy(..))
12-
import Pathy (class IsDirOrFile, class IsRelOrAbs, Abs, Dir, Path, Rel, alterExtension, canonicalize, currentDir, dir, file, parentOf, parseAbsDir, parseAbsFile, parseRelDir, parseRelFile, posixParser, posixPrinter, printPath, relativeTo, rename, rootDir, sandbox, debugPrintPath, unsandbox, (<..>), (<.>), (</>))
15+
import Pathy (class IsDirOrFile, class IsRelOrAbs, Abs, Dir, Name(..), Path, Rel, alterExtension, canonicalize, currentDir, debugPrintPath, dir, extension, file, parentOf, parseAbsDir, parseAbsFile, parseRelDir, parseRelFile, posixParser, posixPrinter, printPath, relativeTo, rename, rootDir, sandbox, unsandbox, (<..>), (<.>), (</>), joinName, reflectName, splitName)
1316
import Pathy.Gen as PG
17+
import Test.QuickCheck ((===))
1418
import Test.QuickCheck as QC
1519
import Test.QuickCheck.Gen as Gen
1620
import Unsafe.Coerce (unsafeCoerce)
@@ -65,6 +69,31 @@ parsePrintRelFilePath :: Gen.Gen QC.Result
6569
parsePrintRelFilePath = PG.genRelFilePath <#> \path ->
6670
parsePrintCheck path (parseRelFile posixParser $ printTestPath path)
6771

72+
genAmbigiousName :: forall a. Gen.Gen (Name a)
73+
genAmbigiousName =
74+
let
75+
genNES = PG.genName <#> un Name
76+
in
77+
map Name $ Gen.oneOf $ genNES :|
78+
[ genNES <#> \a -> a <> (NES.singleton '.')
79+
, genNES <#> \a -> (NES.singleton '.') <> a
80+
, pure (NES.singleton '.')
81+
, do
82+
a <- genNES
83+
b <- genNES
84+
pure $ a <> (NES.singleton '.') <> b
85+
]
86+
87+
checkAlterExtensionId :: Gen.Gen QC.Result
88+
checkAlterExtensionId = do
89+
n <- genAmbigiousName
90+
pure $ alterExtension id n === id n
91+
92+
checkJoinSplitNameId :: Gen.Gen QC.Result
93+
checkJoinSplitNameId = do
94+
n <- genAmbigiousName
95+
pure $ joinName (splitName n) === id n
96+
6897
checkRelative :: forall b. IsDirOrFile b => Gen.Gen (Path Abs b) -> Gen.Gen QC.Result
6998
checkRelative gen = do
7099
p1 <- gen
@@ -92,6 +121,8 @@ main = do
92121
info "checking `parse <<< print` for `RelFile`" *> QC.quickCheck parsePrintRelFilePath
93122
info "checking `relativeTo` for `AbsDir`" *> QC.quickCheck (checkRelative PG.genAbsDirPath)
94123
info "checking `relativeTo` for `AbsFile`" *> QC.quickCheck (checkRelative PG.genAbsFilePath)
124+
info "checking `joinName <<< splitName === id`" *> QC.quickCheck 100 checkJoinSplitNameId
125+
info "checking `alterExtension id === id`" *> QC.quickCheck 100 checkAlterExtensionId
95126

96127
-- Should not compile:
97128
-- test
@@ -231,6 +262,22 @@ main = do
231262
(rename (alterExtension (const Nothing)) (file (SProxy :: SProxy "image.png")))
232263
(file $ SProxy :: SProxy "image")
233264

265+
test """extension (Name ".foo") == Nothing"""
266+
(extension (reflectName $ SProxy :: SProxy ".foo"))
267+
(Nothing)
268+
test """extension (Name "foo.") == Nothing"""
269+
(extension (reflectName $ SProxy :: SProxy "foo."))
270+
(Nothing)
271+
test """extension (Name "foo") == Nothing"""
272+
(extension (reflectName $ SProxy :: SProxy "foo"))
273+
(Nothing)
274+
test """extension (Name ".") == Nothing"""
275+
(extension (reflectName $ SProxy :: SProxy "."))
276+
(Nothing)
277+
test """extension (Name "foo.baz") == (Just "baz")"""
278+
(extension (reflectName $ SProxy :: SProxy "foo.baz"))
279+
(NES.fromString "baz")
280+
234281
test "sandbox - fail when relative path lies outside sandbox (above)"
235282
(sandbox (rootDir </> dirBar) (parentOf currentDir))
236283
Nothing

0 commit comments

Comments
 (0)