packages feed

pgmq-core (empty) → 0.1.0.0

raw patch · 4 files changed

+161/−0 lines, 4 filesdep +aesondep +basedep +template-haskell

Dependencies added: aeson, base, template-haskell, text, time

Files

+ CHANGELOG.md view
@@ -0,0 +1,8 @@+# Revision history for pgmq-core++## 0.1.0.0 -- 2026-02-21++* Initial release+* Core types: `Message`, `MessageBody`, `MessageHeaders`, `MessageId`, `Queue`, `QueueName`, `PgmqError`+* Queue name validation following pgmq-rs conventions+* Template Haskell `Lift` instance for `QueueName`
+ LICENSE view
@@ -0,0 +1,20 @@+Copyright (c) 2025 Nadeem Bitar++Permission is hereby granted, free of charge, to any person obtaining+a copy of this software and associated documentation files (the+"Software"), to deal in the Software without restriction, including+without limitation the rights to use, copy, modify, merge, publish,+distribute, sublicense, and/or sell copies of the Software, and to+permit persons to whom the Software is furnished to do so, subject to+the following conditions:++The above copyright notice and this permission notice shall be included+in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ pgmq-core.cabal view
@@ -0,0 +1,43 @@+cabal-version:   3.4+name:            pgmq-core+version:         0.1.0.0+synopsis:        Core types for pgmq-hs, a Haskell client for PGMQ+description:+  Core types and type classes for pgmq-hs, a Haskell client library+  for PGMQ (PostgreSQL Message Queue). Provides Message, Queue,+  QueueName, and related types used across pgmq-hs packages.++homepage:        https://github.com/topagentnetwork/pgmq-hs+license:         MIT+license-file:    LICENSE+author:          Nadeem Bitar+maintainer:      nadeem@topagentnetwork.com+category:        Database+build-type:      Simple+extra-doc-files: CHANGELOG.md++common warnings+  ghc-options:+    -Wall -Wcompat -Widentities -Wincomplete-uni-patterns+    -Wincomplete-record-updates -Wredundant-constraints+    -fhide-source-paths -Wmissing-export-lists -Wpartial-fields+    -Wmissing-deriving-strategies++library+  import:             warnings+  exposed-modules:    Pgmq.Types+  default-extensions:+    DeriveGeneric+    GeneralisedNewtypeDeriving+    ImportQualifiedPost+    OverloadedStrings++  build-depends:+    , aeson             ^>=2.2+    , base              >=4.18  && <5+    , template-haskell  >=2.20  && <3+    , text              ^>=2.1+    , time              ^>=1.14++  hs-source-dirs:     src+  default-language:   GHC2024
+ src/Pgmq/Types.hs view
@@ -0,0 +1,90 @@+{-# LANGUAGE TemplateHaskellQuotes #-}++module Pgmq.Types+  ( MessageBody (..),+    MessageHeaders (..),+    MessageId (..),+    Message (..),+    Queue (..),+    QueueName,+    parseQueueName,+    queueNameToText,+    PgmqError (..),+  )+where++import Data.Aeson (FromJSON, ToJSON, Value)+import Data.Char (isAlphaNum, isAscii)+import Data.Int (Int64)+import Data.Text (Text)+import Data.Text qualified as T+import Data.Time (UTCTime)+import GHC.Generics (Generic)+import Language.Haskell.TH.Syntax (Lift (..))++newtype MessageBody = MessageBody {unMessageBody :: Value}+  deriving newtype (Eq, Ord, FromJSON, ToJSON)+  deriving stock (Show, Generic)++-- | Message headers for metadata (routing, tracing, etc.)+-- Added in pgmq 1.5.0+newtype MessageHeaders = MessageHeaders {unMessageHeaders :: Value}+  deriving newtype (Eq, Ord, FromJSON, ToJSON)+  deriving stock (Show, Generic)++newtype MessageId = MessageId {unMessageId :: Int64}+  deriving newtype (Eq, Ord, FromJSON, ToJSON)+  deriving stock (Show, Generic)++data Queue = Queue+  { name :: !QueueName,+    createdAt :: !UTCTime,+    isPartitioned :: !Bool,+    isUnlogged :: !Bool+  }+  deriving stock (Eq, Generic, Show)++-- | https://tembo.io/pgmq/api/sql/types/+-- Note: headers field added in pgmq 1.5.0+-- Note: lastReadAt field added in pgmq 1.10.0+data Message = Message+  { messageId :: !MessageId,+    visibilityTime :: !UTCTime,+    enqueuedAt :: !UTCTime,+    lastReadAt :: !(Maybe UTCTime),+    readCount :: !Int64,+    body :: !MessageBody,+    headers :: !(Maybe Value)+  }+  deriving stock (Eq, Generic, Show)++newtype QueueName = QueueName Text+  deriving newtype (Eq, Ord, FromJSON, ToJSON)+  deriving stock (Show, Generic)++instance Lift QueueName where+  lift (QueueName t) = [|QueueName t|]+  liftTyped (QueueName t) = [||QueueName t||]++queueNameToText :: QueueName -> Text+queueNameToText (QueueName t) = t++newtype PgmqError = InvalidQueueName Text+  deriving stock (Show, Generic)++-- Adopted from https://github.com/tembo-io/pgmq/blob/e4d4b84bf302df77be2d1f877c5cf8ef8861bfc7/pgmq-rs/src/util.rs#L94+parseQueueName :: Text -> Either PgmqError QueueName+parseQueueName t+  | not isShortEnough = Left $ InvalidQueueName "The queue name is too long."+  | not hasValidCharacters = Left $ InvalidQueueName "The queue name contains invalid characters."+  | otherwise = Right $ QueueName t+  where+    isShortEnough = T.length t <= maxQueueNameLength+    hasValidCharacters = T.all isValidChar t+    isValidChar c = (isAscii c && isAlphaNum c) || c == '_'++    -- PostgreSQL identifier length information+    -- https://www.postgresql.org/docs/current/sql-syntax-lexical.html#SQL-SYNTAX-IDENTIFIERS+    maxIdentifierLength = 63 -- PostgreSQL truncates beyond this length+    longestPrefix :: Text = "archived_at_idx_"+    maxQueueNameLength = maxIdentifierLength - T.length longestPrefix