Fingerprint the 'Buuka' and use it as context

This commit is contained in:
Mats Rauhala 2021-01-01 09:04:48 +02:00
parent ec5576213f
commit 29b71fc216
4 changed files with 35 additions and 14 deletions

View File

@ -55,6 +55,7 @@ library
, hashids , hashids
, text , text
, lens , lens
, hashable
hs-source-dirs: src hs-source-dirs: src
executable buuka executable buuka

View File

@ -1,5 +1,5 @@
{ mkDerivation, aeson, base, bytestring, containers, exceptions { mkDerivation, aeson, base, bytestring, containers, exceptions
, filepath, hashids, hedgehog, hedgehog-corpus, lens, mtl , filepath, hashable, hashids, hedgehog, hedgehog-corpus, lens, mtl
, optparse-applicative, stdenv, tasty, tasty-hedgehog, text , optparse-applicative, stdenv, tasty, tasty-hedgehog, text
, transformers, unliftio, vector, yaml , transformers, unliftio, vector, yaml
}: }:
@ -10,8 +10,8 @@ mkDerivation {
isLibrary = true; isLibrary = true;
isExecutable = true; isExecutable = true;
libraryHaskellDepends = [ libraryHaskellDepends = [
aeson base bytestring containers exceptions filepath hashids lens aeson base bytestring containers exceptions filepath hashable
mtl text transformers unliftio vector yaml hashids lens mtl text transformers unliftio vector yaml
]; ];
executableHaskellDepends = [ base optparse-applicative unliftio ]; executableHaskellDepends = [ base optparse-applicative unliftio ];
testHaskellDepends = [ testHaskellDepends = [

View File

@ -9,35 +9,44 @@ module Data.Buuka
, insert , insert
, elements , elements
, fingerprint
) )
where where
import Database.Migrations import Database.Migrations
import Data.Aeson import Data.Aeson
import Data.Bits
(finiteBitSize, shiftR, (.&.))
import Data.Hashable
(Hashable, hash)
import GHC.Generics import GHC.Generics
(Generic) (Generic)
import Control.Monad.Reader import Control.Monad.Reader
import Control.Monad.State import Control.Monad.State
import Data.ByteString
(ByteString)
import qualified Data.ByteString as B
newtype URL = URL String newtype URL = URL String
deriving stock (Show, Eq, Generic, Ord) deriving stock (Show, Eq, Generic, Ord)
deriving newtype (ToJSON, FromJSON, FromJSONKey, ToJSONKey) deriving newtype (ToJSON, FromJSON, FromJSONKey, ToJSONKey, Hashable)
data BuukaEntry data BuukaEntry
= BuukaEntry { url :: URL = BuukaEntry { url :: URL
, title :: Maybe String , title :: Maybe String
} }
deriving stock (Show, Eq, Generic) deriving stock (Show, Eq, Generic)
deriving anyclass (ToJSON, FromJSON) deriving anyclass (ToJSON, FromJSON, Hashable)
instance SafeJSON BuukaEntry where instance SafeJSON BuukaEntry where
type Version BuukaEntry = 0 type Version BuukaEntry = 0
newtype Buuka = Buuka [BuukaEntry] newtype Buuka = Buuka [BuukaEntry]
deriving stock (Show, Eq) deriving stock (Show, Eq)
deriving newtype (Semigroup, Monoid, FromJSON, ToJSON) deriving newtype (Semigroup, Monoid, FromJSON, ToJSON, Hashable)
insert :: BuukaEntry -> Buuka -> Buuka insert :: BuukaEntry -> Buuka -> Buuka
insert e (Buuka b) = Buuka (e : b) insert e (Buuka b) = Buuka (e : b)
@ -45,6 +54,14 @@ insert e (Buuka b) = Buuka (e : b)
elements :: Buuka -> [BuukaEntry] elements :: Buuka -> [BuukaEntry]
elements (Buuka b) = b elements (Buuka b) = b
-- | Create a (non-cryptographic) hash out of the 'Buuka'
fingerprint :: Buuka -> ByteString
fingerprint = toBS . hash
where
toBS x =
let bs = finiteBitSize x
in B.pack [fromIntegral ((x `shiftR` s) .&. 255) | s <- [0..bs - 1]]
instance SafeJSON Buuka where instance SafeJSON Buuka where
type Version Buuka = 0 type Version Buuka = 0

View File

@ -12,7 +12,7 @@ import Data.Semigroup
(Max(..)) (Max(..))
import Data.Buuka import Data.Buuka
(BuukaEntry(..), URL(..)) (Buuka, BuukaEntry(..), URL(..))
import qualified Data.Buuka as B import qualified Data.Buuka as B
import Web.Hashids import Web.Hashids
@ -23,15 +23,18 @@ import Data.Text.Strict.Lens
list :: BuukaM () list :: BuukaM ()
list = list =
buukaQ (asks (format . B.elements)) >>= traverse_ (liftIO . putStrLn) buukaQ (asks go) >>= traverse_ (liftIO . putStrLn)
where where
ctx = hashidsSimple "buuka" go :: Buuka -> [String]
format :: [BuukaEntry] -> [String] go b =
format xs = let ctx = hashidsSimple (B.fingerprint b)
let formatted = zipWith formatEntry [1..] xs in format ctx (B.elements b)
format :: HashidsContext -> [BuukaEntry] -> [String]
format ctx xs =
let formatted = zipWith (formatEntry ctx) [1..] xs
indexWidth = getMax . foldMap (Max . length . fst) $ formatted indexWidth = getMax . foldMap (Max . length . fst) $ formatted
in fmap (\(idx,x) -> idx <> replicate (indexWidth - length idx) ' ' <> ". " <> x) formatted in fmap (\(idx,x) -> idx <> replicate (indexWidth - length idx) ' ' <> ". " <> x) formatted
formatEntry :: Int -> BuukaEntry -> (String, String) formatEntry :: HashidsContext -> Int -> BuukaEntry -> (String, String)
formatEntry n = \case formatEntry ctx n = \case
BuukaEntry{title=Just t} -> (encode ctx n ^. utf8 . unpacked, t) BuukaEntry{title=Just t} -> (encode ctx n ^. utf8 . unpacked, t)
BuukaEntry{url=URL u} -> (encode ctx n ^. utf8 . unpacked, u) BuukaEntry{url=URL u} -> (encode ctx n ^. utf8 . unpacked, u)