{-# language TypeApplications #-} {-# language DataKinds #-} {-# language LambdaCase #-} {-# options_ghc -Wall -Werror #-} module Main (main) where import GHC.Exts (IsList(..)) import Data.Foldable (traverse_) import System.Exit (ExitCode(ExitSuccess, ExitFailure), exitWith) import Bloom (Bloom) import qualified Bloom import qualified Data.Binary as Binary import qualified Data.ByteString.Lazy as ByteString import System.Directory import System.FilePath import System.Environment import Control.Monad (guard) main :: IO () main = do b <- cachedLoad input <- lines <$> getContents let ws' = filter (not . (`Bloom.elem` b)) input traverse_ putStrLn ws' exitWith (if null ws' then ExitSuccess else ExitFailure 1) cachedLoad :: IO (Bloom Size String) cachedLoad = do cache <- getCachePath load cache >>= \case Nothing -> do b <- build ByteString.writeFile cache $ Binary.encode b pure b Just a -> pure a getCachePath :: IO FilePath getCachePath = ( "bloom-spell/words.bin") <$> getEnv "XDG_DATA_HOME" load :: FilePath -> IO (Maybe (Bloom Size String)) load p = do cacheExists <- doesFileExist p guard cacheExists s <- ByteString.readFile p case Binary.decodeOrFail @(Bloom Size _) s of Left{} -> pure Nothing Right (_, _, a) -> pure $ pure a build :: IO (Bloom Size String) build = fromList @(Bloom Size _) . lines <$> readFile dict type Size = 0xffff0 dict :: FilePath dict = "/usr/share/dict/words"