diff --git a/ChangeLog.md b/ChangeLog.md
new file mode 100644
--- /dev/null
+++ b/ChangeLog.md
@@ -0,0 +1,16 @@
+# Changelog for coformat
+
+## v0.2.1.0
+
+* Allow the user to force-set formatter options.
+* Fixed discovering the default style options: need to pass user-forced and hardcoded options,
+  otherwise clang-format might return nonsense.
+
+## v0.2.0.0
+
+* Changed the scoring from Levenshtein distance to something more suitable for the task.
+* Allow resuming the optimization process from an existing style file.
+
+## v0.1.0.0
+
+Initial release: it formats something and is even tolerant to the formatter's crashes.
diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,30 @@
+Copyright Georg Rudoy (c) 2019
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Georg Rudoy nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,7 @@
+# coformat
+
+Code style formatters take a style description and format a code base accordingly.
+
+*coformat* is _dual_ to them: it takes a code base and produces a code style description for it! This is achieved by a cunning combination of state-of-the-art type system features (`DataKinds`, `RankNTypes` and `GADTs`, among others) and modern machine learning techniques (there is a numerical optimization step after all).
+
+More seriously, this project only supports `clang-format` for now, but plugging other formatters (perhaps for other languages) should be trivial enough, provided there is an easy way to get the list of available options and their possible values.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/app/Main.hs b/app/Main.hs
new file mode 100644
--- /dev/null
+++ b/app/Main.hs
@@ -0,0 +1,59 @@
+{-# LANGUAGE FlexibleContexts, FlexibleInstances, TypeOperators #-}
+{-# LANGUAGE DataKinds, RankNTypes, GADTs #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE OverloadedStrings, RecordWildCards #-}
+
+module Main where
+
+import qualified Data.ByteString.Char8 as BS
+import qualified Data.List.NonEmpty as N
+import Control.Concurrent.Async.Pool
+import Control.Monad.Except
+import Control.Monad.Logger
+import GHC.Conc
+import GHC.Generics
+import Numeric.Natural
+import Options.Generic
+import System.IO(IOMode(..), Handle, stderr, withFile)
+import System.Log.FastLogger
+
+import Clang.Coformat.Pipeline
+
+data Options w = Options
+  { parallelism :: w ::: Maybe Natural <?> "Max parallel threads of heavy-duty computations (defaults to NCPUs - 1)"
+  , debugLog :: w ::: Maybe FilePath <?> "Debug log file (disabled by default)"
+  , maxSubsetSize :: w ::: Maybe Natural <?> "Maximum size of the inter-dependent subsets to consider (defaults to 1)"
+  , resumePath :: w ::: Maybe FilePath <?> "The path to the style format file to start from (if any)"
+  , forceOption :: w ::: [String] <?> "Force these options to have the given values (`option:value`)"
+  , input :: w ::: N.NonEmpty FilePath <?> "The input file(s) to use"
+  , output :: w ::: FilePath <?> "Where to save the resulting configuration file"
+  } deriving (Generic)
+
+instance ParseRecord (Options Wrapped)
+
+logOutput :: Maybe Handle
+          -> Loc -> LogSource -> LogLevel -> LogStr -> IO ()
+logOutput debugHandle loc src level msg
+  | level == LevelDebug = case debugHandle of Nothing -> pure ()
+                                              Just handle -> BS.hPutStr handle str
+  | otherwise = BS.hPutStr stderr str
+  where
+    str = fromLogStr $ defaultLogStr loc src level msg
+
+main :: IO ()
+main = do
+  Options { .. } <- unwrapRecord "coformat"
+
+  tgSize <- case parallelism of
+                 Just n -> pure $ fromIntegral n
+                 Nothing -> (\n -> max 1 $ n - 1) <$> getNumCapabilities
+
+  let withDebugLog | Just path <- debugLog = \f -> withFile path AppendMode $ f . Just
+                   | otherwise = ($ Nothing)
+
+  res <- withDebugLog $ \maybeLogHandle ->
+         withTaskGroup tgSize $ \taskGroup ->
+         (`runLoggingT` logOutput maybeLogHandle) $ runExceptT $ runOptPipeline PipelineOpts { forceStrs = forceOption, .. }
+  case res of
+       Left err -> putStrLn err
+       Right bs -> BS.writeFile output bs
diff --git a/coformat.cabal b/coformat.cabal
new file mode 100644
--- /dev/null
+++ b/coformat.cabal
@@ -0,0 +1,150 @@
+cabal-version: 1.12
+
+-- This file has been generated from package.yaml by hpack version 0.31.2.
+--
+-- see: https://github.com/sol/hpack
+--
+-- hash: ea70ef65978e2e5237ba446709fed08a5dcdef26609680601b4b7a02afdbdc95
+
+name:           coformat
+version:        0.2.1.0
+synopsis:       Generate clang-format config based on some existing code base
+description:    Please see the README on GitHub at <https://github.com/0xd34df00d/coformat#readme>
+category:       Language
+homepage:       https://github.com/0xd34df00d/coformat#readme
+bug-reports:    https://github.com/0xd34df00d/coformat/issues
+author:         Georg Rudoy
+maintainer:     0xd34df00d@gmail.com
+copyright:      2019 Georg Rudoy
+license:        BSD3
+license-file:   LICENSE
+build-type:     Simple
+extra-source-files:
+    README.md
+    ChangeLog.md
+
+source-repository head
+  type: git
+  location: https://github.com/0xd34df00d/coformat
+
+library
+  exposed-modules:
+      Clang.Coformat.Formatter
+      Clang.Coformat.Optimization
+      Clang.Coformat.Pipeline
+      Clang.Coformat.Score
+      Clang.Coformat.StyOpts
+      Clang.Coformat.Util
+      Clang.Coformat.Variables
+      Clang.Format.Descr
+      Clang.Format.Descr.Operations
+      Clang.Format.DescrParser
+      Clang.Format.YamlConversions
+  other-modules:
+      Paths_coformat
+  hs-source-dirs:
+      src
+  ghc-options: -Wall
+  build-depends:
+      aeson
+    , async
+    , async-pool
+    , base >=4.7 && <5
+    , bytestring
+    , can-i-haz
+    , command-qq
+    , containers
+    , dom-selector
+    , extra
+    , fast-logger
+    , generic-data
+    , hashable
+    , html-conduit
+    , interpolate
+    , lens
+    , lens-aeson
+    , monad-logger
+    , mtl
+    , optparse-generic
+    , scientific
+    , temporary
+    , text
+    , unordered-containers
+    , xml-conduit
+    , yaml
+  default-language: Haskell2010
+
+executable coformat-exe
+  main-is: Main.hs
+  other-modules:
+      Paths_coformat
+  hs-source-dirs:
+      app
+  ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N
+  build-depends:
+      aeson
+    , async
+    , async-pool
+    , base >=4.7 && <5
+    , bytestring
+    , can-i-haz
+    , coformat
+    , command-qq
+    , containers
+    , dom-selector
+    , extra
+    , fast-logger
+    , generic-data
+    , hashable
+    , html-conduit
+    , interpolate
+    , lens
+    , lens-aeson
+    , monad-logger
+    , mtl
+    , optparse-generic
+    , scientific
+    , temporary
+    , text
+    , unordered-containers
+    , xml-conduit
+    , yaml
+  default-language: Haskell2010
+
+test-suite coformat-test
+  type: exitcode-stdio-1.0
+  main-is: Spec.hs
+  other-modules:
+      Paths_coformat
+  hs-source-dirs:
+      test
+  ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N
+  build-depends:
+      aeson
+    , async
+    , async-pool
+    , base >=4.7 && <5
+    , bytestring
+    , can-i-haz
+    , coformat
+    , command-qq
+    , containers
+    , dom-selector
+    , extra
+    , fast-logger
+    , generic-data
+    , hashable
+    , html-conduit
+    , interpolate
+    , lens
+    , lens-aeson
+    , monad-logger
+    , mtl
+    , optparse-generic
+    , scientific
+    , temporary
+    , text
+    , unordered-containers
+    , xml-conduit
+    , yaml
+  default-language: Haskell2010
diff --git a/src/Clang/Coformat/Formatter.hs b/src/Clang/Coformat/Formatter.hs
new file mode 100644
--- /dev/null
+++ b/src/Clang/Coformat/Formatter.hs
@@ -0,0 +1,19 @@
+{-# LANGUAGE DataKinds #-}
+
+module Clang.Coformat.Formatter where
+
+import qualified Data.ByteString.Char8 as BS
+import qualified Data.Text as T
+import Control.Monad.IO.Class
+
+import Clang.Format.Descr
+
+data FormatterInfo = FormatterInfo
+  { executableName :: String
+  , knownOptions :: [ConfigItemT 'Supported]
+  , baseStyles :: [T.Text]
+  } deriving (Show)
+
+class Formatter fmt where
+  formatterInfo :: proxy fmt -> FormatterInfo
+  formatFile :: MonadIO m => proxy fmt -> T.Text -> [ConfigItemT 'Value] -> FilePath -> m BS.ByteString
diff --git a/src/Clang/Coformat/Optimization.hs b/src/Clang/Coformat/Optimization.hs
new file mode 100644
--- /dev/null
+++ b/src/Clang/Coformat/Optimization.hs
@@ -0,0 +1,164 @@
+{-# LANGUAGE GADTs, DataKinds, TypeApplications, RankNTypes, ScopedTypeVariables, ConstraintKinds #-}
+{-# LANGUAGE DeriveAnyClass, DeriveGeneric #-}
+{-# LANGUAGE FlexibleInstances, FlexibleContexts #-}
+{-# LANGUAGE QuasiQuotes, RecordWildCards, TupleSections, LambdaCase #-}
+
+module Clang.Coformat.Optimization where
+
+import qualified Data.ByteString.Lazy.Char8 as BSL
+import qualified Data.HashMap.Strict as HM
+import qualified Data.Text as T
+import qualified Data.Text.Lazy.Encoding as TL
+import Control.Concurrent.Async.Pool
+import Control.Lens
+import Control.Monad.Except.CoHas
+import Control.Monad.Extra
+import Control.Monad.Logger
+import Control.Monad.Reader.Has hiding(update)
+import Control.Monad.State.Strict
+import Data.Foldable
+import Data.List
+import Data.Ord
+import Data.Proxy
+import Data.String.Interpolate.IsString
+import Numeric.Natural
+import System.Command.QQ
+
+import Clang.Coformat.Score
+import Clang.Coformat.StyOpts
+import Clang.Coformat.Util
+import Clang.Coformat.Variables
+import Clang.Format.Descr
+
+data FmtEnv = FmtEnv
+  { baseStyle :: T.Text
+  , preparedFiles :: [PreparedFile]
+  , constantOpts :: [ConfigItemT 'Value]
+  }
+
+data OptEnv = OptEnv
+  { categoricalVariables :: [IxedCategoricalVariable]
+  , integralVariables :: [IxedIntegralVariable]
+  , maxSubsetSize :: Natural
+  }
+
+runClangFormat :: (MonadError err m, CoHas UnexpectedFailure err, CoHas ExpectedFailure err, MonadIO m, MonadLogger m)
+               => PreparedFile -> String -> BSL.ByteString -> m Score
+runClangFormat prepared logStr formattedSty = do
+  stdout <- checked [sh|clang-format --style='#{unpackedSty}' #{path}|]
+  let dist = calcScore prepared $ BSL.toStrict $ TL.encodeUtf8 stdout
+  logDebugN [i|#{logStr}: #{dist}|]
+  pure dist
+  where
+    path = filename prepared
+    unpackedSty = BSL.unpack formattedSty
+
+type OptMonad err r m = (MonadLoggerIO m, MonadError err m, CoHas UnexpectedFailure err, MonadReader r m, Has FmtEnv r)
+
+runClangFormatFiles :: (OptMonad err r m, CoHas ExpectedFailure err)
+                    => [ConfigItemT 'Value] -> String -> m Score
+runClangFormatFiles varOpts logStr = do
+  FmtEnv { .. } <- ask
+  let sty = StyOpts { basedOnStyle = baseStyle, additionalOpts = constantOpts <> varOpts }
+  let formattedSty = formatStyArg sty
+  fmap mconcat $ forM preparedFiles $ \prepared -> runClangFormat prepared [i|#{logStr} at #{filename prepared}|] formattedSty
+
+chooseBaseStyle :: (MonadError String m, MonadLoggerIO m) => [T.Text] -> [ConfigItemT 'Value] -> [PreparedFile] -> m (T.Text, Score)
+chooseBaseStyle baseStyles predefinedOpts files = do
+  sty2dists <- forConcurrently' ((,) <$> baseStyles <*> files) $ \(sty, file) -> do
+    let formattedArg = formatStyArg StyOpts { basedOnStyle = sty, additionalOpts = predefinedOpts }
+    convert (show @(Either ExpectedFailure UnexpectedFailure)) $ (sty,) <$> runClangFormat file [i|Initial guess for #{sty} at #{filename file}|] formattedArg
+  let accumulated = HM.toList $ HM.fromListWith (<>) sty2dists
+  forM_ accumulated $ \(sty, acc) -> logInfoN [i|Initial accumulated guess for #{sty}: #{acc}|]
+  pure $ minimumBy (comparing snd) accumulated
+
+variateAt :: forall a. (Variate a, Foldable (VariateResult a))
+          => Proxy a -> Int -> [ConfigItemT 'Value] -> [[ConfigItemT 'Value]]
+variateAt _ idx opts = [ update idx (updater v') opts | v' <- toList variated ]
+  where
+    thePrism :: Prism' (ConfigTypeT 'Value) a
+    thePrism = varPrism
+    variated = variate $ value (opts !! idx) ^?! thePrism
+    updater v cfg = cfg { value = value cfg & thePrism .~ v }
+
+data OptState = OptState
+  { currentOpts :: [ConfigItemT 'Value]
+  , currentScore :: Score
+  } deriving (Show)
+
+initOptState :: [ConfigItemT 'Value] -> Score -> OptState
+initOptState currentOpts currentScore = OptState { .. }
+
+dropExpectedFailures :: OptMonad err r m
+                     => (forall err' r' m'. (OptMonad err' r' m', CoHas ExpectedFailure err') => m' Score)
+                     -> m Score
+dropExpectedFailures act = do
+  res <- runExceptT act
+  case res of
+       Left (UnexpectedFailure failure) -> throwError failure
+       Left (ExpectedFailure failure) -> do
+         logErrorN [i|Unable to run the formatter: #{show failure}|]
+         pure maxBound
+       Right sc -> pure sc
+
+variateSubset :: [SomeIxedVariable] -> [ConfigItemT 'Value] -> [[ConfigItemT 'Value]]
+variateSubset [] opts = [opts]
+variateSubset (SomeIxedVariable (IxedVariable (MkDV (_ :: a)) idx) : rest) opts = concatMap (variateSubset rest) $ variateAt @a Proxy idx opts
+
+showVariated :: [SomeIxedVariable] -> [ConfigItemT 'Value] -> String
+showVariated vars opts = intercalate ", " [showVar var | var <- vars]
+  where
+    showVar (SomeIxedVariable (IxedVariable _ idx)) = [i|#{name $ opts !! idx} -> #{value $ opts !! idx}|]
+
+chooseBestSubset :: (OptMonad err r m, Has OptState r, Has TaskGroup r)
+                 => Natural -> [SomeIxedVariable] -> m (Maybe ([ConfigItemT 'Value], Score))
+chooseBestSubset subsetSize ixedVariables = do
+  OptState { .. } <- ask
+  partialResults <- forConcurrentlyPooled (subsetsN subsetSize ixedVariables) $ \someVarsSubset -> do
+    opt2scores <- forM (variateSubset someVarsSubset currentOpts) $ \opts' ->
+      fmap (opts',) $ dropExpectedFailures $ runClangFormatFiles opts' $ showVariated someVarsSubset opts'
+    let (bestOpts, bestScore) = minimumBy (comparing snd) opt2scores
+    when (bestScore < currentScore) $
+      logInfoN [i|Total dist for #{showVariated someVarsSubset bestOpts}: #{currentScore} -> #{bestScore}|]
+    pure (bestOpts, bestScore, someVarsSubset)
+  let (bestOpts, bestScore, bestVarsSubset) = minimumBy (comparing (^. _2)) partialResults
+  if bestScore < currentScore
+    then do
+      logInfoN [i|Choosing #{showVariated bestVarsSubset bestOpts}|]
+      pure $ Just (bestOpts, bestScore)
+    else pure Nothing
+
+stepGDGeneric' :: (OptMonad err r m, Has TaskGroup r, Has OptEnv r, MonadState OptState m)
+               => Natural -> [OptEnv -> [SomeIxedVariable]] -> m ()
+stepGDGeneric' subsetSize varGetters = do
+  current <- get
+  fmtEnv@FmtEnv { .. } <- ask
+  optEnv <- ask
+  tg :: TaskGroup <- ask
+  runReaderT (chooseBestSubset subsetSize $ concatMap ($ optEnv) varGetters) (current, fmtEnv, tg) >>=
+    \case Nothing -> pure ()
+          Just (opts', score') -> do
+            logInfoN [i|Total score after optimization on all files: #{score'}|]
+            put OptState { currentOpts = opts', currentScore = score' }
+
+stepGDGeneric :: (OptMonad err r m, Has TaskGroup r, Has OptEnv r, MonadState OptState m)
+              => Natural -> [OptEnv -> [SomeIxedVariable]] -> m ()
+stepGDGeneric subsetSize varGetters = whenM ((> mempty) <$> gets currentScore) $ stepGDGeneric' subsetSize varGetters
+
+fixGD :: (OptMonad err r m, Has TaskGroup r, Has OptEnv r, MonadState OptState m, err ~ UnexpectedFailure)
+      => Maybe Int -> Natural -> m ()
+fixGD (Just 0) _ = pure ()
+fixGD counter curSubsetSize = do
+  maxSubsetSize' <- asks maxSubsetSize
+  if curSubsetSize > maxSubsetSize'
+    then logInfoN [i|Done optimizing|]
+    else do
+      startScore <- gets currentScore
+      stepGDGeneric curSubsetSize [asSome . categoricalVariables, asSome . integralVariables]
+      endScore <- gets currentScore
+      logInfoN [i|Full optimization step done, went from #{startScore} to #{endScore}|]
+      if startScore /= endScore
+        then fixGD (subtract 1 <$> counter) 1
+        else do
+          logInfoN [i|Done optimizing with subset size #{curSubsetSize}, stopped at score #{endScore}|]
+          fixGD (subtract 1 <$> counter) (curSubsetSize + 1)
diff --git a/src/Clang/Coformat/Pipeline.hs b/src/Clang/Coformat/Pipeline.hs
new file mode 100644
--- /dev/null
+++ b/src/Clang/Coformat/Pipeline.hs
@@ -0,0 +1,148 @@
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE DataKinds, GADTs #-}
+{-# LANGUAGE TypeApplications, OverloadedStrings, RecordWildCards, QuasiQuotes #-}
+
+module Clang.Coformat.Pipeline
+( runOptPipeline
+, PipelineOpts(..)
+) where
+
+import qualified Control.Monad.Except.CoHas as EC
+import qualified Data.ByteString.Char8 as BS
+import qualified Data.ByteString.Lazy.Char8 as BSL
+import qualified Data.HashMap.Strict as HM
+import qualified Data.Text as T
+import qualified Data.Text.Lazy.Encoding as TL
+import Control.Concurrent.Async.Pool
+import Control.Lens hiding (Wrapped, Unwrapped)
+import Control.Monad.Except
+import Control.Monad.Logger
+import Control.Monad.Reader
+import Control.Monad.State.Strict
+import Data.Aeson.Lens
+import Data.Bifunctor
+import Data.Foldable
+import Data.List.NonEmpty(NonEmpty)
+import Data.Maybe
+import Data.String.Interpolate.IsString
+import Data.Traversable
+import Numeric.Natural
+import System.Command.QQ
+
+import Clang.Coformat.Optimization
+import Clang.Coformat.Score
+import Clang.Coformat.StyOpts
+import Clang.Coformat.Util
+import Clang.Coformat.Variables
+import Clang.Format.Descr
+import Clang.Format.Descr.Operations
+import Clang.Format.DescrParser
+import Clang.Format.YamlConversions
+
+liftEither' :: (MonadError String m, Show e) => String -> Either e a -> m a
+liftEither' context = liftEither . first ((context <>) . show)
+
+parseOptsDescription :: (MonadError String m, MonadIO m) => FilePath -> m ([T.Text], [ConfigItemT 'Supported])
+parseOptsDescription path = do
+  parseResult <- liftIO $ parseFile path
+  supportedOptions <- filterParsedItems <$> liftEither' "Unable to parse the file: " parseResult
+  baseStyles <- case find ((== bosKey) . name) supportedOptions of
+                     Nothing -> throwError "No `BasedOnStyle` option"
+                     Just stys -> pure stys
+  let varyingOptions = filter ((/= bosKey) . name) supportedOptions
+  case value baseStyles of
+       CTEnum { .. } -> pure (variants, varyingOptions)
+       _ -> throwError [i|Unknown type for the `BaseStyles` option: #{value baseStyles}|]
+  where
+    bosKey = ["BasedOnStyle"]
+
+hardcodedOpts :: [ConfigItemT 'Value]
+hardcodedOpts = [ ConfigItem { name = ["Language"], value = CTEnum ["Cpp"] "Cpp" }
+                , ConfigItem { name = ["BreakBeforeBraces"], value = CTEnum ["Custom"] "Custom" }
+                , ConfigItem { name = ["DisableFormat"], value = CTBool False }
+                , ConfigItem { name = ["SortIncludes"], value = CTBool False }
+                ]
+
+data InitializeOptionsResult = InitializeOptionsResult
+  { baseStyle :: T.Text
+  , baseScore :: Score
+  , baseOptions :: [ConfigItemT 'Value]
+  , filledOptions :: [ConfigItemT 'Value]
+  , userForcedOpts :: [ConfigItemT 'Value]
+  }
+
+initializeOptions :: (MonadError String m, MonadLoggerIO m)
+                  => [PreparedFile] -> Maybe FilePath -> [String] -> m InitializeOptionsResult
+initializeOptions preparedFiles maybeResumePath forceStrs = do
+  (baseStyles, allOptions) <- parseOptsDescription "data/ClangFormatStyleOptions-9.html"
+  let varyingOptions = filter (not . (`elem` hardcodedOptsNames) . name) allOptions
+  userForcedOpts <- parseUserOpts forceStrs allOptions
+
+  maybeResumeObj <- for maybeResumePath $ liftIO . BS.readFile
+                                      >=> convert (show @FillError) . preprocessYaml PartialConfig
+  maybeResumeOptions <- for maybeResumeObj $ convert (show @FillError) . collectConfigItems varyingOptions
+
+  let allFixedOpts = hardcodedOpts <> userForcedOpts
+
+  (baseStyle, baseScore) <-
+      case maybeResumeObj of
+           Nothing -> chooseBaseStyle baseStyles allFixedOpts preparedFiles
+           Just resumeObj -> do
+              baseStyle <- EC.liftMaybe ("Unable to find `BasedOnStyle` key in the resume file" :: String)
+                        $ HM.lookup "BasedOnStyle" resumeObj ^? _Just . _String
+              constantOpts <- convert (show @FillError) $ collectConfigItems varyingOptions resumeObj
+              score <- convert (show @Failure) $ flip runReaderT FmtEnv { .. } $ runClangFormatFiles allFixedOpts [i|Calculating the score of the resumed-from style|]
+              pure (baseStyle, score)
+
+  logInfoN [i|Using initial style: #{baseStyle} with score of #{baseScore}|]
+  let formattedBaseSty = BSL.unpack $ formatStyArg $ StyOpts { basedOnStyle = baseStyle, additionalOpts = allFixedOpts }
+  stdout <- convert (show @Failure) $ checked [sh|clang-format --style='#{formattedBaseSty}' --dump-config|]
+  baseOptions <- convert (show @FillError) $ fillConfigItems varyingOptions $ BSL.toStrict $ TL.encodeUtf8 stdout
+
+  let filledOptions | Just resumeOptions <- maybeResumeOptions = baseOptions `replaceItemsWith` resumeOptions
+                    | otherwise = baseOptions
+
+  pure InitializeOptionsResult { .. }
+  where
+    hardcodedOptsNames = name <$> hardcodedOpts
+
+parseUserOpts :: (MonadError String m, ParseableConfigState f) => [String] -> [ConfigItemT f] -> m [ConfigItemT 'Value]
+parseUserOpts opts baseOpts = forM opts $ splitStr >=> findBaseOpt >=> uncurry parseConfigValue
+  where
+    splitStr str | (name, _:valStr) <- break (== ':') str = pure (T.splitOn "." $ T.pack name, valStr)
+                 | otherwise = throwError [i|Unable to parse `#{str}`: it should have the form of `name:value`|]
+    findBaseOpt (name, valStr) | Just item <- HM.lookup name baseOptsMap = pure (item, valStr)
+                               | otherwise = throwError [i|Unable to find option `#{name}`|]
+
+    baseOptsMap = HM.fromList [ (name item, item) | item <- baseOpts]
+
+data PipelineOpts = PipelineOpts
+  { maxSubsetSize :: Maybe Natural
+  , resumePath :: Maybe FilePath
+  , input :: NonEmpty FilePath
+  , taskGroup :: TaskGroup
+  , forceStrs :: [String]
+  }
+
+runOptPipeline :: (MonadError String m, MonadLoggerIO m)
+               => PipelineOpts -> m BS.ByteString
+runOptPipeline PipelineOpts { .. } = do
+  preparedFiles <- mapM prepareFile $ toList input
+
+  InitializeOptionsResult { .. } <- initializeOptions preparedFiles resumePath forceStrs
+
+  let categoricalVariables = [ IxedVariable dv idx
+                             | (Just dv, idx) <- zip (typToDV . value <$> filledOptions) [0..]
+                             ]
+  let integralVariables = [ IxedVariable dv idx
+                          | (Just dv, idx) <- zip (typToIV . value <$> filledOptions) [0..]
+                          ]
+  let constantOpts = hardcodedOpts <> userForcedOpts
+  let fmtEnv = FmtEnv { .. }
+  let optEnv = OptEnv { maxSubsetSize = fromMaybe 1 maxSubsetSize, .. }
+  let optState = initOptState filledOptions baseScore
+  finalOptState <- convert (show @UnexpectedFailure) $ flip runReaderT (fmtEnv, optEnv, taskGroup) $ execStateT (fixGD Nothing 1) optState
+  let finalStyOpts = StyOpts { basedOnStyle = baseStyle
+                             , additionalOpts = constantOpts <> currentOpts finalOptState `subtractMatching` baseOptions
+                             }
+  pure $ formatClangFormat finalStyOpts
diff --git a/src/Clang/Coformat/Score.hs b/src/Clang/Coformat/Score.hs
new file mode 100644
--- /dev/null
+++ b/src/Clang/Coformat/Score.hs
@@ -0,0 +1,70 @@
+{-# LANGUAGE DeriveGeneric, DerivingVia #-}
+{-# LANGUAGE RecordWildCards, QuasiQuotes #-}
+
+module Clang.Coformat.Score
+( Score
+, calcScore
+
+, PreparedFile
+, filename
+, prepareFile
+) where
+
+import qualified Data.ByteString.Char8 as BS
+import qualified Data.IntMap.Strict as IM
+import Control.Monad.IO.Class
+import Data.Char
+import Data.Monoid
+import Data.String.Interpolate
+import GHC.Generics
+import Generic.Data
+
+type CharsHist = IM.IntMap Int
+
+data PreparedFile = PreparedFile
+  { filename :: FilePath
+  , contents :: BS.ByteString
+  , charsHist :: CharsHist
+  } deriving (Eq)
+
+prepareFile :: MonadIO m => FilePath -> m PreparedFile
+prepareFile filename = do
+  contents <- liftIO $ BS.readFile filename
+  let charsHist = calcCharsHist contents
+  pure $ PreparedFile { .. }
+
+data Score = Score
+  { significantLettersCountsDiff :: Sum Int
+  , spacesMisalignment :: Sum Int
+  } deriving (Eq, Ord, Bounded, Generic)
+    deriving (Semigroup, Monoid) via (Generically Score)
+
+instance Show Score where
+  show Score { .. } = [i|Score { #{getSum significantLettersCountsDiff} / #{getSum spacesMisalignment} }|]
+
+calcScore :: PreparedFile -> BS.ByteString -> Score
+calcScore prepared str = Score { .. }
+  where
+    significantLettersCountsDiff = Sum $ calcCharsDiff (charsHist prepared) (calcCharsHist str)
+    spacesMisalignment | significantLettersCountsDiff /= 0 = 0
+                       | otherwise = Sum $ alignSpaces (contents prepared) str
+
+calcCharsDiff :: CharsHist -> CharsHist -> Int
+calcCharsDiff hm1 hm2 = sum $ IM.elems $ IM.unionWith (\v1 v2 -> abs $ v1 - v2) hm1 hm2
+
+calcCharsHist :: BS.ByteString -> CharsHist
+calcCharsHist = BS.foldl' ins mempty
+  where
+    ins hm ch | isSpace ch = hm
+              | otherwise = IM.insertWith (+) (ord ch) 1 hm
+
+alignSpaces :: BS.ByteString -> BS.ByteString -> Int
+alignSpaces bs1 bs2 = go (BS.unpack bs1) (BS.unpack bs2)
+  where
+    go s1 [] = length s1
+    go [] s2 = length s2
+    go (c1:s1) (c2:s2) | c1 == c2 = go s1 s2
+                       | isSpace c1 && isSpace c2 = 2 + go s1 s2
+                       | isSpace c1 = 1 + go s1 (c2:s2)
+                       | isSpace c2 = 1 + go (c1:s1) s2
+                       | otherwise = 2 + length s1 + length s2
diff --git a/src/Clang/Coformat/StyOpts.hs b/src/Clang/Coformat/StyOpts.hs
new file mode 100644
--- /dev/null
+++ b/src/Clang/Coformat/StyOpts.hs
@@ -0,0 +1,44 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE FlexibleContexts, GADTs #-}
+{-# LANGUAGE RecordWildCards, OverloadedStrings #-}
+
+module Clang.Coformat.StyOpts where
+
+import qualified Data.ByteString.Lazy.Char8 as BSL
+import qualified Data.HashMap.Strict as HM
+import qualified Data.Text as T
+import Control.Lens
+import Data.Aeson
+import Data.Aeson.Lens(_Object)
+import Data.List
+import Data.Maybe
+import Data.Void
+
+import Clang.Format.Descr
+
+data StyOpts = StyOpts
+  { basedOnStyle :: T.Text
+  , additionalOpts :: [ConfigItemT 'Value]
+  }
+
+instance ToJSON StyOpts where
+  toJSON StyOpts { .. } = Object $ foldl' f (HM.singleton "BasedOnStyle" $ String basedOnStyle) additionalOpts
+    where
+      f hm ConfigItem { .. } = updateRec value name hm
+
+      updateRec value [key] = HM.insert key (toJson value)
+      updateRec value (key:rest) = HM.alter (Just . Object . updateRec value rest . getObj) key
+      updateRec _ [] = id
+      getObj = fromMaybe mempty . ((^? _Object) =<<)
+
+      toJson (CTInt n) = Number $ fromIntegral n
+      toJson (CTUnsigned n) = Number $ fromIntegral n
+      toJson (CTString v) = absurd v
+      toJson (CTStringVec v) = absurd v
+      toJson (CTRawStringFormats v) = absurd v
+      toJson (CTIncludeCats v) = absurd v
+      toJson (CTBool b) = Bool b
+      toJson (CTEnum _ opt) = String opt
+
+formatStyArg :: StyOpts -> BSL.ByteString
+formatStyArg = encode
diff --git a/src/Clang/Coformat/Util.hs b/src/Clang/Coformat/Util.hs
new file mode 100644
--- /dev/null
+++ b/src/Clang/Coformat/Util.hs
@@ -0,0 +1,78 @@
+{-# LANGUAGE FlexibleContexts, RankNTypes #-}
+{-# LANGUAGE DeriveGeneric, DeriveAnyClass #-}
+
+module Clang.Coformat.Util where
+
+import qualified Control.Concurrent.Async as A
+import qualified Control.Concurrent.Async.Pool as A.P
+import qualified Control.Monad.Except as E
+import qualified Control.Monad.Reader as R
+import qualified Data.Text.Lazy as TL
+import Control.Monad.Except.CoHas
+import Control.Monad.Logger
+import Control.Monad.Reader.Has
+import Data.Bifunctor
+import GHC.Generics
+import Numeric.Natural
+import System.Exit
+
+data ExpectedFailure = FormatterSegfaulted TL.Text   -- kek
+  deriving (Eq, Show)
+
+data UnexpectedFailure = FormatterFailure
+  { errorCode :: Int
+  , errorOutput :: TL.Text
+  } deriving (Eq, Show)
+
+data Failure = ExpectedFailure ExpectedFailure
+             | UnexpectedFailure UnexpectedFailure
+             deriving (Eq, Show, Generic, CoHas ExpectedFailure, CoHas UnexpectedFailure)
+
+failuresAreUnexpected :: Failure -> UnexpectedFailure
+failuresAreUnexpected (UnexpectedFailure err) = err
+failuresAreUnexpected (ExpectedFailure (FormatterSegfaulted out)) = FormatterFailure 0 out
+
+checked :: (MonadError err m, CoHas UnexpectedFailure err, CoHas ExpectedFailure err, MonadIO m)
+        => IO (ExitCode, TL.Text, TL.Text) -> m TL.Text
+checked act = do
+  (ec, stdout, stderr) <- liftIO act
+  case ec of ExitSuccess -> pure stdout
+             ExitFailure n | n == cfCrashRetCode -> throwError $ FormatterSegfaulted stderr
+                           | otherwise -> throwError $ FormatterFailure n stderr
+  where
+    cfCrashRetCode = -8
+
+update :: Int -> (a -> a) -> [a] -> [a]
+update idx f = zipWith z [0..]
+  where z idx' val = if idx' == idx then f val else val
+
+forConcurrently' :: (MonadLoggerIO m, MonadError e m)
+                 => [a]
+                 -> (forall m'. (MonadLoggerIO m', MonadError e m') => a -> m' b)
+                 -> m [b]
+forConcurrently' lst act = do
+  logger <- askLoggerIO
+  result <- liftIO $ A.forConcurrently lst $ \elt -> flip runLoggingT logger $ runExceptT $ act elt
+  E.liftEither $ sequence result
+
+forConcurrentlyPooled :: (MonadLoggerIO m, MonadError e m, MonadReader r m, Has A.P.TaskGroup r)
+                      => [a]
+                      -> (forall m'. (MonadLoggerIO m', MonadReader r m', MonadError e m') => a -> m' b)
+                      -> m [b]
+forConcurrentlyPooled lst act = do
+  logger <- askLoggerIO
+  tg <- ask
+  env <- R.ask
+  result <- liftIO $ flip (A.P.mapConcurrently tg) lst $ \elt -> flip runLoggingT logger $ runExceptT $ flip runReaderT env $ act elt
+  E.liftEither $ sequence result
+
+convert :: MonadError e' m
+        => (e -> e')
+        -> ExceptT e m a
+        -> m a
+convert cvt act = runExceptT act >>= (E.liftEither . first cvt)
+
+subsetsN :: Natural -> [a] -> [[a]]
+subsetsN _ [] = []
+subsetsN 1 xs = pure <$> xs
+subsetsN n (x:xs) = ((x:) <$> subsetsN (n - 1) xs) <> subsetsN n xs
diff --git a/src/Clang/Coformat/Variables.hs b/src/Clang/Coformat/Variables.hs
new file mode 100644
--- /dev/null
+++ b/src/Clang/Coformat/Variables.hs
@@ -0,0 +1,94 @@
+{-# LANGUAGE GADTs, TypeFamilies, DataKinds, TypeApplications, ConstraintKinds #-}
+{-# LANGUAGE FlexibleInstances, FlexibleContexts #-}
+{-# LANGUAGE LambdaCase #-}
+{-# OPTIONS_GHC -Wno-type-defaults #-}
+
+module Clang.Coformat.Variables where
+
+import qualified Data.Text as T
+import Control.Lens
+import Control.Monad
+import Data.Kind
+import Numeric.Natural
+
+import Clang.Format.Descr
+
+data KnownVariateType = Categorical | Integral
+
+class Variate a where
+  type VariateResult a :: Type -> Type
+  type VariateType a :: KnownVariateType
+  variate :: a -> VariateResult a a
+  varPrism :: Prism' (ConfigTypeT 'Value) a
+
+data Variable varTy where
+  -- TODO proxy should be enough
+  MkDV :: (Variate a, VariateType a ~ varTy, Foldable (VariateResult a)) => a -> Variable varTy
+
+data IxedVariable varTy = IxedVariable
+  { discreteVar :: Variable varTy
+  , varIdx :: Int
+  }
+
+type CategoricalVariate a = (Variate a, VariateType a ~ 'Categorical)
+type CategoricalVariable = Variable 'Categorical
+type IxedCategoricalVariable = IxedVariable 'Categorical
+
+
+instance Variate Bool where
+  type VariateResult Bool = []
+  type VariateType Bool = 'Categorical
+  variate b = [not b]
+  varPrism = prism' CTBool $ \case CTBool b -> Just b
+                                   _ -> Nothing
+
+type EnumVar = ([T.Text], T.Text)
+
+instance Variate EnumVar where
+  type VariateResult EnumVar = []
+  type VariateType EnumVar = 'Categorical
+  variate (vars, cur) = [(vars, next) | next <- vars, next /= cur]
+  varPrism = prism' (uncurry CTEnum) $ \case CTEnum vars cur -> Just (vars, cur)
+                                             _ -> Nothing
+
+typToDV :: ConfigTypeT 'Value -> Maybe CategoricalVariable
+typToDV val = msum [ MkDV <$> val ^? varPrism @Bool
+                   , MkDV <$> val ^? varPrism @([T.Text], T.Text)
+                   ]
+
+searchSpace :: Show a => Integral a => a -> [a]
+searchSpace n = [ 2 ^ k | k <- [ 0 .. maxK ] ]
+  where
+    nBase = round $ logBase 2 $ fromIntegral n
+    maxK | n > 100 = nBase + 1
+         | otherwise = 2 * nBase
+
+instance Variate Int where
+  type VariateResult Int = []
+  type VariateType Int = 'Integral
+  variate n = n - 1 : n + 1 : searchSpace n <> map negate (searchSpace n)
+  varPrism = prism' CTInt $ \case CTInt n -> Just n
+                                  _ -> Nothing
+
+instance Variate Natural where
+  type VariateResult Natural = []
+  type VariateType Natural = 'Integral
+  variate n | n > 0 = n - 1 : n + 1 : searchSpace n
+            | otherwise = n + 1 : searchSpace n
+  varPrism = prism' CTUnsigned $ \case CTUnsigned n -> Just n
+                                       _ -> Nothing
+
+type IntegralVariate a = (Variate a, VariateType a ~ 'Integral)
+type IntegralVariable = Variable 'Integral
+type IxedIntegralVariable = IxedVariable 'Integral
+
+typToIV :: ConfigTypeT 'Value -> Maybe IntegralVariable
+typToIV val = msum [ MkDV <$> val ^? varPrism @Int
+                   , MkDV <$> val ^? varPrism @Natural
+                   ]
+
+data SomeIxedVariable :: Type where
+  SomeIxedVariable :: IxedVariable varTy -> SomeIxedVariable
+
+asSome :: [IxedVariable varTy] -> [SomeIxedVariable]
+asSome = fmap SomeIxedVariable
diff --git a/src/Clang/Format/Descr.hs b/src/Clang/Format/Descr.hs
new file mode 100644
--- /dev/null
+++ b/src/Clang/Format/Descr.hs
@@ -0,0 +1,41 @@
+{-# LANGUAGE DataKinds, TypeFamilies #-}
+{-# LANGUAGE StandaloneDeriving, FlexibleInstances #-}
+{-# LANGUAGE Strict #-}
+
+module Clang.Format.Descr where
+
+import qualified Data.Text as T
+import Data.Void
+import Numeric.Natural
+
+data Stage = Parsed | Supported | Value
+
+type family CTData f ty where
+  CTData 'Parsed _ = ()
+  CTData 'Supported Void = Void
+  CTData 'Supported _ = ()
+  CTData 'Value ty = ty
+
+data ConfigTypeT f
+  = CTInt (CTData f Int)
+  | CTUnsigned (CTData f Natural)
+  | CTBool (CTData f Bool)
+  | CTString (CTData f Void)
+  | CTStringVec (CTData f Void)
+  | CTRawStringFormats (CTData f Void)
+  | CTIncludeCats (CTData f Void)
+  | CTEnum { variants :: [T.Text], enumValue :: CTData f T.Text }
+
+deriving instance Show (ConfigTypeT 'Parsed)
+deriving instance Show (ConfigTypeT 'Supported)
+deriving instance Show (ConfigTypeT 'Value)
+deriving instance Eq (ConfigTypeT 'Value)
+
+data ConfigItemT f = ConfigItem
+  { name :: [T.Text]
+  , value :: ConfigTypeT f
+  }
+
+deriving instance Show (ConfigItemT 'Parsed)
+deriving instance Show (ConfigItemT 'Supported)
+deriving instance Show (ConfigItemT 'Value)
diff --git a/src/Clang/Format/Descr/Operations.hs b/src/Clang/Format/Descr/Operations.hs
new file mode 100644
--- /dev/null
+++ b/src/Clang/Format/Descr/Operations.hs
@@ -0,0 +1,61 @@
+{-# LANGUAGE DataKinds, GADTs, ConstraintKinds #-}
+{-# LANGUAGE FlexibleContexts, ScopedTypeVariables #-}
+{-# LANGUAGE RecordWildCards, LambdaCase, QuasiQuotes, OverloadedStrings #-}
+
+module Clang.Format.Descr.Operations where
+
+import qualified Data.HashMap.Strict as HM
+import qualified Data.Map.Strict as M
+import qualified Data.Text as T
+import Control.Monad.Except
+import Data.Bifunctor
+import Data.Maybe
+import Data.String.Interpolate.IsString
+import Data.Typeable
+import Data.Void
+import Text.Read
+
+import Clang.Format.Descr
+
+filterParsedItems :: [ConfigItemT 'Parsed] -> [ConfigItemT 'Supported]
+filterParsedItems = mapMaybe $ \ConfigItem { .. } -> ConfigItem name <$> filterType value
+  where
+    filterType = \case
+                    CTInt () -> Just $ CTInt ()
+                    CTUnsigned () -> Just $ CTUnsigned ()
+                    CTBool () -> Just $ CTBool ()
+                    CTString () -> Nothing
+                    CTStringVec () -> Nothing
+                    CTRawStringFormats () -> Nothing
+                    CTIncludeCats () -> Nothing
+                    CTEnum vars () -> Just $ CTEnum vars ()
+
+replaceItemsWith :: [ConfigItemT 'Value] -> [ConfigItemT 'Value] -> [ConfigItemT 'Value]
+replaceItemsWith l1 l2 = M.elems $ toMap l2 <> toMap l1
+  where
+    toMap lst = M.fromList [ (name item, item) | item <- lst ]
+
+subtractMatching :: [ConfigItemT 'Value] -> [ConfigItemT 'Value] -> [ConfigItemT 'Value]
+subtractMatching minuend subtrahend = filter f minuend
+  where
+    f ConfigItem { .. } = (/= Just value) $ HM.lookup name subMap
+    subMap = HM.fromList [ (name, value) | ConfigItem { .. } <- subtrahend]
+
+type ParseableConfigState f = (CTData f Void ~ Void, Show (ConfigTypeT f))
+
+parseConfigValue :: (MonadError String m, ParseableConfigState f) => ConfigItemT f -> String -> m (ConfigItemT 'Value)
+parseConfigValue cfg str = liftEither $ (\parsed -> ConfigItem { name = name cfg, value = parsed }) <$> eitherParsed
+  where
+    eitherParsed = case value cfg of
+                        CTInt _ -> CTInt <$> readEither'
+                        CTUnsigned _ -> CTUnsigned <$> readEither'
+                        CTBool _ -> CTBool <$> readEither'
+                        CTString val -> absurd val
+                        CTStringVec val -> absurd val
+                        CTRawStringFormats val -> absurd val
+                        CTIncludeCats val -> absurd val
+                        CTEnum variants _ | var `elem` variants -> pure $ CTEnum variants var
+                                          | otherwise -> throwError [i|Unsupported option `#{var}`, supported ones are `#{T.intercalate "`, `" variants}`|]
+                            where var = T.pack str
+    readEither' :: forall a. (Typeable a, Read a) => Either String a
+    readEither' = first (\err -> [i|Error parsing #{str} as #{typeRep (Proxy :: Proxy a)}: #{err}, expected type: #{value cfg}|]) $ readEither str
diff --git a/src/Clang/Format/DescrParser.hs b/src/Clang/Format/DescrParser.hs
new file mode 100644
--- /dev/null
+++ b/src/Clang/Format/DescrParser.hs
@@ -0,0 +1,85 @@
+{-# LANGUAGE QuasiQuotes, ParallelListComp, RecordWildCards, OverloadedStrings #-}
+{-# LANGUAGE DataKinds, GADTs #-}
+
+module Clang.Format.DescrParser
+( parseFile
+, parseDescr
+) where
+
+import qualified Data.ByteString.Lazy.Char8 as LBS
+import qualified Data.Text as T
+import qualified Data.Text.Lazy as TL
+import Data.Char
+import Data.String.Interpolate
+import Control.Monad.Extra
+import Text.HTML.DOM
+import Text.XML hiding(parseLBS)
+import Text.XML.Cursor
+import Text.XML.Scraping
+import Text.XML.Selector.TH
+import Text.XML.Selector.Types
+
+import Clang.Format.Descr
+
+parseFile :: FilePath -> IO (Either String [ConfigItemT 'Parsed])
+parseFile = fmap parseDescr . LBS.readFile
+
+parseDescr :: LBS.ByteString -> Either String [ConfigItemT 'Parsed]
+parseDescr = parseCursor . fromDocument . parseLBS
+
+parseCursor :: Cursor -> Either String [ConfigItemT 'Parsed]
+parseCursor cur = do
+  items <- mapM parseItem [ (header, body) | header <- [jq|dl.docutils > dt|] `queryT` cur
+                                           | body   <- [jq|dl.docutils > dd|] `queryT` cur
+                                           ]
+  braceWrappingKludge items
+
+braceWrappingKludge :: [ConfigItemT 'Parsed] -> Either String [ConfigItemT 'Parsed]
+braceWrappingKludge = concatMapM f
+  where
+    f c@ConfigItem { .. } | name /= ["BraceWrapping"] = pure [c]
+    f ConfigItem { value = CTEnum { .. } } = pure [ ConfigItem { name = ["BraceWrapping", var], value = CTBool () }
+                                                  | var <- variants
+                                                  , isUpper $ T.head var
+                                                  ]
+    f _ = Left "Expected BraceWrapping to have (mis)type of CTEnum"
+
+parseItem :: (Cursor, Cursor) -> Either String (ConfigItemT 'Parsed)
+parseItem (header, body) = do
+  nameToken <- header @>. [jq|strong|]
+  typStr <- header @>. [jq|span.pre|]
+  value <- parseType nameToken typStr body
+  let name = [nameToken]
+  pure ConfigItem { .. }
+
+parseType :: T.Text -> T.Text -> Cursor -> Either String (ConfigTypeT 'Parsed)
+parseType name typStr cur
+  | Just typ <- lookup typStr variantless = pure typ
+  | otherwise = do
+      let allVars = TL.toStrict . innerText <$> [jq|li code.docutils > span.pre|] `queryT` cur
+      let unEnumed = [ T.tail rest
+                     | var <- allVars
+                     , let (_, rest) = T.break (== '_') var
+                     , not $ T.null rest
+                     ]
+      let variants = if null unEnumed then allVars else unEnumed
+      when (null variants) $ Left [i|no variants found for `#{typStr}` for `#{name}`|]
+      pure CTEnum { enumValue = (), .. }
+  where
+    variantless = [ ("int", CTInt ())
+                  , ("bool", CTBool ())
+                  , ("unsigned", CTUnsigned ())
+                  , ("std::string", CTString ())
+                  , ("std::vector<std::string>", CTStringVec ())
+                  , ("std::vector<RawStringFormat>", CTRawStringFormats ())
+                  , ("std::vector<IncludeCategory>", CTIncludeCats ())
+                  ]
+
+(@>) :: Cursor -> [JQSelector] -> Either String Cursor
+cur @> expr | (sub:_) <- queryT expr cur = pure sub
+            | NodeElement el <- node cur
+            , let el' = el { elementNodes = [] } = Left [i|nothing found for expression #{expr} under element #{el'}|]
+            | otherwise = Left [i|nothing found for expression #{expr}|]
+
+(@>.) :: Cursor -> [JQSelector] -> Either String T.Text
+cur @>. expr = TL.toStrict . innerText <$> cur @> expr
diff --git a/src/Clang/Format/YamlConversions.hs b/src/Clang/Format/YamlConversions.hs
new file mode 100644
--- /dev/null
+++ b/src/Clang/Format/YamlConversions.hs
@@ -0,0 +1,114 @@
+{-# LANGUAGE DataKinds, GADTs #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE DeriveGeneric, DeriveAnyClass #-}
+{-# LANGUAGE RecordWildCards, OverloadedStrings, LambdaCase #-}
+
+module Clang.Format.YamlConversions
+( fillConfigItems
+, FillError
+
+, YamlConfigType(..)
+, preprocessYaml
+
+, collectConfigItems
+
+, formatClangFormat
+) where
+
+import qualified Data.ByteString.Char8 as BS
+import qualified Data.Text as T
+import qualified Data.HashMap.Strict as HM
+import Control.Monad.Except.CoHas
+import Data.Either
+import Data.Scientific
+import Data.Yaml
+import Data.Yaml.Pretty
+import GHC.Generics
+
+import Clang.Format.Descr
+import Clang.Coformat.StyOpts
+
+data FillError
+  = YamlParseError ParseException
+  | YamlAnalysisError YamlAnalysisError
+  | YamlValueNotFound ValueNotFound
+  deriving (Show, Generic, CoHas ParseException, CoHas YamlAnalysisError, CoHas ValueNotFound)
+
+data YamlConfigType = StyleDump | PartialConfig
+
+preprocessYaml :: (MonadError e m, CoHas ParseException e, CoHas YamlAnalysisError e, CoHas ValueNotFound e)
+               => YamlConfigType -> BS.ByteString -> m Object
+preprocessYaml configType yamlContents = liftEither (decodeEither' yamlContents)
+                                     >>= extractMap
+                                     >>= bwWithFailure configType
+  where
+    bwWithFailure StyleDump = braceWrappingKludge >=> liftEither
+    bwWithFailure PartialConfig = \obj -> braceWrappingKludge obj
+                                      >>= \case Left _ -> pure obj
+                                                Right obj' -> pure obj'
+
+-- Errors out in case of missing items (note 'ValueNotFound' in the constraint).
+fillConfigItems :: (MonadError e m, CoHas ParseException e, CoHas YamlAnalysisError e, CoHas ValueNotFound e)
+                => [ConfigItemT 'Supported] -> BS.ByteString -> m [ConfigItemT 'Value]
+fillConfigItems supported yamlContents = preprocessYaml StyleDump yamlContents
+                                     >>= fillConfigItemsFromObj supported
+                                     >>= liftEither . sequence
+
+-- Drops missing items.
+collectConfigItems :: (MonadError e m, CoHas ParseException e, CoHas YamlAnalysisError e)
+                   => [ConfigItemT 'Supported] -> Object -> m [ConfigItemT 'Value]
+collectConfigItems supported yamlObject = rights <$> fillConfigItemsFromObj supported yamlObject
+
+data YamlAnalysisError
+  = YamlNotAnObject String
+  | IncompatibleValue T.Text (ConfigTypeT 'Supported) Value
+  deriving (Show)
+
+newtype ValueNotFound = ValueNotFound { missingValueName :: T.Text } deriving (Show)
+
+extractMap :: (MonadError e m, CoHas YamlAnalysisError e) => Value -> m Object
+extractMap (Object fields) = pure fields
+extractMap _ = throwError $ YamlNotAnObject "Top-level value is not an object"
+
+braceWrappingKludge :: (MonadError e m, CoHas YamlAnalysisError e) => Object -> m (Either ValueNotFound Object)
+braceWrappingKludge fields
+  | Nothing <- maybeBWVal = pure $ Left $ ValueNotFound bwField
+  | Just (Object obj) <- maybeBWVal = let bwSubfields = HM.fromList [ (bwField <> "." <> key, val)
+                                                                    | (key, val) <- HM.toList obj
+                                                                    ]
+                                      in pure $ Right $ HM.delete bwField fields <> bwSubfields
+  | otherwise = throwError $ YamlNotAnObject "Brace wrapping value is not an object"
+  where
+    bwField = "BraceWrapping"
+    maybeBWVal = HM.lookup bwField fields
+
+fillConfigItemsFromObj :: (MonadError e m, CoHas YamlAnalysisError e)
+                       => [ConfigItemT 'Supported] -> Object -> m [Either ValueNotFound (ConfigItemT 'Value)]
+fillConfigItemsFromObj supported fields = mapM fillConfigItem supported
+  where
+    fillConfigItem ConfigItem { .. }
+      | Nothing <- maybeYamlVal = pure $ Left $ ValueNotFound nameConcatted
+      | Just yamlVal <- maybeYamlVal = do
+        value' <- case (value, yamlVal) of
+                       (CTInt _, Number num)
+                           | Just int <- toBoundedInteger num -> pure $ CTInt int
+                       (CTUnsigned _, Number num)
+                           | Just int <- toBoundedInteger num :: Maybe Int
+                           , int >= 0 -> pure $ CTUnsigned $ fromIntegral int
+                       (CTBool _, Bool b) -> pure $ CTBool b
+                       (CTEnum vars _, String s)
+                           | s `elem` vars -> pure $ CTEnum vars s
+                       (CTEnum vars _, Bool b)
+                           | boolAsEnumVar b `elem` vars -> pure $ CTEnum vars $ boolAsEnumVar b
+                       _ -> throwError $ IncompatibleValue nameConcatted value yamlVal
+        pure $ Right $ ConfigItem { name = name, value = value' }
+      where
+        nameConcatted = T.intercalate "." name
+        maybeYamlVal = HM.lookup nameConcatted fields
+
+formatClangFormat :: StyOpts -> BS.ByteString
+formatClangFormat = encodePretty $ setConfCompare compare defConfig
+
+boolAsEnumVar :: Bool -> T.Text
+boolAsEnumVar True = "Yes"
+boolAsEnumVar False = "No"
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,2 @@
+main :: IO ()
+main = putStrLn "Test suite not yet implemented"
