content: {-
    Copyright (c) Meta Platforms, Inc. and affiliates.
    All rights reserved.
  
    This source code is licensed under the BSD-style license found in the
    LICENSE file in the root directory of this source tree.
  -}
  
  -- (c) The University of Glasgow 2006
  
  module Util.String
    ( capitalize
    , decapitalize
    , toArgs
    , strip
    ) where
  
  import Data.Char (isSpace, toUpper, toLower)
  import Control.Applicative
  
  -- | For processing a string representing a list of arguments into a list of
  -- strings, handling surrounding quotes, brackets and spaces. From GHC's Util
  -- module. Reproduced here so it can be used without a dependency on GHC.
  toArgs :: String -> Either String   -- Error
                             [String] -- Args
  toArgs str
      = case dropWhile isSpace str of
        s@('[':_) -> case reads s of
                     [(args, spaces)]
                      | all isSpace spaces ->
                         Right args
                     _ ->
                         Left ("Couldn't read " ++ show str ++ " as [String]")
        s -> toArgs' s
   where
    toArgs' :: String -> Either String [String]
    -- Remove outer quotes:
    -- > toArgs' "\"foo\" \"bar baz\""
    -- Right ["foo", "bar baz"]
    --
    -- Keep inner quotes:
    -- > toArgs' "-DFOO=\"bar baz\""
    -- Right ["-DFOO=\"bar baz\""]
    toArgs' s = case dropWhile isSpace s of
                [] -> Right []
                ('"' : _) -> do
                      -- readAsString removes outer quotes
                      (arg, rest) <- readAsString s
                      (arg:) `fmap` toArgs' rest
                s' -> case break (isSpace <||> (== '"')) s' of
                      (argPart1, s''@('"':_)) -> do
                          (argPart2, rest) <- readAsString s''
                          -- show argPart2 to keep inner quotes
                          ((argPart1 ++ show argPart2):) `fmap` toArgs' rest
                      (arg, s'') -> (arg:) `fmap` toArgs' s''
  
    readAsString :: String -> Either String (String, String)
    readAsString s = case reads s of
                  [(arg, rest)]
                      -- rest must either be [] or start with a space
                      | all isSpace (take 1 rest) ->
                      Right (arg, rest)
                  _ ->
                      Left ("Couldn't read " ++ show s ++ " as String")
  
  
  (<||>) :: Applicative f => f Bool -> f Bool -> f Bool
  (<||>) = liftA2 (||)
  infixr 2 <||> -- same as (||)
  
  -- | Strip whitespace from both beginning and end of a string.
  strip :: String -> String
  strip = reverse . dropWhile isSpace . reverse . dropWhile isSpace
  
  -- | Capitalize the first character of a string.
  capitalize :: String -> String
  capitalize (x:xs) = toUpper x : xs
  capitalize _ = []
  
  -- | Decapitalize the first character of a string
  decapitalize :: String -> String
  decapitalize (x:xs) = toLower x : xs
  decapitalize _ = []
  