diff --git a/attoparsec-run.cabal b/attoparsec-run.cabal
--- a/attoparsec-run.cabal
+++ b/attoparsec-run.cabal
@@ -1,7 +1,7 @@
 cabal-version: 3.0
 
 name: attoparsec-run
-version: 0.0.1.0
+version: 0.0.2.0
 synopsis: Conveniently run Attoparsec parsers
 category: Parsing
 
@@ -29,17 +29,32 @@
     type: git
     location: git://github.com/typeclasses/attoparsec-run.git
 
-library
+common base
     default-language: Haskell2010
+    default-extensions: FlexibleContexts
     ghc-options: -Wall
-    hs-source-dirs: library
     build-depends:
       , attoparsec ^>= 0.14.4
-      , base ^>= 4.14 || ^>= 4.15 || ^>= 4.16 || ^>= 4.17
+      , base ^>= 4.14 || ^>= 4.15 || ^>= 4.16 || ^>= 4.17 || ^>= 4.18
       , bytestring ^>= 0.10.12 || ^>= 0.11
+      , mtl ^>= 2.2.2 || ^>= 2.3
       , text ^>= 1.2.4 || ^>= 2.0
+
+library
+    import: base
+    hs-source-dirs: library
     exposed-modules:
         Data.Attoparsec.Run
 
         Data.Attoparsec.ByteString.Run
         Data.Attoparsec.Text.Run
+
+test-suite test-attoparsec-run
+    import: base
+    default-extensions: OverloadedStrings
+    hs-source-dirs: test
+    type: exitcode-stdio-1.0
+    main-is: Main.hs
+    build-depends:
+        attoparsec-run
+      , hspec ^>= 2.8.5 || ^>= 2.9 || ^>= 2.10
diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,3 +1,25 @@
+### 0.0.2.0 (2023-04-10)
+
+New in `Data.Attoparsec.Run`:
+
+```haskell
+inputState :: (Monoid i, MonadState [i] m) => RestorableInput m i
+```
+
+New in `Data.Attoparsec.ByteString.run`:
+
+```haskell
+parseOnlyStrict :: ByteString -> Parser a -> Either ParseError a
+parseOnlyLazy :: Lazy.ByteString -> Parser a -> Either ParseError a
+```
+
+New in `Data.Attoparsec.Text.run`:
+
+```haskell
+parseOnlyStrict :: Text -> Parser a -> Either ParseError a
+parseOnlyLazy :: Lazy.Text -> Parser a -> Either ParseError a
+```
+
 ### 0.0.1.0 (2023-01-09)
 
 New function:
diff --git a/library/Data/Attoparsec/ByteString/Run.hs b/library/Data/Attoparsec/ByteString/Run.hs
--- a/library/Data/Attoparsec/ByteString/Run.hs
+++ b/library/Data/Attoparsec/ByteString/Run.hs
@@ -1,6 +1,6 @@
 module Data.Attoparsec.ByteString.Run
   (
-    parseStream, parseAndRestore,
+    parseStream, parseAndRestore, parseOnlyStrict, parseOnlyLazy,
     module Data.Attoparsec.Run,
   )
   where
@@ -8,10 +8,17 @@
 import Data.Attoparsec.Run
 
 import Control.Monad (unless)
+import Control.Monad.State (evalState)
 import Data.Attoparsec.ByteString (parseWith, Parser)
-import Data.ByteString (ByteString)
-import Data.ByteString (null)
+import Data.ByteString (ByteString, null)
+import qualified Data.ByteString.Lazy as Lazy
 import Prelude (($), (<$>), pure, mempty, Monad, Either)
+
+parseOnlyStrict :: ByteString -> Parser a -> Either ParseError a
+parseOnlyStrict x p = evalState (parseAndRestore inputState p) [x]
+
+parseOnlyLazy :: Lazy.ByteString -> Parser a -> Either ParseError a
+parseOnlyLazy x p = evalState (parseAndRestore inputState p) (Lazy.toChunks x)
 
 parseStream :: Monad m =>
     BufferedInput m ByteString -> Parser a -> m (FinalResult ByteString a)
diff --git a/library/Data/Attoparsec/Run.hs b/library/Data/Attoparsec/Run.hs
--- a/library/Data/Attoparsec/Run.hs
+++ b/library/Data/Attoparsec/Run.hs
@@ -4,8 +4,10 @@
 
 import Data.IORef (newIORef, readIORef, writeIORef)
 import Data.List (intercalate)
