diff --git a/app/Main.hs b/app/Main.hs index 10421fb..1256e28 100644 --- a/app/Main.hs +++ b/app/Main.hs @@ -1,6 +1,10 @@ module Main where -import Data.String.Strip +import Args +import Options.Applicative (execParser) main :: IO () -main = interact strip +main = execParser app >>= run + +run :: Args -> IO () +run = putStrLn . show diff --git a/escape-from-itunes.cabal b/escape-from-itunes.cabal index 5886d44..8cac4bb 100644 --- a/escape-from-itunes.cabal +++ b/escape-from-itunes.cabal @@ -15,9 +15,13 @@ cabal-version: >=1.10 library hs-source-dirs: src - exposed-modules: Data.String.Strip + exposed-modules: Itunes + , Args + , Attributes build-depends: base >= 4.7 && < 5 , idiii == 0.1.3.3 + , optparse-applicative == 0.14.3.0 + , regex-compat == 0.95.1 default-language: Haskell2010 executable escape-from-itunes @@ -26,16 +30,19 @@ executable escape-from-itunes ghc-options: -threaded -rtsopts -with-rtsopts=-N build-depends: base , escape-from-itunes + , optparse-applicative == 0.14.3.0 default-language: Haskell2010 test-suite escape-from-itunes-test type: exitcode-stdio-1.0 hs-source-dirs: test main-is: Spec.hs + other-modules: ArgsSpec build-depends: base , escape-from-itunes , hspec , QuickCheck + , optparse-applicative == 0.14.3.0 ghc-options: -threaded -rtsopts -with-rtsopts=-N default-language: Haskell2010 diff --git a/src/Args.hs b/src/Args.hs new file mode 100644 index 0000000..9a77988 --- /dev/null +++ b/src/Args.hs @@ -0,0 +1,39 @@ +module Args where + +import Options.Applicative +import Data.Semigroup ((<>)) +import Text.Regex (mkRegex, splitRegex) +import Attributes + +data Args = Args { + source :: String + , target :: String + , attributes :: [Attribute] + } + deriving (Show, Eq) + +getArgs :: Parser Args +getArgs = Args + <$> strOption + ( long "source" <> + short 's' <> + help "Root of existing library") + <*> strOption + ( long "target" <> + short 't' <> + help "Root of new library") + <*> attrList + +attrList :: Parser [Attribute] +attrList = option (maybeReader attrList) ( + long "attributes" <> + short 'a' <> + help "comma-separated list of orderedf attributes for building directory tree") + where attrList = sequence . (fmap toAttribute) . (splitRegex (mkRegex ",")) + helpString = "comma-separated list of ordered attributes for building directory tree. Chosen from artist, title, album, year, track, composer" + +app :: ParserInfo Args +app = info (getArgs <**> helper) + ( fullDesc + <> progDesc "Copies an iTunes music library to a new directory tree built from the ID3 tag hierarchy provided" + <> header "Make your music library make sense" ) diff --git a/src/Attributes.hs b/src/Attributes.hs new file mode 100644 index 0000000..fb6cd4c --- /dev/null +++ b/src/Attributes.hs @@ -0,0 +1,34 @@ +module Attributes where + +import Data.Accessor +import ID3.Simple +import ID3.Type + +data Attribute = Artist + | Title + | Album + | Year + | Track + | Composer + deriving (Show, Eq) + +toAttribute :: String -> Maybe Attribute +toAttribute "artist" = Just Artist +toAttribute "title" = Just Title +toAttribute "album" = Just Album +toAttribute "year" = Just Year +toAttribute "track" = Just Track +toAttribute "composer" = Just Composer +toAttribute _ = Nothing + +getAttribute :: Tag -> Attribute -> Maybe String +getAttribute tag Artist = getArtist tag +getAttribute tag Title = getTitle tag +getAttribute tag Year = getYear tag +getAttribute tag Track = getTrack tag +getAttribute tag Composer = getFrameText "TCOM" tag + +getFrameText :: FrameID -> Tag -> Maybe String +getFrameText frid tag = case tag^.frame frid of + Nothing -> Nothing + Just fr -> Just (fr^.textContent) diff --git a/src/Data/String/Strip.hs b/src/Data/String/Strip.hs deleted file mode 100644 index a8d5273..0000000 --- a/src/Data/String/Strip.hs +++ /dev/null @@ -1,6 +0,0 @@ -module Data.String.Strip (strip) where - -import Data.Char - -strip :: String -> String -strip = dropWhile isSpace . reverse . dropWhile isSpace . reverse diff --git a/src/Itunes.hs b/src/Itunes.hs new file mode 100644 index 0000000..ca9afd6 --- /dev/null +++ b/src/Itunes.hs @@ -0,0 +1 @@ +module Itunes where diff --git a/stack.yaml b/stack.yaml index eb0da62..e800091 100644 --- a/stack.yaml +++ b/stack.yaml @@ -40,6 +40,8 @@ packages: # (e.g., acme-missiles-0.3) extra-deps: [ idiii-0.1.3.3 + , optparse-applicative-0.14.3.0 + , regex-compat-0.95.1 ] # Override default flag values for local packages and extra-deps diff --git a/test/ArgsSpec.hs b/test/ArgsSpec.hs new file mode 100644 index 0000000..646341b --- /dev/null +++ b/test/ArgsSpec.hs @@ -0,0 +1,33 @@ +module ArgsSpec where + +import Test.Hspec +import Options.Applicative +import Attributes +import Args + +spec = do + describe "parsing arguments" $ do + it "parses properly supplied short arguments" $ do + getParseResult ( + execParserPure defaultPrefs app [ "-s" + , "/Users/shterrett/iTunesMusic" + , "-t" + , "/Users/shterrett/music" + , "-a" + , "composer,album,artist" + ]) + `shouldBe` (Just $ Args "/Users/shterrett/iTunesMusic" + "/Users/shterrett/music" + [Composer, Album, Artist]) + it "parses properly supplied long arguments" $ do + getParseResult ( + execParserPure defaultPrefs app [ "--source" + , "/Users/shterrett/iTunesMusic" + , "--target" + , "/Users/shterrett/music" + , "--attributes" + , "composer,album,artist" + ]) + `shouldBe` (Just $ Args "/Users/shterrett/iTunesMusic" + "/Users/shterrett/music" + [Composer, Album, Artist]) diff --git a/test/Data/String/StripSpec.hs b/test/Data/String/StripSpec.hs deleted file mode 100644 index 3ecb689..0000000 --- a/test/Data/String/StripSpec.hs +++ /dev/null @@ -1,19 +0,0 @@ -module Data.String.StripSpec (main, spec) where - -import Test.Hspec -import Test.QuickCheck - -import Data.String.Strip - --- `main` is here so that this module can be run from GHCi on its own. It is --- not needed for automatic spec discovery. -main :: IO () -main = hspec spec - -spec :: Spec -spec = do - describe "strip" $ do - it "removes leading and trailing whitespace" $ do - strip "\t foo bar\n" `shouldBe` "foo bar" - it "is idempotent" $ property $ - \str -> strip str === strip (strip str)