module Data.NonEmptyText ( NonEmptyText -- Creation , new , singleton , toText , fromText -- Basic interface , cons , snoc , uncons , unsnoc , append , Data.NonEmptyText.head , Data.NonEmptyText.last , Data.NonEmptyText.tail , Data.NonEmptyText.init , Data.NonEmptyText.length , isSingleton ) where import qualified Data.Text as Text data NonEmptyText = NonEmptyText Char Text.Text deriving (Eq, Ord) instance Show NonEmptyText where show = show . toText -- | /O(1)/ Create a new 'NonEmptyText' -- -- >>> new 'h' "ello world" -- "hello world" -- new :: Char -> Text.Text -> NonEmptyText new = NonEmptyText -- | /O(1)/ Convert a character into a 'NonEmptyText'. -- -- >>> singleton 'a' -- "a" -- singleton :: Char -> NonEmptyText singleton = flip NonEmptyText Text.empty -- | /O(1)/ Check if the string is composed of only one character isSingleton :: NonEmptyText -> Bool isSingleton = Text.null . snd . uncons -- | /O(n)/ Prefixes the 'NonEmptyText' with one character cons :: Char -> NonEmptyText -> NonEmptyText cons h t = new h (toText t) -- | /O(n)/ Suffixes the 'NonEmptyText' with one character snoc :: NonEmptyText -> Char -> NonEmptyText snoc (NonEmptyText h t) c = new h (Text.snoc t c) -- | /O(n)/ Appends one 'NonEmptyText' to another -- -- >>> append <$> fromText "hello," <*> fromText " world." -- Just "hello, world." append :: NonEmptyText -> NonEmptyText -> NonEmptyText append (NonEmptyText h t) = new h . Text.append t . toText -- | /O(1)/ Return the first character and the rest of the 'NonEmptyText' uncons :: NonEmptyText -> (Char, Text.Text) uncons (NonEmptyText h t) = (h, t) -- | /O(n)/ Return the beginning of the 'NonEmptyText', and its last character unsnoc :: NonEmptyText -> (Text.Text, Char) unsnoc (NonEmptyText h t) = case unsnocT t of Nothing -> (Text.empty, h) Just (m, e) -> (Text.cons h m, e) where unsnocT :: Text.Text -> Maybe (Text.Text, Char) unsnocT text = -- Some old version of Data.Text don't have unsnoc let n = Text.length text - 1 in if Text.null text then Nothing else Just (Text.take n text, Text.index text n) -- | /O(1)/ Return the first of the 'NonEmptyText' -- -- As opposed to 'Data.Text.head', this is guaranteed to succeed, as the -- the text is never empty. head :: NonEmptyText -> Char head = fst . uncons -- | /O(1)/ Return the last character of the 'NonEmptyText' -- -- This never fails. last :: NonEmptyText -> Char last = snd . unsnoc -- | /O(1)/ Return all characters of the 'NonEmptyText' but the first one tail :: NonEmptyText -> Text.Text tail = snd . uncons -- | /O(n)/ Return all character of the 'NonEmptyText' but the last one init :: NonEmptyText -> Text.Text init = fst . unsnoc -- | /O(n)/ Return the length of the total 'NonEmptyText'. length :: NonEmptyText -> Int length = (1 +) . Text.length . Data.NonEmptyText.tail -- | /O(n)/ Convert to NonEmptyText to Text. -- -- The 'Data.Text.Text' result is guaranteed to be non-empty. However, this is -- not reflected in the type. toText :: NonEmptyText -> Text.Text toText = uncurry Text.cons . uncons -- | /O(n)/ Create a 'NonEmptyText' from 'Data.Text.Text'. -- -- If the original text is empty, this will return 'Data.Maybe.Nothing'. -- -- >>> fromText "hello" -- Just "hello" -- >>> fromText "" -- Nothing fromText :: Text.Text -> Maybe NonEmptyText fromText t = Prelude.uncurry NonEmptyText <$> Text.uncons t