-import Prelude (Either (..), Eq, Ord, Show, String, IO,
+import Prelude (Either (..), Eq, Ord, Show, String, IO, Monoid, mempty,
           pure, error, otherwise, null, ($), ($!), (++))
+import Control.Monad.State (MonadState)
+import qualified Control.Monad.State as State
 
 data FinalResult i a = FinalResult
     i -- ^ Remaining unparsed input
@@ -76,3 +78,18 @@
             (x : xs) -> do
                 writeIORef buffer $! xs
                 pure x
+
+{-| A 'RestorableInput' in which getting and restoring are both backed
+    by 'MonadState' operations. -}
+inputState :: (Monoid i, MonadState [i] m) => RestorableInput m i
+inputState = RestorableInput get restore
+  where
+    get = do
+        xs <- State.get
+        case xs of
+            [] -> pure mempty
+            (x : xs') -> do
+                State.put xs'
+                pure x
+
+    restore x = State.modify' (x :)
diff --git a/library/Data/Attoparsec/Text/Run.hs b/library/Data/Attoparsec/Text/Run.hs
--- a/library/Data/Attoparsec/Text/Run.hs
+++ b/library/Data/Attoparsec/Text/Run.hs
@@ -1,6 +1,6 @@
 module Data.Attoparsec.Text.Run
   (
-    parseStream, parseAndRestore,
+    parseStream, parseAndRestore, parseOnlyStrict, parseOnlyLazy,
     module Data.Attoparsec.Run,
   )
   where
@@ -8,10 +8,17 @@
 import Data.Attoparsec.Run
 
 import Control.Monad (unless)
+import Control.Monad.State (evalState)
 import Data.Attoparsec.Text (parseWith, Parser)
-import Data.Text (Text)
-import Data.Text (null)
+import Data.Text (Text, null)
+import qualified Data.Text.Lazy as Lazy
 import Prelude (($), (<$>), pure, mempty, Monad, Either)
+
+parseOnlyStrict :: Text -> Parser a -> Either ParseError a
+parseOnlyStrict x p = evalState (parseAndRestore inputState p) [x]
+
+parseOnlyLazy :: Lazy.Text -> Parser a -> Either ParseError a
+parseOnlyLazy x p = evalState (parseAndRestore inputState p) (Lazy.toChunks x)
 
 parseStream :: Monad m =>
     BufferedInput m Text -> Parser a -> m (FinalResult Text a)
diff --git a/test/Main.hs b/test/Main.hs
new file mode 100644
--- /dev/null
+++ b/test/Main.hs
@@ -0,0 +1,56 @@
+module Main (main) where
+
+import Test.Hspec
+
+import Control.Applicative (liftA2)
+import Data.Attoparsec.Combinator ((<?>))
+import Data.Attoparsec.Run (ParseError (ParseError))
+import qualified Data.ByteString.Lazy as BS.Lazy
+import qualified Data.Text.Lazy as Text.Lazy
+import qualified Data.Attoparsec.ByteString as P.BS
+import qualified Data.Attoparsec.ByteString.Run as Run.BS
+import qualified Data.Attoparsec.Text as P.Text
+import qualified Data.Attoparsec.Text.Run as Run.Text
+
+main :: IO ()
+main = hspec $ do
+
+    describe "ByteString" $ do
+
+        describe "parseOnlyStrict" $ do
+
+            specify "success" $
+                Run.BS.parseOnlyStrict "abc" (P.BS.anyWord8 <?> "1")
+                    `shouldBe` Right 97
+
+            specify "failure" $
+                Run.BS.parseOnlyStrict ("abc") (P.BS.satisfy (< 97) <?> "1")
+                    `shouldBe` Left (ParseError ["1"] "Failed reading: satisfy")
+
+        describe "parseOnlyLazy" $ do
+
+            specify "two chunks" $
+                Run.BS.parseOnlyLazy
+                    (BS.Lazy.fromChunks ["a", "b"])
+                    (liftA2 (,) P.BS.anyWord8 P.BS.anyWord8)
+                    `shouldBe` Right (97, 98)
+
+    describe "Text" $ do
+
+        describe "parseOnlyStrict" $ do
+
+            specify "success" $
+                Run.Text.parseOnlyStrict "abc" P.Text.anyChar
+                    `shouldBe` Right 'a'
+
+            specify "failure" $
+                Run.Text.parseOnlyStrict ("abc") (P.Text.satisfy (< 'a') <?> "1")
+                    `shouldBe` Left (ParseError ["1"] "Failed reading: satisfy")
+
+        describe "parseOnlyLazy" $ do
+
+            specify "two chunks" $
+                Run.Text.parseOnlyLazy
+                    (Text.Lazy.fromChunks ["a", "b"])
+                    (liftA2 (,) P.Text.anyChar P.Text.anyChar)
+                    `shouldBe` Right ('a', 'b')
