-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Shell programming, Haskell-style -- -- turtle is a reimplementation of the Unix command line -- environment in Haskell so that you can use Haskell as both a shell and -- a scripting language. -- -- Features include: -- --
-- select :: [a] -> Shell a -- liftIO :: IO a -> Shell a -- using :: Managed a -> Shell a ---- -- Those three embeddings obey these laws: -- --
-- do { x <- select m; select (f x) } = select (do { x <- m; f x })
-- do { x <- liftIO m; liftIO (f x) } = liftIO (do { x <- m; f x })
-- do { x <- with m; using (f x) } = using (do { x <- m; f x })
--
-- select (return x) = return x
-- liftIO (return x) = return x
-- using (return x) = return x
--
--
-- ... and select obeys these additional laws:
--
-- -- select xs <|> select ys = select (xs <|> ys) -- select empty = empty ---- -- You typically won't build Shells using the Shell -- constructor. Instead, use these functions to generate primitive -- Shells: -- --
-- -- For every shell `s`: -- _foldIO s (FoldM step begin done) = do -- x <- begin -- x' <- _foldIO s (FoldM step (return x) return) -- done x' ---- -- ... which is a fancy way of saying that your Shell must call -- 'begin' exactly once when it begins and call 'done' -- exactly once when it ends. module Turtle.Shell -- | A (Shell a) is a protected stream of a's with side -- effects newtype Shell a Shell :: (forall r. FoldM IO a r -> IO r) -> Shell a [_foldIO] :: Shell a -> forall r. FoldM IO a r -> IO r -- | Use a FoldM IO to reduce the stream of -- a's produced by a Shell foldIO :: MonadIO io => Shell a -> FoldM IO a r -> io r -- | Use a Fold to reduce the stream of a's produced by a -- Shell fold :: MonadIO io => Shell a -> Fold a b -> io b -- | Run a Shell to completion, discarding any unused values sh :: MonadIO io => Shell a -> io () -- | Run a Shell to completion, printing any unused values view :: (MonadIO io, Show a) => Shell a -> io () -- | Convert a list to a Shell that emits each element of the list select :: [a] -> Shell a -- | Lift a computation from the IO monad. liftIO :: MonadIO m => forall a. IO a -> m a -- | Acquire a Managed resource within a Shell in an -- exception-safe way using :: Managed a -> Shell a instance GHC.Base.Functor Turtle.Shell.Shell instance GHC.Base.Applicative Turtle.Shell.Shell instance GHC.Base.Monad Turtle.Shell.Shell instance GHC.Base.Alternative Turtle.Shell.Shell instance GHC.Base.MonadPlus Turtle.Shell.Shell instance Control.Monad.IO.Class.MonadIO Turtle.Shell.Shell instance GHC.Base.Monoid a => GHC.Base.Monoid (Turtle.Shell.Shell a) instance GHC.Base.Monoid a => GHC.Num.Num (Turtle.Shell.Shell a) instance Data.String.IsString a => Data.String.IsString (Turtle.Shell.Shell a) -- | Example usage of this module: -- --
-- -- options.hs
--
-- {-# LANGUAGE OverloadedStrings #-}
--
-- import Turtle
--
-- parser :: Parser (Text, Int)
-- parser = (,) <$> optText "name" 'n' "Your first name"
-- <*> optInt "age" 'a' "Your current age"
--
-- main = do
-- (name, age) <- options "Greeting script" parser
-- echo (format ("Hello there, "%s) name)
-- echo (format ("You are "%d%" years old") age)
--
--
-- -- $ ./options --name John --age 42 -- Hello there, John -- You are 42 years old ---- --
-- $ ./options --help -- Greeting script -- -- Usage: options (-n|--name NAME) (-a|--age AGE) -- -- Available options: -- -h,--help Show this help text -- --name NAME Your first name -- --age AGE Your current age ---- -- See the Turtle.Tutorial module which contains more examples on -- how to use command-line parsing. module Turtle.Options -- | A Parser a is an option parser returning a value of type -- a. data Parser a :: * -> * -- | The name of a command-line argument -- -- This is used to infer the long name and metavariable for the command -- line flag. For example, an ArgName of "name" will -- create a --name flag with a NAME metavariable data ArgName -- | The name of a sub-command -- -- This is lower-cased to create a sub-command. For example, a -- CommandName of "Name" will parse name on the -- command line before parsing the remaining arguments using the -- command's subparser. data CommandName -- | The short one-character abbreviation for a flag (i.e. -n) type ShortName = Char -- | A brief description of what your program does -- -- This description will appear in the header of the --help -- output data Description -- | A helpful message explaining what a flag does -- -- This will appear in the --help output data HelpMessage -- | This parser returns True if the given flag is set and -- False if the flag is absent switch :: ArgName -> ShortName -> Optional HelpMessage -> Parser Bool -- | Parse a Text value as a flag-based option optText :: ArgName -> ShortName -> Optional HelpMessage -> Parser Text -- | Parse an Int as a flag-based option optInt :: ArgName -> ShortName -> Optional HelpMessage -> Parser Int -- | Parse an Integer as a flag-based option optInteger :: ArgName -> ShortName -> Optional HelpMessage -> Parser Integer -- | Parse a Double as a flag-based option optDouble :: ArgName -> ShortName -> Optional HelpMessage -> Parser Double -- | Parse a FilePath value as a flag-based option optPath :: ArgName -> ShortName -> Optional HelpMessage -> Parser FilePath -- | Parse any type that implements Read optRead :: Read a => ArgName -> ShortName -> Optional HelpMessage -> Parser a -- | Build a flag-based option parser for any type by providing a -- Text-parsing function opt :: (Text -> Maybe a) -> ArgName -> ShortName -> Optional HelpMessage -> Parser a -- | Parse a Text as a positional argument argText :: ArgName -> Optional HelpMessage -> Parser Text -- | Parse an Int as a positional argument argInt :: ArgName -> Optional HelpMessage -> Parser Int -- | Parse an Integer as a positional argument argInteger :: ArgName -> Optional HelpMessage -> Parser Integer -- | Parse a Double as a positional argument argDouble :: ArgName -> Optional HelpMessage -> Parser Double -- | Parse a FilePath as a positional argument argPath :: ArgName -> Optional HelpMessage -> Parser FilePath -- | Parse any type that implements Read as a positional argument argRead :: Read a => ArgName -> Optional HelpMessage -> Parser a -- | Build a positional argument parser for any type by providing a -- Text-parsing function arg :: (Text -> Maybe a) -> ArgName -> Optional HelpMessage -> Parser a -- | Create a sub-command that parses CommandName and then parses -- the rest of the command-line arguments -- -- The sub-command will have its own Description and help text subcommand :: CommandName -> Description -> Parser a -> Parser a -- | Parse the given options from the command line options :: MonadIO io => Description -> Parser a -> io a instance Data.String.IsString Turtle.Options.HelpMessage instance Data.String.IsString Turtle.Options.Description instance Data.String.IsString Turtle.Options.CommandName instance Data.String.IsString Turtle.Options.ArgName -- | Use this module to either: -- --
-- >>> :set -XOverloadedStrings
--
-- >>> match ("can" <|> "cat") "cat"
-- ["cat"]
--
-- >>> match ("can" <|> "cat") "dog"
-- []
--
-- >>> match (decimal `sepBy` ",") "1,2,3"
-- [[1,2,3]]
--
--
-- This pattern has unlimited backtracking, and will return as many
-- solutions as possible:
--
-- -- >>> match (prefix (star anyChar)) "123" -- ["123","12","1",""] ---- -- Use do notation to structure more complex patterns: -- --
-- >>> :{
-- let bit = ("0" *> pure False) <|> ("1" *> pure True) :: Pattern Bool;
-- portableBitMap = do
-- { "P1"
-- ; width <- spaces1 *> decimal
-- ; height <- spaces1 *> decimal
-- ; count width (count height (spaces1 *> bit))
-- };
-- in match (prefix portableBitMap) "P1\n2 2\n0 0\n1 0\n"
-- :}
-- [[[False,False],[True,False]]]
--
module Turtle.Pattern
-- | A fully backtracking pattern that parses an 'a' from some
-- Text
data Pattern a
-- | Match a Pattern against a Text input, returning all
-- possible solutions
--
-- The Pattern must match the entire Text
match :: Pattern a -> Text -> [a]
-- | Match any character
--
-- -- >>> match anyChar "1" -- "1" -- -- >>> match anyChar "" -- "" --anyChar :: Pattern Char -- | Matches the end of input -- --
-- >>> match eof "1" -- [] -- -- >>> match eof "" -- [()] --eof :: Pattern () -- | Synonym for anyChar dot :: Pattern Char -- | Match any character that satisfies the given predicate -- --
-- >>> match (satisfy (== '1')) "1" -- "1" -- -- >>> match (satisfy (== '2')) "1" -- "" --satisfy :: (Char -> Bool) -> Pattern Char -- | Match a specific character -- --
-- >>> match (char '1') "1" -- "1" -- -- >>> match (char '2') "1" -- "" --char :: Char -> Pattern Char -- | Match any character except the given one -- --
-- >>> match (notChar '2') "1" -- "1" -- -- >>> match (notChar '1') "1" -- "" --notChar :: Char -> Pattern Char -- | Match a specific string -- --
-- >>> match (text "123") "123" -- ["123"] ---- -- You can also omit the text function if you enable the -- OverloadedStrings extension: -- --
-- >>> match "123" "123" -- ["123"] --text :: Text -> Pattern Text -- | Match a specific string in a case-insensitive way -- -- This only handles ASCII strings -- --
-- >>> match (asciiCI "abc") "ABC" -- ["ABC"] --asciiCI :: Text -> Pattern Text -- | Match any one of the given characters -- --
-- >>> match (oneOf "1a") "1" -- "1" -- -- >>> match (oneOf "2a") "1" -- "" --oneOf :: [Char] -> Pattern Char -- | Match anything other than the given characters -- --
-- >>> match (noneOf "2a") "1" -- "1" -- -- >>> match (noneOf "1a") "1" -- "" --noneOf :: [Char] -> Pattern Char -- | Match a whitespace character -- --
-- >>> match space " " -- " " -- -- >>> match space "1" -- "" --space :: Pattern Char -- | Match zero or more whitespace characters -- --
-- >>> match spaces " " -- [" "] -- -- >>> match spaces "" -- [""] --spaces :: Pattern Text -- | Match one or more whitespace characters -- --
-- >>> match spaces1 " " -- [" "] -- -- >>> match spaces1 "" -- [] --spaces1 :: Pattern Text -- | Match the tab character ('t') -- --
-- >>> match tab "\t" -- "\t" -- -- >>> match tab " " -- "" --tab :: Pattern Char -- | Match the newline character ('n') -- --
-- >>> match newline "\n" -- "\n" -- -- >>> match newline " " -- "" --newline :: Pattern Char -- | Matches a carriage return ('r') followed by a newline -- ('n') -- --
-- >>> match crlf "\r\n" -- ["\r\n"] -- -- >>> match crlf "\n\r" -- [] --crlf :: Pattern Text -- | Match an uppercase letter -- --
-- >>> match upper "A" -- "A" -- -- >>> match upper "a" -- "" --upper :: Pattern Char -- | Match a lowercase letter -- --
-- >>> match lower "a" -- "a" -- -- >>> match lower "A" -- "" --lower :: Pattern Char -- | Match a letter or digit -- --
-- >>> match alphaNum "1" -- "1" -- -- >>> match alphaNum "a" -- "a" -- -- >>> match alphaNum "A" -- "A" -- -- >>> match alphaNum "." -- "" --alphaNum :: Pattern Char -- | Match a letter -- --
-- >>> match letter "A" -- "A" -- -- >>> match letter "a" -- "a" -- -- >>> match letter "1" -- "" --letter :: Pattern Char -- | Match a digit -- --
-- >>> match digit "1" -- "1" -- -- >>> match digit "a" -- "" --digit :: Pattern Char -- | Match a hexadecimal digit -- --
-- >>> match hexDigit "1" -- "1" -- -- >>> match hexDigit "A" -- "A" -- -- >>> match hexDigit "a" -- "a" -- -- >>> match hexDigit "g" -- "" --hexDigit :: Pattern Char -- | Match an octal digit -- --
-- >>> match octDigit "1" -- "1" -- -- >>> match octDigit "9" -- "" --octDigit :: Pattern Char -- | Match an unsigned decimal number -- --
-- >>> match decimal "123" -- [123] -- -- >>> match decimal "-123" -- [] --decimal :: Num n => Pattern n -- | Transform a numeric parser to accept an optional leading '+' -- or '-' sign -- --
-- >>> match (signed decimal) "+123" -- [123] -- -- >>> match (signed decimal) "-123" -- [-123] -- -- >>> match (signed decimal) "123" -- [123] --signed :: Num a => Pattern a -> Pattern a -- | Use this to match the prefix of a string -- --
-- >>> match "A" "ABC" -- [] -- -- >>> match (prefix "A") "ABC" -- ["A"] --prefix :: Pattern a -> Pattern a -- | Use this to match the suffix of a string -- --
-- >>> match "C" "ABC" -- [] -- -- >>> match (suffix "C") "ABC" -- ["C"] --suffix :: Pattern a -> Pattern a -- | Use this to match the interior of a string -- --
-- >>> match "B" "ABC" -- [] -- -- >>> match (has "B") "ABC" -- ["B"] --has :: Pattern a -> Pattern a -- | Match the entire string if it begins with the given pattern -- -- This returns the entire string, not just the matched prefix -- --
-- >>> match (begins "A" ) "ABC"
-- ["ABC"]
--
-- >>> match (begins ("A" *> pure "1")) "ABC"
-- ["1BC"]
--
begins :: Pattern Text -> Pattern Text
-- | Match the entire string if it ends with the given pattern
--
-- This returns the entire string, not just the matched prefix
--
--
-- >>> match (ends "C" ) "ABC"
-- ["ABC"]
--
-- >>> match (ends ("C" *> pure "1")) "ABC"
-- ["AB1"]
--
ends :: Pattern Text -> Pattern Text
-- | Match the entire string if it contains the given pattern
--
-- This returns the entire string, not just the interior pattern
--
--
-- >>> match (contains "B" ) "ABC"
-- ["ABC"]
--
-- >>> match (contains ("B" *> pure "1")) "ABC"
-- ["A1C"]
--
contains :: Pattern Text -> Pattern Text
-- | (invert p) succeeds if p fails and fails if
-- p succeeds
--
-- -- >>> match (invert "A") "A" -- [] -- -- >>> match (invert "A") "B" -- [()] --invert :: Pattern a -> Pattern () -- | Match a Char, but return Text -- --
-- >>> match (once (char '1')) "1" -- ["1"] -- -- >>> match (once (char '1')) "" -- [] --once :: Pattern Char -> Pattern Text -- | Parse 0 or more occurrences of the given character -- --
-- >>> match (star anyChar) "123" -- ["123"] -- -- >>> match (star anyChar) "" -- [""] ---- -- See also: chars star :: Pattern Char -> Pattern Text -- | Parse 1 or more occurrences of the given character -- --
-- >>> match (plus digit) "123" -- ["123"] -- -- >>> match (plus digit) "" -- [] ---- -- See also: chars1 plus :: Pattern Char -> Pattern Text -- | Patterns that match multiple times are greedy by default, meaning that -- they try to match as many times as possible. The selfless -- combinator makes a pattern match as few times as possible -- -- This only changes the order in which solutions are returned, by -- prioritizing less greedy solutions -- --
-- >>> match (prefix (selfless (some anyChar))) "123" -- ["1","12","123"] -- -- >>> match (prefix (some anyChar) ) "123" -- ["123","12","1"] --selfless :: Pattern a -> Pattern a -- | Apply the patterns in the list in order, until one of them succeeds -- --
-- >>> match (choice ["cat", "dog", "egg"]) "egg" -- ["egg"] -- -- >>> match (choice ["cat", "dog", "egg"]) "cat" -- ["cat"] -- -- >>> match (choice ["cat", "dog", "egg"]) "fan" -- [] --choice :: [Pattern a] -> Pattern a -- | Apply the given pattern a fixed number of times, collecting the -- results -- --
-- >>> match (count 3 anyChar) "123" -- ["123"] -- -- >>> match (count 4 anyChar) "123" -- [] --count :: Int -> Pattern a -> Pattern [a] -- | Apply the given pattern at least the given number of times, collecting -- the results -- --
-- >>> match (lowerBounded 5 dot) "123" -- [] -- -- >>> match (lowerBounded 2 dot) "123" -- ["123"] --lowerBounded :: Int -> Pattern a -> Pattern [a] -- | Apply the given pattern 0 or more times, up to a given bound, -- collecting the results -- --
-- >>> match (upperBounded 5 dot) "123"
-- ["123"]
--
-- >>> match (upperBounded 2 dot) "123"
-- []
--
-- >>> match ((,) <$> upperBounded 2 dot <*> chars) "123"
-- [("12","3"),("1","23")]
--
upperBounded :: Int -> Pattern a -> Pattern [a]
-- | Apply the given pattern a number of times restricted by given lower
-- and upper bounds, collecting the results
--
-- -- >>> match (bounded 2 5 "cat") "catcatcat" -- [["cat","cat","cat"]] -- -- >>> match (bounded 2 5 "cat") "cat" -- [] -- -- >>> match (bounded 2 5 "cat") "catcatcatcatcatcat" -- [] ---- -- bounded could be implemented naively as follows: -- --
-- bounded m n p = do -- x <- choice (map pure [m..n]) -- count x p --bounded :: Int -> Int -> Pattern a -> Pattern [a] -- | Transform a parser to a succeed with an empty value instead of failing -- -- See also: optional -- --
-- >>> match (option "1" <> "2") "12" -- ["12"] -- -- >>> match (option "1" <> "2") "2" -- ["2"] --option :: Monoid a => Pattern a -> Pattern a -- | (between open close p) matches 'p' in between -- 'open' and 'close' -- --
-- >>> match (between (char '(') (char ')') (star anyChar)) "(123)"
-- ["123"]
--
-- >>> match (between (char '(') (char ')') (star anyChar)) "(123"
-- []
--
between :: Pattern a -> Pattern b -> Pattern c -> Pattern c
-- | Discard the pattern's result
--
-- -- >>> match (skip anyChar) "1" -- [()] -- -- >>> match (skip anyChar) "" -- [] --skip :: Pattern a -> Pattern () -- | Restrict the pattern to consume no more than the given number of -- characters -- --
-- >>> match (within 2 decimal) "12" -- [12] -- -- >>> match (within 2 decimal) "1" -- [1] -- -- >>> match (within 2 decimal) "123" -- [] --within :: Int -> Pattern a -> Pattern a -- | Require the pattern to consume exactly the given number of characters -- --
-- >>> match (fixed 2 decimal) "12" -- [12] -- -- >>> match (fixed 2 decimal) "1" -- [] --fixed :: Int -> Pattern a -> Pattern a -- | p sepBy sep matches zero or more occurrences of -- p separated by sep -- --
-- >>> match (decimal `sepBy` char ',') "1,2,3" -- [[1,2,3]] -- -- >>> match (decimal `sepBy` char ',') "" -- [[]] --sepBy :: Pattern a -> Pattern b -> Pattern [a] -- | p sepBy1 sep matches one or more occurrences of -- p separated by sep -- --
-- >>> match (decimal `sepBy1` ",") "1,2,3" -- [[1,2,3]] -- -- >>> match (decimal `sepBy1` ",") "" -- [] --sepBy1 :: Pattern a -> Pattern b -> Pattern [a] -- | Like star dot or star anyChar, except more efficient chars :: Pattern Text -- | Like plus dot or plus anyChar, except more efficient chars1 :: Pattern Text instance GHC.Base.MonadPlus Turtle.Pattern.Pattern instance GHC.Base.Alternative Turtle.Pattern.Pattern instance GHC.Base.Monad Turtle.Pattern.Pattern instance GHC.Base.Applicative Turtle.Pattern.Pattern instance GHC.Base.Functor Turtle.Pattern.Pattern instance GHC.Base.Monoid a => GHC.Base.Monoid (Turtle.Pattern.Pattern a) instance GHC.Base.Monoid a => GHC.Num.Num (Turtle.Pattern.Pattern a) instance (a ~ Data.Text.Internal.Text) => Data.String.IsString (Turtle.Pattern.Pattern a) -- | Minimalist implementation of type-safe formatted strings, borrowing -- heavily from the implementation of the formatting package. -- -- Example use of this module: -- --
-- >>> :set -XOverloadedStrings
--
-- >>> import Turtle.Format
--
-- >>> format ("This is a "%s%" string that takes "%d%" arguments") "format" 2
-- "This is a format string that takes 2 arguments"
--
--
-- A Format string that takes no arguments has this type:
--
-- -- "I take 0 arguments" :: Format r r -- -- format "I take 0 arguments" :: Text ---- --
-- >>> format "I take 0 arguments" -- "I take 0 arguments" ---- -- A Format string that takes one argument has this type: -- --
-- "I take "%d%" arguments" :: Format r (Int -> r)
--
-- format ("I take "%d%" argument") :: Int -> Text
--
--
--
-- >>> format ("I take "%d%" argument") 1
-- "I take 1 argument"
--
--
-- A Format string that takes two arguments has this type:
--
--
-- "I "%s%" "%d%" arguments" :: Format r (Text -> Int -> r)
--
-- format ("I "%s%" "%d%" arguments") :: Text -> Int -> Text
--
--
--
-- >>> format ("I "%s%" "%d%" arguments") "take" 2
-- "I take 2 arguments"
--
module Turtle.Format
-- | A Format string
data Format a b
-- | Concatenate two Format strings
(%) :: Format b c -> Format a b -> Format a c
-- | Convert a Format string to a print function that takes zero or
-- more typed arguments and returns a Text string
format :: Format Text r -> r
-- | Create your own format specifier
makeFormat :: (a -> Text) -> Format r (a -> r)
-- | Format any Showable value
--
-- -- >>> format w True -- "True" --w :: Show a => Format r (a -> r) -- | Format an Integral value as a signed decimal -- --
-- >>> format d 25 -- "25" -- -- >>> format d (-25) -- "-25" --d :: Integral n => Format r (n -> r) -- | Format a Word value as an unsigned decimal -- --
-- >>> format u 25 -- "25" --u :: Format r (Word -> r) -- | Format a Word value as an unsigned octal number -- --
-- >>> format o 25 -- "31" --o :: Format r (Word -> r) -- | Format a Word value as an unsigned hexadecimal number -- (without a leading "0x") -- --
-- >>> format x 25 -- "19" --x :: Format r (Word -> r) -- | Format a Double using decimal notation with 6 digits of -- precision -- --
-- >>> format f 25.1 -- "25.100000" --f :: Format r (Double -> r) -- | Format a Double using scientific notation with 6 digits -- of precision -- --
-- >>> format e 25.1 -- "2.510000e1" --e :: Format r (Double -> r) -- | Format a Double using decimal notation for small -- exponents and scientific notation for large exponents -- --
-- >>> format g 25.1 -- "25.100000" -- -- >>> format g 123456789 -- "1.234568e8" -- -- >>> format g 0.00000000001 -- "1.000000e-11" --g :: Format r (Double -> r) -- | Format that inserts Text -- --
-- >>> format s "ABC" -- "ABC" --s :: Format r (Text -> r) -- | Format a FilePath into Text fp :: Format r (FilePath -> r) -- | Convert a Showable value to Text -- -- Short-hand for (format w) -- --
-- >>> repr (1,2) -- "(1,2)" --repr :: Show a => a -> Text instance Control.Category.Category Turtle.Format.Format instance (a ~ b) => Data.String.IsString (Turtle.Format.Format a b) -- | This module provides a large suite of utilities that resemble Unix -- utilities. -- -- Many of these commands are just existing Haskell commands renamed to -- match their Unix counterparts: -- --
-- >>> :set -XOverloadedStrings -- -- >>> cd "/tmp" -- -- >>> pwd -- FilePath "/tmp" ---- -- Some commands are Shells that emit streams of values. -- view prints all values in a Shell stream: -- --
-- >>> view (ls "/usr") -- FilePath "/usr/lib" -- FilePath "/usr/src" -- FilePath "/usr/sbin" -- FilePath "/usr/include" -- FilePath "/usr/share" -- FilePath "/usr/games" -- FilePath "/usr/local" -- FilePath "/usr/bin" -- -- >>> view (find (suffix "Browser.py") "/usr/lib") -- FilePath "/usr/lib/python3.4/idlelib/ClassBrowser.py" -- FilePath "/usr/lib/python3.4/idlelib/RemoteObjectBrowser.py" -- FilePath "/usr/lib/python3.4/idlelib/PathBrowser.py" -- FilePath "/usr/lib/python3.4/idlelib/ObjectBrowser.py" ---- -- Use fold to reduce the output of a Shell stream: -- --
-- >>> import qualified Control.Foldl as Fold -- -- >>> fold (ls "/usr") Fold.length -- 8 -- -- >>> fold (find (suffix "Browser.py") "/usr/lib") Fold.head -- Just (FilePath "/usr/lib/python3.4/idlelib/ClassBrowser.py") ---- -- Create files using output: -- --
-- >>> output "foo.txt" ("123" <|> "456" <|> "ABC")
--
-- >>> realpath "foo.txt"
-- FilePath "/tmp/foo.txt"
--
--
-- Read in files using input:
--
-- -- >>> stdout (input "foo.txt") -- 123 -- 456 -- ABC ---- -- Format strings in a type safe way using format: -- --
-- >>> dir <- pwd
--
-- >>> format ("I am in the "%fp%" directory") dir
-- "I am in the /tmp directory"
--
--
-- Commands like grep, sed and find accept arbitrary
-- Patterns
--
--
-- >>> stdout (grep ("123" <|> "ABC") (input "foo.txt"))
-- 123
-- ABC
--
-- >>> let exclaim = fmap (<> "!") (plus digit)
--
-- >>> stdout (sed exclaim (input "foo.txt"))
-- 123!
-- 456!
-- ABC
--
--
-- Note that grep and find differ from their Unix
-- counterparts by requiring that the Pattern matches the entire
-- line or file name by default. However, you can optionally match the
-- prefix, suffix, or interior of a line:
--
-- -- >>> stdout (grep (has "2") (input "foo.txt")) -- 123 -- -- >>> stdout (grep (prefix "1") (input "foo.txt")) -- 123 -- -- >>> stdout (grep (suffix "3") (input "foo.txt")) -- 123 ---- -- You can also build up more sophisticated Shell programs using -- sh in conjunction with do notation: -- --
-- {-# LANGUAGE OverloadedStrings #-}
--
-- import Turtle
--
-- main = sh example
--
-- example = do
-- -- Read in file names from "files1.txt" and "files2.txt"
-- file <- fmap fromText (input "files1.txt" <|> input "files2.txt")
--
-- -- Stream each file to standard output only if the file exists
-- True <- liftIO (testfile file)
-- line <- input file
-- liftIO (echo line)
--
--
-- See Turtle.Tutorial for an extended tutorial explaining how to
-- use this library in greater detail.
module Turtle.Prelude
-- | Run a command using execvp, retrieving the exit code
--
-- The command inherits stdout and stderr for the
-- current process
proc :: MonadIO io => Text -> [Text] -> Shell Text -> io ExitCode
-- | Run a command line using the shell, retrieving the exit code
--
-- This command is more powerful than proc, but highly vulnerable
-- to code injection if you template the command line with untrusted
-- input
--
-- The command inherits stdout and stderr for the
-- current process
shell :: MonadIO io => Text -> Shell Text -> io ExitCode
-- | Run a command using execvp, retrieving the exit code and
-- stdout as a non-lazy blob of Text
--
-- The command inherits stderr for the current process
procStrict :: MonadIO io => Text -> [Text] -> Shell Text -> io (ExitCode, Text)
-- | Run a command line using the shell, retrieving the exit code and
-- stdout as a non-lazy blob of Text
--
-- This command is more powerful than proc, but highly vulnerable
-- to code injection if you template the command line with untrusted
-- input
--
-- The command inherits stderr for the current process
shellStrict :: MonadIO io => Text -> Shell Text -> io (ExitCode, Text)
-- | Print to stdout
echo :: MonadIO io => Text -> io ()
-- | Print to stderr
err :: MonadIO io => Text -> io ()
-- | Read in a line from stdin
--
-- Returns Nothing if at end of input
readline :: MonadIO io => io (Maybe Text)
-- | Get command line arguments in a list
arguments :: MonadIO io => io [Text]
-- | Set or modify an environment variable
export :: MonadIO io => Text -> Text -> io ()
-- | Delete an environment variable
unset :: MonadIO io => Text -> io ()
-- | Look up an environment variable
need :: MonadIO io => Text -> io (Maybe Text)
-- | Retrieve all environment variables
env :: MonadIO io => io [(Text, Text)]
-- | Change the current directory
cd :: MonadIO io => FilePath -> io ()
-- | Get the current directory
pwd :: MonadIO io => io FilePath
-- | Get the home directory
home :: MonadIO io => io FilePath
-- | Canonicalize a path
realpath :: MonadIO io => FilePath -> io FilePath
-- | Move a file or directory
--
-- Works if the two paths are on the same filesystem. If not, mv
-- will still work when dealing with a regular file, but the operation
-- will not be atomic
mv :: MonadIO io => FilePath -> FilePath -> io ()
-- | Create a directory
--
-- Fails if the directory is present
mkdir :: MonadIO io => FilePath -> io ()
-- | Create a directory tree (equivalent to mkdir -p)
--
-- Does not fail if the directory is present
mktree :: MonadIO io => FilePath -> io ()
-- | Copy a file
cp :: MonadIO io => FilePath -> FilePath -> io ()
-- | Remove a file
rm :: MonadIO io => FilePath -> io ()
-- | Remove a directory
rmdir :: MonadIO io => FilePath -> io ()
-- | Remove a directory tree (equivalent to rm -r)
--
-- Use at your own risk
rmtree :: MonadIO io => FilePath -> io ()
-- | Check if a file exists
testfile :: MonadIO io => FilePath -> io Bool
-- | Check if a directory exists
testdir :: MonadIO io => FilePath -> io Bool
-- | Check if a path exists
testpath :: MonadIO io => FilePath -> io Bool
-- | Get the current time
date :: MonadIO io => io UTCTime
-- | Get the time a file was last modified
datefile :: MonadIO io => FilePath -> io UTCTime
-- | Touch a file, updating the access and modification times to the
-- current time
--
-- Creates an empty file if it does not exist
touch :: MonadIO io => FilePath -> io ()
-- | Time how long a command takes in monotonic wall clock time
--
-- Returns the duration alongside the return value
time :: MonadIO io => io a -> io (a, NominalDiffTime)
-- | Get the system's host name
hostname :: MonadIO io => io Text
-- | Sleep for the given duration
--
-- A numeric literal argument is interpreted as seconds. In other words,
-- (sleep 2.0) will sleep for two seconds.
sleep :: MonadIO io => NominalDiffTime -> io ()
-- | Exit with the given exit code
--
-- An exit code of 0 indicates success
exit :: MonadIO io => ExitCode -> io a
-- | Throw an exception using the provided Text message
die :: MonadIO io => Text -> io a
-- | Analogous to && in Bash
--
-- Runs the second command only if the first one returns
-- ExitSuccess
(.&&.) :: Monad m => m ExitCode -> m ExitCode -> m ExitCode
-- | Analogous to || in Bash
--
-- Run the second command only if the first one returns
-- ExitFailure
(.||.) :: Monad m => m ExitCode -> m ExitCode -> m ExitCode
-- | Acquire a Managed read-only Handle from a
-- FilePath
readonly :: FilePath -> Managed Handle
-- | Acquire a Managed write-only Handle from a
-- FilePath
writeonly :: FilePath -> Managed Handle
-- | Acquire a Managed append-only Handle from a
-- FilePath
appendonly :: FilePath -> Managed Handle
-- | Create a temporary file underneath the given directory
--
-- Deletes the temporary file when done
--
-- Note that this provides the Handle of the file in order to
-- avoid a potential race condition from the file being moved or deleted
-- before you have a chance to open the file. The mktempfile
-- function provides a simpler API if you don't need to worry about that
-- possibility.
mktemp :: FilePath -> Text -> Managed (FilePath, Handle)
-- | Create a temporary file underneath the given directory
--
-- Deletes the temporary file when done
mktempfile :: FilePath -> Text -> Managed FilePath
-- | Create a temporary directory underneath the given directory
--
-- Deletes the temporary directory when done
mktempdir :: FilePath -> Text -> Managed FilePath
-- | Fork a thread, acquiring an Async value
fork :: IO a -> Managed (Async a)
-- | Wait for an asynchronous action to complete, and return its value. If
-- the asynchronous action threw an exception, then the exception is
-- re-thrown by wait.
--
-- -- wait = atomically . waitSTM --wait :: Async a -> IO a -- | Run a command using execvp, streaming stdout as -- lines of Text -- -- The command inherits stderr for the current process inproc :: Text -> [Text] -> Shell Text -> Shell Text -- | Run a command line using the shell, streaming stdout as lines -- of Text -- -- This command is more powerful than inproc, but highly -- vulnerable to code injection if you template the command line with -- untrusted input -- -- The command inherits stderr for the current process inshell :: Text -> Shell Text -> Shell Text -- | Run a command using the shell, streaming stdout and -- stderr as lines of Text. Lines from stdout -- are wrapped in Right and lines from stderr are wrapped -- in Left. inprocWithErr :: Text -> [Text] -> Shell Text -> Shell (Either Text Text) -- | Run a command line using the shell, streaming stdout and -- stderr as lines of Text. Lines from stdout -- are wrapped in Right and lines from stderr are wrapped -- in Left. -- -- This command is more powerful than inprocWithErr, but highly -- vulnerable to code injection if you template the command line with -- untrusted input inshellWithErr :: Text -> Shell Text -> Shell (Either Text Text) -- | Read lines of Text from standard input stdin :: Shell Text -- | Read lines of Text from a file input :: FilePath -> Shell Text -- | Read lines of Text from a Handle inhandle :: Handle -> Shell Text -- | Stream lines of Text to standard output stdout :: MonadIO io => Shell Text -> io () -- | Stream lines of Text to a file output :: MonadIO io => FilePath -> Shell Text -> io () -- | Stream lines of Text to a Handle outhandle :: MonadIO io => Handle -> Shell Text -> io () -- | Stream lines of Text to append to a file append :: MonadIO io => FilePath -> Shell Text -> io () -- | Stream lines of Text to standard error stderr :: MonadIO io => Shell Text -> io () -- | Read in a stream's contents strictly strict :: MonadIO io => Shell Text -> io Text -- | Stream all immediate children of the given directory, excluding -- "." and ".." ls :: FilePath -> Shell FilePath -- | Stream all recursive descendents of the given directory -- -- This skips any directories that fail the supplied predicate -- --
-- lstree = lsif (\_ -> return True) --lsif :: (FilePath -> IO Bool) -> FilePath -> Shell FilePath -- | Stream all recursive descendents of the given directory lstree :: FilePath -> Shell FilePath -- | Combine the output of multiple Shells, in order cat :: [Shell a] -> Shell a -- | Keep all lines that match the given Pattern grep :: Pattern a -> Shell Text -> Shell Text -- | Replace all occurrences of a Pattern with its Text -- result -- -- sed performs substitution on a line-by-line basis, meaning that -- substitutions may not span multiple lines. Additionally, substitutions -- may occur multiple times within the same line, like the behavior of -- s....../g. -- -- Warning: Do not use a Pattern that matches the empty string, -- since it will match an infinite number of times. sed tries to -- detect such Patterns and die with an error message if -- they occur, but this detection is necessarily incomplete. sed :: Pattern Text -> Shell Text -> Shell Text -- | Like sed, but operates in place on a FilePath (analogous -- to sed -i) inplace :: MonadIO io => Pattern Text -> FilePath -> io () -- | Search a directory recursively for all files matching the given -- Pattern find :: Pattern a -> FilePath -> Shell FilePath -- | A Stream of "y"s yes :: Shell Text -- | Number each element of a Shell (starting at 0) nl :: Num n => Shell a -> Shell (n, a) -- | Merge two Shells together, element-wise -- -- If one Shell is longer than the other, the excess elements are -- truncated paste :: Shell a -> Shell b -> Shell (a, b) -- | A Shell that endlessly emits () endless :: Shell () -- | Limit a Shell to a fixed number of values limit :: Int -> Shell a -> Shell a -- | Limit a Shell to values that satisfy the predicate -- -- This terminates the stream on the first value that does not satisfy -- the predicate limitWhile :: (a -> Bool) -> Shell a -> Shell a -- | Cache a Shell's output so that repeated runs of the script will -- reuse the result of previous runs. You must supply a FilePath -- where the cached result will be stored. -- -- The stored result is only reused if the Shell successfully ran -- to completion without any exceptions. Note: on some platforms Ctrl-C -- will flush standard input and signal end of file before killing the -- program, which may trick the program into "successfully" completing. cache :: (Read a, Show a) => FilePath -> Shell a -> Shell a -- | Count the number of characters in the stream (like wc -c) -- -- This uses the convention that the elements of the stream are -- implicitly ended by newlines that are one character wide countChars :: Integral n => Fold Text n -- | Count the number of words in the stream (like wc -w) countWords :: Integral n => Fold Text n -- | Count the number of lines in the stream (like wc -l) -- -- This uses the convention that each element of the stream represents -- one line countLines :: Integral n => Fold Text n -- | Split a line into chunks delimited by the given Pattern cut :: Pattern a -> Text -> [Text] data Permissions :: * -- | Update a file or directory's user permissions -- --
-- chmod rwo "foo.txt" -- chmod u=rw foo.txt -- chmod executable "foo.txt" -- chmod u+x foo.txt -- chmod nonwritable "foo.txt" -- chmod u-x foo.txt --chmod :: MonadIO io => (Permissions -> Permissions) -> FilePath -> io Permissions -- | Get a file or directory's user permissions getmod :: MonadIO io => FilePath -> io Permissions -- | Set a file or directory's user permissions setmod :: MonadIO io => Permissions -> FilePath -> io () -- |
-- +r --readable :: Permissions -> Permissions -- |
-- -r --nonreadable :: Permissions -> Permissions -- |
-- +w --writable :: Permissions -> Permissions -- |
-- -w --nonwritable :: Permissions -> Permissions -- |
-- +x --executable :: Permissions -> Permissions -- |
-- -x --nonexecutable :: Permissions -> Permissions -- |
-- +s --searchable :: Permissions -> Permissions -- |
-- -s --nonsearchable :: Permissions -> Permissions -- |
-- -r -w -x --ooo :: Permissions -> Permissions -- |
-- +r -w -x --roo :: Permissions -> Permissions -- |
-- -r +w -x --owo :: Permissions -> Permissions -- |
-- -r -w +x --oox :: Permissions -> Permissions -- |
-- -r -w +s --oos :: Permissions -> Permissions -- |
-- +r +w -x --rwo :: Permissions -> Permissions -- |
-- +r -w +x --rox :: Permissions -> Permissions -- |
-- +r -w +s --ros :: Permissions -> Permissions -- |
-- -r +w +x --owx :: Permissions -> Permissions -- |
-- +r +w +x --rwx :: Permissions -> Permissions -- |
-- +r +w +s --rws :: Permissions -> Permissions -- | Get the size of a file or a directory du :: MonadIO io => FilePath -> io Size -- | An abstract file size -- -- Specify the units you want by using an accessor like kilobytes -- -- The Num instance for Size interprets numeric literals as -- bytes data Size -- | Format a Size using a human readable representation -- --
-- >>> format sz 42 -- "42 B" -- -- >>> format sz 2309 -- "2.309 KB" -- -- >>> format sz 949203 -- "949.203 MB" -- -- >>> format sz 1600000000 -- "1.600 GB" -- -- >>> format sz 999999999999999999 -- "999999.999 TB" --sz :: Format r (Size -> r) -- | Extract a size in bytes bytes :: Integral n => Size -> n -- |
-- 1 kilobyte = 1000 bytes --kilobytes :: Integral n => Size -> n -- |
-- 1 megabyte = 1000 kilobytes --megabytes :: Integral n => Size -> n -- |
-- 1 gigabyte = 1000 megabytes --gigabytes :: Integral n => Size -> n -- |
-- 1 terabyte = 1000 gigabytes --terabytes :: Integral n => Size -> n -- |
-- 1 kibibyte = 1024 bytes --kibibytes :: Integral n => Size -> n -- |
-- 1 mebibyte = 1024 kibibytes --mebibytes :: Integral n => Size -> n -- |
-- 1 gibibyte = 1024 mebibytes --gibibytes :: Integral n => Size -> n -- |
-- 1 tebibyte = 1024 gibibytes --tebibytes :: Integral n => Size -> n instance GHC.Num.Num Turtle.Prelude.Size instance GHC.Show.Show Turtle.Prelude.Size -- | See Turtle.Tutorial to learn how to use this library or -- Turtle.Prelude for a quick-start guide. -- -- Here is the recommended way to import this library: -- --
-- {-# LANGUAGE OverloadedStrings #-}
--
-- import Turtle
-- import Prelude hiding (FilePath)
--
--
-- This module re-exports the rest of the library and also re-exports
-- useful modules from base:
--
-- Turtle.Format provides type-safe string formatting
--
-- Turtle.Pattern provides Patterns, which are like more
-- powerful regular expressions
--
-- Turtle.Shell provides a Shell abstraction for building
-- streaming, exception-safe pipelines
--
-- Turtle.Prelude provides a library of Unix-like utilities to get
-- you started with basic shell-like programming within Haskell
--
-- Control.Applicative provides two classes:
--
-- -- $ stack install turtle ---- -- This tutorial will mostly focus on using Haskell as a scripting -- language. The first two lines of each script below contain boilerplate -- instructions so that stack will load and run the script. This -- helps ensure that a script will run on any computer that has a -- stack executable, as stack can install a Haskell -- compiler if one is not already present. If you are curious about how -- these two lines work, they are described here: -- -- -- https://github.com/commercialhaskell/stack/blob/master/doc/GUIDE.md#ghcrunghc -- -- If you want to make a Windows script independently executable outside -- of a Git Bash environment, you can either (A) compile the script into -- an executable or (B) run these two commands from a cmd shell -- with administrator privileges to make all *.hs scripts -- executable: -- --
-- assoc .hs=Haskell -- ftype Haskell="C:\path\to\stack.exe" "%1" %* --module Turtle.Tutorial