newtype FieldName = UnsafeFieldName Text
deriving stock (Show, Eq)
deriving newtype (A.ToJSON, A.ToJSONKey, A.FromJSON, A.FromJSONKey, Hashable, Buildable, ToHttpApiData, FromHttpApiData)
allowedCharSet :: [Char]
allowedCharSet = ['a'..'z'] ++ ['A'..'Z'] ++ ['0'..'9'] ++ "-_;"
newFieldName :: Text -> Either BadFieldName FieldName
newFieldName t
| T.null t =
Left $ BadFieldName "Tags must contain at least 1 character"
| T.any (`notElem` allowedCharSet) t =
Left $ BadFieldName ("Tags can only contain the following characters: '" <> T.pack allowedCharSet <> "'")
| otherwise = Right $ UnsafeFieldName t
This data type is a newtype wrapper around Text with the following invariants: the text is not empty and only contains the allowed characters. The constructor is prefixed with Unsafe to discourage its use - we should use the newFieldName smart constructor instead. The smart constructor enforces the invariants.
However, the newtype-derived FromJSON and FromHttpApiData instances don't use the smart constructor, they coerce through the Unsafe constructor.
> A.decode' @FieldName "\"\""
Just (UnsafeFieldName "")
We should add hand-written instances that use the smart constructor.
Let's do this for every newtype with a "smart constructor".
NOTE: the newFieldName error messages say "Tags must ..." instead of saying "Field names must ...", let's fix that too.
Acceptance criteria
The FromJSON, FromJSONKey and FromHttpApiData instances respect the types' invariants.
The error messages in newFieldName do not mention tags
Clarification and motivation
Consider the
FieldName
data type:This data type is a newtype wrapper around
Text
with the following invariants: the text is not empty and only contains the allowed characters. The constructor is prefixed withUnsafe
to discourage its use - we should use thenewFieldName
smart constructor instead. The smart constructor enforces the invariants.However, the newtype-derived
FromJSON
andFromHttpApiData
instances don't use the smart constructor, they coerce through theUnsafe
constructor.We should add hand-written instances that use the smart constructor. Let's do this for every newtype with a "smart constructor".
NOTE: the
newFieldName
error messages say "Tags must ..." instead of saying "Field names must ...", let's fix that too.Acceptance criteria
FromJSON
,FromJSONKey
andFromHttpApiData
instances respect the types' invariants.newFieldName
do not mention tags