module Nixfmt
( errorBundlePretty
, ParseErrorBundle
, format
, formatVerify
) where
import Data.Bifunctor (bimap, first)
import Data.Text (Text)
import qualified Text.Megaparsec as Megaparsec (parse)
import Text.Megaparsec.Error (errorBundlePretty)
import Nixfmt.Parser (file)
import Nixfmt.Predoc (layout)
import Nixfmt.Pretty ()
import Nixfmt.Types (ParseErrorBundle)
type Width = Int
format :: Width -> FilePath -> Text -> Either String Text
format :: Width -> FilePath -> Text -> Either FilePath Text
format Width
width FilePath
filename
= (ParseErrorBundle Text Void -> FilePath)
-> (File -> Text)
-> Either (ParseErrorBundle Text Void) File
-> Either FilePath Text
forall (p :: * -> * -> *) a b c d.
Bifunctor p =>
(a -> b) -> (c -> d) -> p a c -> p b d
bimap ParseErrorBundle Text Void -> FilePath
forall s e.
(VisualStream s, TraversableStream s, ShowErrorComponent e) =>
ParseErrorBundle s e -> FilePath
errorBundlePretty (Width -> File -> Text
forall a. Pretty a => Width -> a -> Text
layout Width
width)
(Either (ParseErrorBundle Text Void) File -> Either FilePath Text)
-> (Text -> Either (ParseErrorBundle Text Void) File)
-> Text
-> Either FilePath Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Parsec Void Text File
-> FilePath -> Text -> Either (ParseErrorBundle Text Void) File
forall e s a.
Parsec e s a -> FilePath -> s -> Either (ParseErrorBundle s e) a
Megaparsec.parse Parsec Void Text File
file FilePath
filename
formatVerify :: Width -> FilePath -> Text -> Either String Text
formatVerify :: Width -> FilePath -> Text -> Either FilePath Text
formatVerify Width
width FilePath
path Text
unformatted = do
File
unformattedParsed <- Text -> Either FilePath File
parse Text
unformatted
let formattedOnce :: Text
formattedOnce = Width -> File -> Text
forall a. Pretty a => Width -> a -> Text
layout Width
width File
unformattedParsed
File
formattedOnceParsed <- Text -> Either FilePath File
parse Text
formattedOnce
let formattedTwice :: Text
formattedTwice = Width -> File -> Text
forall a. Pretty a => Width -> a -> Text
layout Width
width File
formattedOnceParsed
if File
formattedOnceParsed File -> File -> Bool
forall a. Eq a => a -> a -> Bool
/= File
unformattedParsed
then FilePath -> Either FilePath Text
forall b. FilePath -> Either FilePath b
pleaseReport FilePath
"Parses differently after formatting."
else if Text
formattedOnce Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
/= Text
formattedTwice
then FilePath -> Either FilePath Text
forall b. FilePath -> Either FilePath b
pleaseReport FilePath
"Nixfmt is not idempotent."
else Text -> Either FilePath Text
forall a b. b -> Either a b
Right Text
formattedOnce
where
parse :: Text -> Either FilePath File
parse = (ParseErrorBundle Text Void -> FilePath)
-> Either (ParseErrorBundle Text Void) File -> Either FilePath File
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first ParseErrorBundle Text Void -> FilePath
forall s e.
(VisualStream s, TraversableStream s, ShowErrorComponent e) =>
ParseErrorBundle s e -> FilePath
errorBundlePretty (Either (ParseErrorBundle Text Void) File -> Either FilePath File)
-> (Text -> Either (ParseErrorBundle Text Void) File)
-> Text
-> Either FilePath File
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Parsec Void Text File
-> FilePath -> Text -> Either (ParseErrorBundle Text Void) File
forall e s a.
Parsec e s a -> FilePath -> s -> Either (ParseErrorBundle s e) a
Megaparsec.parse Parsec Void Text File
file FilePath
path
pleaseReport :: FilePath -> Either FilePath b
pleaseReport FilePath
x = FilePath -> Either FilePath b
forall a b. a -> Either a b
Left (FilePath -> Either FilePath b) -> FilePath -> Either FilePath b
forall a b. (a -> b) -> a -> b
$ FilePath
path FilePath -> FilePath -> FilePath
forall a. Semigroup a => a -> a -> a
<> FilePath
": " FilePath -> FilePath -> FilePath
forall a. Semigroup a => a -> a -> a
<> FilePath
x FilePath -> FilePath -> FilePath
forall a. Semigroup a => a -> a -> a
<> FilePath
" This is a bug in nixfmt. Please report it at https://github.com/serokell/nixfmt"