packages feed

named-text (empty) → 1.0.0.0

raw patch · 4 files changed

+372/−0 lines, 4 filesdep +basedep +deepseqdep +hashable

Dependencies added: base, deepseq, hashable, prettyprinter, sayable, text

Files

+ CHANGELOG.md view
@@ -0,0 +1,5 @@+# Revision history for named-text++## 1.0.0.0 -- 2022-06-29++* First independent version.
+ Data/Name.hs view
@@ -0,0 +1,295 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE ExplicitNamespaces #-}+{-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FunctionalDependencies #-}+{-# LANGUAGE GADTs #-}+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE KindSignatures #-}+{-# LANGUAGE MagicHash #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE PolyKinds #-}+{-# LANGUAGE RankNTypes #-}+{-# LANGUAGE StandaloneDeriving #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE TypeOperators #-}+{-# LANGUAGE UndecidableInstances #-}++module Data.Name+  ( type Name+  , Named+  , name, nameOf, nameProxy, styleProxy, caselessName+  , NameStyle, UTF8, CaseInsensitive, Secure+  , HasName, myName+  , NameText, nameText+  , SomeName(SomeName), viewSomeName+  , SomeNameStyle(SomeNameStyle), viewSomeNameStyle+  , SecureName, secureName, secureNameBypass+  , IsText(fromText)+  , ConvertName(convertName)+  , ConvertNameStyle(convertStyle)+  , ValidNames, validName+)+where++import           Control.DeepSeq ( NFData )+import           Data.Hashable ( Hashable )+import           Data.Proxy ( Proxy(Proxy) )+import           Data.String ( IsString(fromString) )+import           Data.Text ( Text )+import qualified Data.Text as T+import           GHC.Exts ( Proxy#, proxy#, IsList(fromList, toList), Item )+import           GHC.Generics ( Generic )+import           GHC.TypeLits+import           Prettyprinter ( (<+>) )+import qualified Prettyprinter as PP+import           Text.Sayable+++-- | The 'Named' is a wrapper around any 'Text' that identifies the type of+-- 'Text' via the @sym@ phantom symbol type, as well as a usage specified by the+-- @style@ type parameter.  Use of 'Named' should always be preferred to using+-- a raw 'Text' (or 'String').++newtype Named (style :: NameStyle) (sym :: Symbol) =+  Named { named :: Text }+  deriving (Eq, Ord, Generic, NFData, Semigroup)++-- | The NameStyle specifies how the name itself is styled.+--+--  * The 'UTF8' default style is orthogonal to a normal String or Text.+--+--  * The 'CaseInsensitive' style indicates that uppercase ASCII characters are+--    equivalent to their lowercase form.+--+--  * The Secure style is case sensitive, but does not reveal the full contents+--    unless the specific "secureName" accessor function is used.  This is useful+--    for storing secrets (e.g. passphrases, access tokens, etc.) that should not+--    be fully visible in log messages and other miscellaneous output.++type NameStyle = Symbol+type UTF8 = "UTF8" :: NameStyle+type CaseInsensitive = "CaseInsensitive" :: NameStyle+type Secure = "SECURE!" :: NameStyle++instance Hashable (Named style sym)+++-- | Retrieve the @sym@ type parameter (the "what am I") of a Named as a text+-- value+nameOf :: KnownSymbol sym => Named style sym -> Proxy# sym -> String+nameOf _ = symbolVal'+++nameProxy :: KnownSymbol sym => Named style sym -> Proxy sym+nameProxy _ = Proxy++styleProxy :: KnownSymbol style => Named style sym -> Proxy style+styleProxy _ = Proxy+++instance {-# OVERLAPPABLE #-} IsString (Named style sym) where+  fromString = Named . fromString+++class IsText a where fromText :: Text -> a+instance {-# OVERLAPPABLE #-} IsText (Named style sym) where+  fromText = Named+++class ConvertName style origTy newTy where+  convertName :: Named style origTy -> Named style newTy+  convertName = fromText . named++class ConvertNameStyle inpStyle outStyle nameTy where+  convertStyle :: Named inpStyle nameTy -> Named outStyle nameTy+  convertStyle = fromText . named+++-- | For an @"info"@ 'saytag' (and possibly others), a 'Name' doesn't include its+-- label.  Normally, it shows the label followed by the text itself.  The Sayable+-- defers to the Prettyprinting instance for actual representation.+instance NameText style => Sayable "info" (Named style nm) where+  sayable = Saying . PP.pretty . nameText+instance {-# OVERLAPPABLE #-} (PP.Pretty (Named style nm)+                              ) => Sayable tag (Named style nm)+ where sayable = Saying . PP.pretty++++----------------------------------------------------------------------++-- | The Name type is for the standard/most commonly used style which is+-- orthogonal to a normal String or Text.++type Name = Named UTF8++name :: Name sym -> Text+name = named+++instance IsList (Name s) where+  type Item (Name s) = Item Text+  fromList = fromText . fromList+  toList = toList . name++instance ConvertName UTF8 a a where convertName = id+instance ConvertName UTF8 "component" "instance component"+instance ConvertName UTF8 "git.branch" "git.branch|ref"+instance ConvertName UTF8 "git.ref" "git.branch|ref"++instance KnownSymbol ty => PP.Pretty (Name ty) where+  pretty nm = (PP.pretty $ nameOf nm proxy#) <+> PP.squotes (PP.pretty (name nm))+++----------------------------------------------------------------------++data SomeName =+  forall (s :: Symbol) . KnownSymbol s => SomeName (Name s)++viewSomeName :: (forall (s :: Symbol) . KnownSymbol s => Name s -> r) -> SomeName -> r+viewSomeName f (SomeName n) = f n+++data SomeNameStyle nameTy =+  forall (s :: Symbol)+  . (KnownSymbol s, NameText s)+  => SomeNameStyle (Named s nameTy)++viewSomeNameStyle :: (forall (s :: Symbol) . KnownSymbol s => Named s nameTy -> r)+                  -> SomeNameStyle nameTy -> r+viewSomeNameStyle f (SomeNameStyle n) = f n+++----------------------------------------------------------------------++-- | Some objects have (contain) an associated name.  If they do, they can+-- declare the HasName constraint, and use myName to reconsistute the name from+-- the object.++class HasName x style nm | x -> style, x -> nm where+  myName :: x -> Named style nm+++-- | A general class that can be used to extract the Text back out of a name.++class NameText style where+  nameText :: Named style nm -> Text+  nameText = named++instance NameText UTF8+++----------------------------------------------------------------------++instance {-# OVERLAPPING #-} IsString (Named CaseInsensitive sym) where+  fromString = Named . T.toLower . fromString++instance {-# OVERLAPPING #-} IsText (Named CaseInsensitive sym) where+  fromText = Named . T.toLower++instance KnownSymbol ty => PP.Pretty (Named CaseInsensitive ty) where+  pretty nm = (PP.pretty $ nameOf nm proxy#)+              <+> PP.surround (PP.pretty (caselessName nm)) "«" "»"++instance NameText CaseInsensitive where+  nameText = named++caselessName :: Named CaseInsensitive sym -> Text+caselessName = named+++----------------------------------------------------------------------++-- | The SecureName is like Name, but its display form does not reveal+-- the full name.++type SecureName = Named Secure++-- | The secureName accessor is used to obtain the name field from a Secure+-- Named.  This is the normal accessor for a Secure Named and will occlude a+-- portion of the extracted name for protection.  For those specific cases where+-- the full Secure Named text is needed, the 'secureNameBypass' accessor should+-- be used instead.++secureName :: Named Secure sym -> Text+secureName nm = if T.length (named nm) < 5+                then T.replicate 8 "#"+                else ((T.take 2 $ named nm)+                      <> T.replicate (T.length (named nm) - 4) "#"+                      <> T.reverse (T.take 2 $ T.reverse $ named nm))++-- | The secureNameBypass accessor is used to obtain the raw Text from a Secure+-- Named; this essentially bypasses the security protection and should only be+-- used in the limited cases when the raw form is absolutely needed.++secureNameBypass :: Named Secure sym -> Text+secureNameBypass = named++-- instance NameText Secure sym   <-- explicitly not defined!++instance KnownSymbol ty => PP.Pretty (Named Secure ty) where+  pretty nm = (PP.pretty $ nameOf nm proxy#)+              <+> PP.squotes (PP.pretty (named nm))++-- Note that there should be no instance of ToJSON for a SecureName!+++----------------------------------------------------------------------+-- | The ValidNames constraint can be used to specify the list of allowed names+-- for a parameterized name argument.+++class ( KnownNat (AllowedNameType nty ntl)+      , DisallowedNameType nty ntl ntl+      )+  => ValidNames (nty :: Symbol) (ntl :: [Symbol]) where+  validName :: Proxy ntl -> Name nty -> Text+++----------------------------------------------------------------------+-- Internal definitions to support the ValidNames constraint class+-- implementation.++instance ( KnownNat (AllowedNameType nty ntl)  -- n.b. if this fails, see Note-1+         , DisallowedNameType nty ntl ntl+         )+   => ValidNames nty ntl where+  validName _ = name++type family AllowedNameType (nty :: Symbol) (ntl :: [Symbol]) :: Nat where+  AllowedNameType nty (nty ': ntl) = 0+  AllowedNameType nty (any ': ntl) = 1 + (AllowedNameType nty ntl)++-- * Note-1+--+-- Normally the DisallowedNameType will generate a useful TypeError if+-- a parameter uses a name not in the allowed names list.  For example:+--+-- > foo :: ValidNames n '[ "name1", "name 2" ] -> Name n -> Bool+-- > foo (Name @"name1" "indiana")  -- OK+-- > foo (Name @"last" "jones")  -- generates TypeError indicating+--                               -- "last" is not a member of the+--                               -- allowed list ["name1", "name 2"]+--+-- However, when parametric constraints are cascaded and the+-- parametric constraints don't align, the compilation complaint will+-- be that there's no KnownNat for the AllowedNameType instance+-- constraint on ValidNames above.  for example:+--+-- > bar :: ValidNames n '[ "name1", "stage" ]+-- > bar n = foo n+--+-- generates the KnownNat error.  Not sure of a better way to handle+-- this at the moment.++class DisallowedNameType (nty :: Symbol) (okntl :: [Symbol]) (ntl :: [Symbol])++instance TypeError ('Text "Name '" ':<>: 'ShowType nty+                    ':<>: 'Text "' not in allowed Names: " ':<>: 'ShowType ntl)+         => DisallowedNameType nty '[] ntl+instance DisallowedNameType nty (nty ': ntys) ntl+instance {-# OVERLAPPABLE #-} DisallowedNameType nty ntys ntl+         => DisallowedNameType nty (oty ': ntys) ntl
+ LICENSE view
@@ -0,0 +1,15 @@+Copyright 2022, Galois Inc++Permission to use, copy, modify, and/or distribute this software for+any purpose with or without fee is hereby granted, provided that the+above copyright notice and this permission notice appear in all+copies.++THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL+WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED+WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE+AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL+DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR+PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER+TORTIOUS ACTION ARISING OUT OF OR IN CONNECTION WITH THE USE OR+PERFORMANCE OF THIS SOFTWARE.
+ named-text.cabal view
@@ -0,0 +1,57 @@+cabal-version:      2.4+name:               named-text+version:            1.0.0.0+synopsis:           A parameterized named text type and associated functionality.+description:+  .+  A plain String or Text is dangerous because it can be arbitrarily combined,+  converted, emitted, and updated without consideration of the validity and+  applicability of the contents and the usage scenario.+  .+  This module provides a Named type wrapper around Text which has two+  additional type parameters: one which indicates what the content of the Text+  represents, and one which indicates the style of the name.+  .+  There are additionally functions and classes which allow conversions and+  transformations between names with different styles and types (or disallow them+  where appropriate).+  .+  The core intent of this library is that the Named is used instead of a raw+  String or Text to provide type-level tracking and safety.++-- bug-reports:+license:            ISC+license-file:       LICENSE+author:             Kevin Quick+maintainer:         kquick@galois.com+copyright:          Galois Inc, 2022+category:           Data+build-type:         Simple+extra-source-files: CHANGELOG.md++source-repository head+  type: git+  location: https://github.com/kquick/named-text++common bldspec+  ghc-options:  -Wall+                -Wcompat+                -Wincomplete-uni-patterns+                -Wsimplifiable-class-constraints+                -Wpartial-fields+                -fhide-source-paths+                -O2+                -flate-specialise+                -fspecialise-aggressively++library+    import:           bldspec+    hs-source-dirs:   .+    default-language: Haskell2010+    build-depends:    base >= 4.10 && < 4.15+                    , deepseq+                    , hashable+                    , prettyprinter >= 1.7.0 && < 1.8+                    , sayable >= 1.0 && < 1.1+                    , text+    exposed-modules:  Data.Name