clickhouse-migrate-0.1.0.0: src/ClickHouseMigrate/Definition.hs
-- | Pure authoring values. Constructors and representations remain private.
module ClickHouseMigrate.Definition
( ComponentName,
MigrationName,
StepName,
MigrationId,
SqlStep,
Migration,
MigrationComponent,
componentName,
migrationName,
stepName,
componentNameText,
migrationNameText,
stepNameText,
migrationIdFromNames,
migrationIdComponent,
migrationIdName,
sqlStep,
sqlMigration,
migrationComponent,
withMigrationDescription,
sqlMigrationFromList,
migrationComponentFromList,
)
where
import ClickHouseMigrate.Error
import ClickHouseMigrate.Internal.SqlEnvelope (validateSql)
import ClickHouseMigrate.Internal.Types
import ClickHouseMigrate.Prelude
import Data.List.NonEmpty qualified as NE
import Data.Set qualified as Set
import Data.Text qualified as Text
validateName :: Text -> Text -> Either DefinitionError Text
validateName scope value
| Text.length value <= 128 && maybe False (\(c, rest) -> first c && Text.all later rest) (Text.uncons value) = Right value
| otherwise = Left (DefinitionError InvalidName [scope] Nothing)
where
first c = (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')
later c = first c || c == '_' || c == '-'
componentName :: Text -> Either DefinitionError ComponentName
componentName = fmap ComponentName . validateName "component"
migrationName :: Text -> Either DefinitionError MigrationName
migrationName = fmap MigrationName . validateName "migration"
stepName :: Text -> Either DefinitionError StepName
stepName = fmap StepName . validateName "step"
componentNameText :: ComponentName -> Text
componentNameText (ComponentName value) = value
migrationNameText :: MigrationName -> Text
migrationNameText (MigrationName value) = value
stepNameText :: StepName -> Text
stepNameText (StepName value) = value
-- | A schedule identity from already validated local names.
migrationIdFromNames :: ComponentName -> MigrationName -> MigrationId
migrationIdFromNames = MigrationId
migrationIdComponent :: MigrationId -> ComponentName
migrationIdComponent (MigrationId component _) = component
migrationIdName :: MigrationId -> MigrationName
migrationIdName (MigrationId _ name) = name
sqlStep :: Text -> ByteString -> Either DefinitionError SqlStep
sqlStep name sql = do
validated <- stepName name
case validateSql sql of
Left (DefinitionError c path offset) -> Left (DefinitionError c (["step", name] ++ path) offset)
Right () -> Right (SqlStep validated sql)
sqlMigration :: Text -> NonEmpty SqlStep -> Either DefinitionError Migration
sqlMigration name steps = do
validated <- migrationName name
unique ["migration", name, "steps"] [step | SqlStep step _ <- NE.toList steps]
pure (Migration validated Nothing steps)
-- | Parsing boundary for formats which can represent empty lists.
sqlMigrationFromList :: Text -> [SqlStep] -> Either DefinitionError Migration
sqlMigrationFromList name steps = nonEmpty ["migration", name, "steps"] steps >>= sqlMigration name
withMigrationDescription :: Maybe Text -> Migration -> Migration
withMigrationDescription description (Migration name _ steps) = Migration name description steps
migrationComponent :: Text -> Set Text -> NonEmpty Migration -> Either DefinitionError MigrationComponent
migrationComponent name dependencies migrations = do
validated <- componentName name
deps <- Set.fromList <$> traverse componentName (Set.toAscList dependencies)
unique ["component", name, "migrations"] [migration | Migration migration _ _ <- NE.toList migrations]
pure (MigrationComponent validated deps migrations)
migrationComponentFromList :: Text -> Set Text -> [Migration] -> Either DefinitionError MigrationComponent
migrationComponentFromList name deps migrations = nonEmpty ["component", name, "migrations"] migrations >>= migrationComponent name deps
nonEmpty :: [Text] -> [a] -> Either DefinitionError (NonEmpty a)
nonEmpty path = maybe (Left (DefinitionError EmptyCollection path Nothing)) Right . NE.nonEmpty
unique :: (Ord a) => [Text] -> [a] -> Either DefinitionError ()
unique path names
| Set.size (Set.fromList names) == length names = Right ()
| otherwise = Left (DefinitionError DuplicateName path Nothing)