-- | This module provides types and functions to generate .proto files.

{-# LANGUAGE CPP #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedLists            #-}
{-# LANGUAGE RecordWildCards            #-}
{-# LANGUAGE ScopedTypeVariables        #-}
{-# OPTIONS_GHC -fno-warn-orphans       #-}

module Proto3.Suite.DotProto.Rendering
  ( renderDotProto
  , defRenderingOptions
  , defSelectorName
  , defEnumMemberName
  , packageFromDefs
  , toProtoFile
  , toProtoFileDef
  , RenderingOptions(..)
  , Pretty(..)
  ) where

import           Data.Char
import qualified Data.List.NonEmpty              as NE
import qualified Data.Text                       as T
import           Filesystem.Path.CurrentOS       (toText)
#if (MIN_VERSION_base(4,11,0))
import           Prelude                         hiding ((<>))
#endif
import           Proto3.Suite.DotProto.AST
import           Proto3.Wire.Types               (FieldNumber (..))
import           Text.PrettyPrint                (($$), (<+>), (<>))
import qualified Text.PrettyPrint                as PP
import           Text.PrettyPrint.HughesPJClass  (Pretty(..))

-- | Options for rendering a @.proto@ file.
data RenderingOptions = RenderingOptions
  { RenderingOptions
-> DotProtoIdentifier -> DotProtoIdentifier -> FieldNumber -> Doc
roSelectorName   :: DotProtoIdentifier -> DotProtoIdentifier -> FieldNumber -> PP.Doc
  -- ^ This function will be applied to each
  -- record selector name to turn it into a protobuf
  -- field name (default: uses the selector name, unchanged).
  , RenderingOptions -> DotProtoIdentifier -> DotProtoIdentifier -> Doc
roEnumMemberName :: DotProtoIdentifier -> DotProtoIdentifier -> PP.Doc
  -- ^ This function will be applied to each
  -- enum member name to turn it into a protobuf
  -- field name (default: uses the field name, unchanged).
  }

-- | Default rendering options.
defRenderingOptions :: RenderingOptions
defRenderingOptions :: RenderingOptions
defRenderingOptions =
    RenderingOptions :: (DotProtoIdentifier -> DotProtoIdentifier -> FieldNumber -> Doc)
-> (DotProtoIdentifier -> DotProtoIdentifier -> Doc)
-> RenderingOptions
RenderingOptions { roSelectorName :: DotProtoIdentifier -> DotProtoIdentifier -> FieldNumber -> Doc
roSelectorName   = DotProtoIdentifier -> DotProtoIdentifier -> FieldNumber -> Doc
defSelectorName
                     , roEnumMemberName :: DotProtoIdentifier -> DotProtoIdentifier -> Doc
roEnumMemberName = DotProtoIdentifier -> DotProtoIdentifier -> Doc
defEnumMemberName
                     }

-- | The default choice of field name for a selector.
defSelectorName :: DotProtoIdentifier -> DotProtoIdentifier -> FieldNumber -> PP.Doc
defSelectorName :: DotProtoIdentifier -> DotProtoIdentifier -> FieldNumber -> Doc
defSelectorName DotProtoIdentifier
_ DotProtoIdentifier
fieldName FieldNumber
_ = DotProtoIdentifier -> Doc
forall a. Pretty a => a -> Doc
pPrint DotProtoIdentifier
fieldName

-- | The default choice of enum member name for an enum
defEnumMemberName :: DotProtoIdentifier -> DotProtoIdentifier -> PP.Doc
defEnumMemberName :: DotProtoIdentifier -> DotProtoIdentifier -> Doc
defEnumMemberName = (DotProtoIdentifier -> Doc)
-> DotProtoIdentifier -> DotProtoIdentifier -> Doc
forall a b. a -> b -> a
const DotProtoIdentifier -> Doc
forall a. Pretty a => a -> Doc
pPrint

-- | Traverses a DotProto AST and generates a .proto file from it
renderDotProto :: RenderingOptions -> DotProto -> PP.Doc
renderDotProto :: RenderingOptions -> DotProto -> Doc
renderDotProto RenderingOptions
opts DotProto{[DotProtoDefinition]
[DotProtoOption]
[DotProtoImport]
DotProtoMeta
DotProtoPackageSpec
protoMeta :: DotProto -> DotProtoMeta
protoDefinitions :: DotProto -> [DotProtoDefinition]
protoPackage :: DotProto -> DotProtoPackageSpec
protoOptions :: DotProto -> [DotProtoOption]
protoImports :: DotProto -> [DotProtoImport]
protoMeta :: DotProtoMeta
protoDefinitions :: [DotProtoDefinition]
protoPackage :: DotProtoPackageSpec
protoOptions :: [DotProtoOption]
protoImports :: [DotProtoImport]
..}
  = String -> Doc
PP.text String
"syntax = \"proto3\";"
 Doc -> Doc -> Doc
$$ DotProtoPackageSpec -> Doc
forall a. Pretty a => a -> Doc
pPrint DotProtoPackageSpec
protoPackage
 Doc -> Doc -> Doc
$$ ([Doc] -> Doc
PP.vcat ([Doc] -> Doc) -> [Doc] -> Doc
forall a b. (a -> b) -> a -> b
$ DotProtoImport -> Doc
forall a. Pretty a => a -> Doc
pPrint    (DotProtoImport -> Doc) -> [DotProtoImport] -> [Doc]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [DotProtoImport]
protoImports)
 Doc -> Doc -> Doc
$$ ([Doc] -> Doc
PP.vcat ([Doc] -> Doc) -> [Doc] -> Doc
forall a b. (a -> b) -> a -> b
$ DotProtoOption -> Doc
topOption (DotProtoOption -> Doc) -> [DotProtoOption] -> [Doc]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [DotProtoOption]
protoOptions)
 Doc -> Doc -> Doc
$$ ([Doc] -> Doc
PP.vcat ([Doc] -> Doc) -> [Doc] -> Doc
forall a b. (a -> b) -> a -> b
$ RenderingOptions -> DotProtoDefinition -> Doc
prettyPrintProtoDefinition RenderingOptions
opts (DotProtoDefinition -> Doc) -> [DotProtoDefinition] -> [Doc]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [DotProtoDefinition]
protoDefinitions)

instance Pretty DotProtoPackageSpec where
  pPrint :: DotProtoPackageSpec -> Doc
pPrint (DotProtoPackageSpec DotProtoIdentifier
p) = String -> Doc
PP.text String
"package" Doc -> Doc -> Doc
<+> DotProtoIdentifier -> Doc
forall a. Pretty a => a -> Doc
pPrint DotProtoIdentifier
p Doc -> Doc -> Doc
<> String -> Doc
PP.text String
";"
  pPrint (DotProtoPackageSpec
DotProtoNoPackage)     = Doc
PP.empty

instance Pretty DotProtoImport where
  pPrint :: DotProtoImport -> Doc
pPrint (DotProtoImport DotProtoImportQualifier
q FilePath
i) =
    String -> Doc
PP.text String
"import" Doc -> Doc -> Doc
<+> DotProtoImportQualifier -> Doc
forall a. Pretty a => a -> Doc
pPrint DotProtoImportQualifier
q Doc -> Doc -> Doc
<+> String -> Doc
PP.text String
fp Doc -> Doc -> Doc
<> String -> Doc
PP.text String
";"
    where
      fp :: String
fp = case Text -> String
T.unpack (Text -> String) -> (FilePath -> Text) -> FilePath -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Text -> Text) -> (Text -> Text) -> Either Text Text -> Text
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either Text -> Text
forall a. a -> a
id Text -> Text
forall a. a -> a
id (Either Text Text -> Text)
-> (FilePath -> Either Text Text) -> FilePath -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FilePath -> Either Text Text
toText (FilePath -> String) -> FilePath -> String
forall a b. (a -> b) -> a -> b
$ FilePath
i of
             [] -> String -> String
forall a. Show a => a -> String
show (String
"" :: String)
             String
x  -> String
x

instance Pretty DotProtoImportQualifier where
  pPrint :: DotProtoImportQualifier -> Doc
pPrint DotProtoImportQualifier
DotProtoImportDefault = Doc
PP.empty
  pPrint DotProtoImportQualifier
DotProtoImportPublic  = String -> Doc
PP.text String
"public"
  pPrint DotProtoImportQualifier
DotProtoImportWeak    = String -> Doc
PP.text String
"weak"

optionAnnotation :: [DotProtoOption] -> PP.Doc
optionAnnotation :: [DotProtoOption] -> Doc
optionAnnotation [] = Doc
PP.empty
optionAnnotation [DotProtoOption]
os = Doc -> Doc
PP.brackets
                    (Doc -> Doc) -> ([Doc] -> Doc) -> [Doc] -> Doc
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Doc] -> Doc
PP.hcat
                    ([Doc] -> Doc) -> ([Doc] -> [Doc]) -> [Doc] -> Doc
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Doc -> [Doc] -> [Doc]
PP.punctuate (String -> Doc
PP.text String
", ")
                    ([Doc] -> Doc) -> [Doc] -> Doc
forall a b. (a -> b) -> a -> b
$ DotProtoOption -> Doc
forall a. Pretty a => a -> Doc
pPrint (DotProtoOption -> Doc) -> [DotProtoOption] -> [Doc]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [DotProtoOption]
os

topOption :: DotProtoOption -> PP.Doc
topOption :: DotProtoOption -> Doc
topOption DotProtoOption
o = String -> Doc
PP.text String
"option" Doc -> Doc -> Doc
<+> DotProtoOption -> Doc
forall a. Pretty a => a -> Doc
pPrint DotProtoOption
o Doc -> Doc -> Doc
<> String -> Doc
PP.text String
";"

instance Pretty DotProtoOption where
  pPrint :: DotProtoOption -> Doc
pPrint (DotProtoOption DotProtoIdentifier
key DotProtoValue
value) = DotProtoIdentifier -> Doc
forall a. Pretty a => a -> Doc
pPrint DotProtoIdentifier
key Doc -> Doc -> Doc
<+> String -> Doc
PP.text String
"=" Doc -> Doc -> Doc
<+> DotProtoValue -> Doc
forall a. Pretty a => a -> Doc
pPrint DotProtoValue
value

renderComment :: String -> PP.Doc
renderComment :: String -> Doc
renderComment = [Doc] -> Doc
PP.vcat ([Doc] -> Doc) -> (String -> [Doc]) -> String -> Doc
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (String -> Doc) -> [String] -> [Doc]
forall a b. (a -> b) -> [a] -> [b]
map ((String -> Doc
PP.text String
"//" Doc -> Doc -> Doc
<+>) (Doc -> Doc) -> (String -> Doc) -> String -> Doc
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Doc
textIfNonempty) ([String] -> [Doc]) -> (String -> [String]) -> String -> [Doc]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> [String]
lines
  where
    textIfNonempty :: String -> Doc
textIfNonempty [] = Doc
PP.empty
    textIfNonempty String
text = String -> Doc
PP.text String
text

-- Put the final closing brace on the next line.
-- This is important, since the final field might have a comment, and
-- the brace cannot be part of the comment.
-- We could use block comments instead, once the parser/lexer supports them.
vbraces :: PP.Doc -> PP.Doc -> PP.Doc
vbraces :: Doc -> Doc -> Doc
vbraces Doc
header Doc
body = Doc
header Doc -> Doc -> Doc
<+> Char -> Doc
PP.char Char
'{' Doc -> Doc -> Doc
$$ Int -> Doc -> Doc
PP.nest Int
2 Doc
body Doc -> Doc -> Doc
$$ Char -> Doc
PP.char Char
'}'

prettyPrintProtoDefinition :: RenderingOptions -> DotProtoDefinition -> PP.Doc
prettyPrintProtoDefinition :: RenderingOptions -> DotProtoDefinition -> Doc
prettyPrintProtoDefinition RenderingOptions
opts = DotProtoDefinition -> Doc
defn where
  defn :: DotProtoDefinition -> PP.Doc
  defn :: DotProtoDefinition -> Doc
defn (DotProtoMessage String
comment DotProtoIdentifier
name [DotProtoMessagePart]
parts) = String -> Doc
renderComment String
comment Doc -> Doc -> Doc
$$
    Doc -> Doc -> Doc
vbraces (String -> Doc
PP.text String
"message" Doc -> Doc -> Doc
<+> DotProtoIdentifier -> Doc
forall a. Pretty a => a -> Doc
pPrint DotProtoIdentifier
name) ([Doc] -> Doc
PP.vcat ([Doc] -> Doc) -> [Doc] -> Doc
forall a b. (a -> b) -> a -> b
$ DotProtoIdentifier -> DotProtoMessagePart -> Doc
msgPart DotProtoIdentifier
name (DotProtoMessagePart -> Doc) -> [DotProtoMessagePart] -> [Doc]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [DotProtoMessagePart]
parts)
  defn (DotProtoEnum    String
comment DotProtoIdentifier
name [DotProtoEnumPart]
parts) = String -> Doc
renderComment String
comment Doc -> Doc -> Doc
$$
    Doc -> Doc -> Doc
vbraces (String -> Doc
PP.text String
"enum"    Doc -> Doc -> Doc
<+> DotProtoIdentifier -> Doc
forall a. Pretty a => a -> Doc
pPrint DotProtoIdentifier
name) ([Doc] -> Doc
PP.vcat ([Doc] -> Doc) -> [Doc] -> Doc
forall a b. (a -> b) -> a -> b
$ DotProtoIdentifier -> DotProtoEnumPart -> Doc
enumPart DotProtoIdentifier
name (DotProtoEnumPart -> Doc) -> [DotProtoEnumPart] -> [Doc]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [DotProtoEnumPart]
parts)
  defn (DotProtoService String
comment DotProtoIdentifier
name [DotProtoServicePart]
parts) = String -> Doc
renderComment String
comment Doc -> Doc -> Doc
$$
    Doc -> Doc -> Doc
vbraces (String -> Doc
PP.text String
"service" Doc -> Doc -> Doc
<+> DotProtoIdentifier -> Doc
forall a. Pretty a => a -> Doc
pPrint DotProtoIdentifier
name) ([Doc] -> Doc
PP.vcat ([Doc] -> Doc) -> [Doc] -> Doc
forall a b. (a -> b) -> a -> b
$ DotProtoServicePart -> Doc
forall a. Pretty a => a -> Doc
pPrint (DotProtoServicePart -> Doc) -> [DotProtoServicePart] -> [Doc]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [DotProtoServicePart]
parts)

  msgPart :: DotProtoIdentifier -> DotProtoMessagePart -> PP.Doc
  msgPart :: DotProtoIdentifier -> DotProtoMessagePart -> Doc
msgPart DotProtoIdentifier
msgName (DotProtoMessageField DotProtoField
f)           = DotProtoIdentifier -> DotProtoField -> Doc
field DotProtoIdentifier
msgName DotProtoField
f
  msgPart DotProtoIdentifier
_       (DotProtoMessageDefinition DotProtoDefinition
definition) = DotProtoDefinition -> Doc
defn DotProtoDefinition
definition
  msgPart DotProtoIdentifier
_       (DotProtoMessageReserved [DotProtoReservedField]
reservations)
    =   String -> Doc
PP.text String
"reserved"
    Doc -> Doc -> Doc
<+> ([Doc] -> Doc
PP.hcat ([Doc] -> Doc) -> ([Doc] -> [Doc]) -> [Doc] -> Doc
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Doc -> [Doc] -> [Doc]
PP.punctuate (String -> Doc
PP.text String
", ") ([Doc] -> Doc) -> [Doc] -> Doc
forall a b. (a -> b) -> a -> b
$ DotProtoReservedField -> Doc
forall a. Pretty a => a -> Doc
pPrint (DotProtoReservedField -> Doc) -> [DotProtoReservedField] -> [Doc]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [DotProtoReservedField]
reservations)
    Doc -> Doc -> Doc
<>  String -> Doc
PP.text String
";"
  msgPart DotProtoIdentifier
msgName (DotProtoMessageOneOf DotProtoIdentifier
name [DotProtoField]
fields)     = Doc -> Doc -> Doc
vbraces (String -> Doc
PP.text String
"oneof" Doc -> Doc -> Doc
<+> DotProtoIdentifier -> Doc
forall a. Pretty a => a -> Doc
pPrint DotProtoIdentifier
name) ([Doc] -> Doc
PP.vcat ([Doc] -> Doc) -> [Doc] -> Doc
forall a b. (a -> b) -> a -> b
$ DotProtoIdentifier -> DotProtoField -> Doc
field DotProtoIdentifier
msgName (DotProtoField -> Doc) -> [DotProtoField] -> [Doc]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [DotProtoField]
fields)

  field :: DotProtoIdentifier -> DotProtoField -> PP.Doc
  field :: DotProtoIdentifier -> DotProtoField -> Doc
field DotProtoIdentifier
msgName (DotProtoField FieldNumber
number DotProtoType
mtype DotProtoIdentifier
name [DotProtoOption]
options String
comments)
    =   DotProtoType -> Doc
forall a. Pretty a => a -> Doc
pPrint DotProtoType
mtype
    Doc -> Doc -> Doc
<+> RenderingOptions
-> DotProtoIdentifier -> DotProtoIdentifier -> FieldNumber -> Doc
roSelectorName RenderingOptions
opts DotProtoIdentifier
msgName DotProtoIdentifier
name FieldNumber
number
    Doc -> Doc -> Doc
<+> String -> Doc
PP.text String
"="
    Doc -> Doc -> Doc
<+> FieldNumber -> Doc
forall a. Pretty a => a -> Doc
pPrint FieldNumber
number
    Doc -> Doc -> Doc
<+> [DotProtoOption] -> Doc
optionAnnotation [DotProtoOption]
options
    Doc -> Doc -> Doc
<>  String -> Doc
PP.text String
";"
    Doc -> Doc -> Doc
$$  Int -> Doc -> Doc
PP.nest Int
2 (String -> Doc
renderComment String
comments)
  field DotProtoIdentifier
_ DotProtoField
DotProtoEmptyField = Doc
PP.empty

  enumPart :: DotProtoIdentifier -> DotProtoEnumPart -> PP.Doc
  enumPart :: DotProtoIdentifier -> DotProtoEnumPart -> Doc
enumPart DotProtoIdentifier
msgName (DotProtoEnumField DotProtoIdentifier
name DotProtoEnumValue
value [DotProtoOption]
options)
    = RenderingOptions -> DotProtoIdentifier -> DotProtoIdentifier -> Doc
roEnumMemberName RenderingOptions
opts DotProtoIdentifier
msgName DotProtoIdentifier
name
    Doc -> Doc -> Doc
<+> String -> Doc
PP.text String
"="
    Doc -> Doc -> Doc
<+> Int -> Doc
forall a. Pretty a => a -> Doc
pPrint (DotProtoEnumValue -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral DotProtoEnumValue
value :: Int)
    Doc -> Doc -> Doc
<+> [DotProtoOption] -> Doc
optionAnnotation [DotProtoOption]
options
    Doc -> Doc -> Doc
<> String -> Doc
PP.text String
";"
  enumPart DotProtoIdentifier
_       (DotProtoEnumOption DotProtoOption
opt)
    = String -> Doc
PP.text String
"option" Doc -> Doc -> Doc
<+> DotProtoOption -> Doc
forall a. Pretty a => a -> Doc
pPrint DotProtoOption
opt Doc -> Doc -> Doc
<> String -> Doc
PP.text String
";"
  enumPart DotProtoIdentifier
_       DotProtoEnumPart
DotProtoEnumEmpty
    = Doc
PP.empty

instance Pretty DotProtoServicePart where
  pPrint :: DotProtoServicePart -> Doc
pPrint (DotProtoServiceRPCMethod RPCMethod{[DotProtoOption]
Streaming
DotProtoIdentifier
rpcMethodOptions :: RPCMethod -> [DotProtoOption]
rpcMethodResponseStreaming :: RPCMethod -> Streaming
rpcMethodResponseType :: RPCMethod -> DotProtoIdentifier
rpcMethodRequestStreaming :: RPCMethod -> Streaming
rpcMethodRequestType :: RPCMethod -> DotProtoIdentifier
rpcMethodName :: RPCMethod -> DotProtoIdentifier
rpcMethodOptions :: [DotProtoOption]
rpcMethodResponseStreaming :: Streaming
rpcMethodResponseType :: DotProtoIdentifier
rpcMethodRequestStreaming :: Streaming
rpcMethodRequestType :: DotProtoIdentifier
rpcMethodName :: DotProtoIdentifier
..})
    =   String -> Doc
PP.text String
"rpc"
    Doc -> Doc -> Doc
<+> DotProtoIdentifier -> Doc
forall a. Pretty a => a -> Doc
pPrint DotProtoIdentifier
rpcMethodName
    Doc -> Doc -> Doc
<+> Doc -> Doc
PP.parens (Streaming -> Doc
forall a. Pretty a => a -> Doc
pPrint Streaming
rpcMethodRequestStreaming Doc -> Doc -> Doc
<+> DotProtoIdentifier -> Doc
forall a. Pretty a => a -> Doc
pPrint DotProtoIdentifier
rpcMethodRequestType)
    Doc -> Doc -> Doc
<+> String -> Doc
PP.text String
"returns"
    Doc -> Doc -> Doc
<+> Doc -> Doc
PP.parens (Streaming -> Doc
forall a. Pretty a => a -> Doc
pPrint Streaming
rpcMethodResponseStreaming Doc -> Doc -> Doc
<+> DotProtoIdentifier -> Doc
forall a. Pretty a => a -> Doc
pPrint DotProtoIdentifier
rpcMethodResponseType)
    Doc -> Doc -> Doc
<+> case [DotProtoOption]
rpcMethodOptions of
          [] -> String -> Doc
PP.text String
";"
          [DotProtoOption]
_  -> Doc -> Doc
PP.braces (Doc -> Doc) -> ([Doc] -> Doc) -> [Doc] -> Doc
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Doc] -> Doc
PP.vcat ([Doc] -> Doc) -> [Doc] -> Doc
forall a b. (a -> b) -> a -> b
$ DotProtoOption -> Doc
topOption (DotProtoOption -> Doc) -> [DotProtoOption] -> [Doc]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> [DotProtoOption]
rpcMethodOptions
  pPrint (DotProtoServiceOption DotProtoOption
option) = DotProtoOption -> Doc
topOption DotProtoOption
option
  pPrint DotProtoServicePart
DotProtoServiceEmpty           = Doc
PP.empty

instance Pretty Streaming where
  pPrint :: Streaming -> Doc
pPrint Streaming
Streaming    = String -> Doc
PP.text String
"stream"
  pPrint Streaming
NonStreaming = Doc
PP.empty

instance Pretty DotProtoIdentifier where
  pPrint :: DotProtoIdentifier -> Doc
pPrint (Single String
name)                    = String -> Doc
PP.text String
name
  pPrint (Dots (Path NonEmpty String
names))              = [Doc] -> Doc
PP.hcat ([Doc] -> Doc) -> ([Doc] -> [Doc]) -> [Doc] -> Doc
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Doc -> [Doc] -> [Doc]
PP.punctuate (String -> Doc
PP.text String
".") ([Doc] -> Doc) -> [Doc] -> Doc
forall a b. (a -> b) -> a -> b
$ String -> Doc
PP.text (String -> Doc) -> [String] -> [Doc]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> NonEmpty String -> [String]
forall a. NonEmpty a -> [a]
NE.toList NonEmpty String
names
  pPrint (Qualified DotProtoIdentifier
qualifier DotProtoIdentifier
identifier) = Doc -> Doc
PP.parens (DotProtoIdentifier -> Doc
forall a. Pretty a => a -> Doc
pPrint DotProtoIdentifier
qualifier) Doc -> Doc -> Doc
<> String -> Doc
PP.text String
"." Doc -> Doc -> Doc
<> DotProtoIdentifier -> Doc
forall a. Pretty a => a -> Doc
pPrint DotProtoIdentifier
identifier
  pPrint DotProtoIdentifier
Anonymous                        = Doc
PP.empty

instance Pretty DotProtoValue where
  pPrint :: DotProtoValue -> Doc
pPrint (Identifier DotProtoIdentifier
value) = DotProtoIdentifier -> Doc
forall a. Pretty a => a -> Doc
pPrint DotProtoIdentifier
value
  pPrint (StringLit  String
value) = String -> Doc
PP.text (String -> Doc) -> String -> Doc
forall a b. (a -> b) -> a -> b
$ String -> String
forall a. Show a => a -> String
show String
value
  pPrint (IntLit     Int
value) = String -> Doc
PP.text (String -> Doc) -> String -> Doc
forall a b. (a -> b) -> a -> b
$ Int -> String
forall a. Show a => a -> String
show Int
value
  pPrint (FloatLit   Double
value) = String -> Doc
PP.text (String -> Doc) -> String -> Doc
forall a b. (a -> b) -> a -> b
$ Double -> String
forall a. Show a => a -> String
show Double
value
  pPrint (BoolLit    Bool
value) = String -> Doc
PP.text (String -> Doc) -> String -> Doc
forall a b. (a -> b) -> a -> b
$ Char -> Char
toLower (Char -> Char) -> String -> String
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Bool -> String
forall a. Show a => a -> String
show Bool
value

instance Pretty DotProtoType where
  pPrint :: DotProtoType -> Doc
pPrint (Prim           DotProtoPrimType
ty) = DotProtoPrimType -> Doc
forall a. Pretty a => a -> Doc
pPrint DotProtoPrimType
ty
  pPrint (Optional       DotProtoPrimType
ty) = DotProtoPrimType -> Doc
forall a. Pretty a => a -> Doc
pPrint DotProtoPrimType
ty
  pPrint (Repeated       DotProtoPrimType
ty) = String -> Doc
PP.text String
"repeated" Doc -> Doc -> Doc
<+> DotProtoPrimType -> Doc
forall a. Pretty a => a -> Doc
pPrint DotProtoPrimType
ty
  pPrint (NestedRepeated DotProtoPrimType
ty) = String -> Doc
PP.text String
"repeated" Doc -> Doc -> Doc
<+> DotProtoPrimType -> Doc
forall a. Pretty a => a -> Doc
pPrint DotProtoPrimType
ty
  pPrint (Map DotProtoPrimType
keyty DotProtoPrimType
valuety) = String -> Doc
PP.text String
"map<" Doc -> Doc -> Doc
<> DotProtoPrimType -> Doc
forall a. Pretty a => a -> Doc
pPrint DotProtoPrimType
keyty Doc -> Doc -> Doc
<> String -> Doc
PP.text String
", " Doc -> Doc -> Doc
<> DotProtoPrimType -> Doc
forall a. Pretty a => a -> Doc
pPrint DotProtoPrimType
valuety Doc -> Doc -> Doc
<> String -> Doc
PP.text String
">"

instance Pretty DotProtoPrimType where
  pPrint :: DotProtoPrimType -> Doc
pPrint (Named DotProtoIdentifier
i)  = DotProtoIdentifier -> Doc
forall a. Pretty a => a -> Doc
pPrint DotProtoIdentifier
i
  pPrint DotProtoPrimType
Int32      = String -> Doc
PP.text String
"int32"
  pPrint DotProtoPrimType
Int64      = String -> Doc
PP.text String
"int64"
  pPrint DotProtoPrimType
SInt32     = String -> Doc
PP.text String
"sint32"
  pPrint DotProtoPrimType
SInt64     = String -> Doc
PP.text String
"sint64"
  pPrint DotProtoPrimType
UInt32     = String -> Doc
PP.text String
"uint32"
  pPrint DotProtoPrimType
UInt64     = String -> Doc
PP.text String
"uint64"
  pPrint DotProtoPrimType
Fixed32    = String -> Doc
PP.text String
"fixed32"
  pPrint DotProtoPrimType
Fixed64    = String -> Doc
PP.text String
"fixed64"
  pPrint DotProtoPrimType
SFixed32   = String -> Doc
PP.text String
"sfixed32"
  pPrint DotProtoPrimType
SFixed64   = String -> Doc
PP.text String
"sfixed64"
  pPrint DotProtoPrimType
String     = String -> Doc
PP.text String
"string"
  pPrint DotProtoPrimType
Bytes      = String -> Doc
PP.text String
"bytes"
  pPrint DotProtoPrimType
Bool       = String -> Doc
PP.text String
"bool"
  pPrint DotProtoPrimType
Float      = String -> Doc
PP.text String
"float"
  pPrint DotProtoPrimType
Double     = String -> Doc
PP.text String
"double"

instance Pretty FieldNumber where
  pPrint :: FieldNumber -> Doc
pPrint = String -> Doc
PP.text (String -> Doc) -> (FieldNumber -> String) -> FieldNumber -> Doc
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word64 -> String
forall a. Show a => a -> String
show (Word64 -> String)
-> (FieldNumber -> Word64) -> FieldNumber -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. FieldNumber -> Word64
getFieldNumber

instance Pretty DotProtoReservedField where
  pPrint :: DotProtoReservedField -> Doc
pPrint (SingleField Int
num)      = String -> Doc
PP.text (String -> Doc) -> String -> Doc
forall a b. (a -> b) -> a -> b
$ Int -> String
forall a. Show a => a -> String
show Int
num
  pPrint (FieldRange Int
start Int
end) = (String -> Doc
PP.text (String -> Doc) -> String -> Doc
forall a b. (a -> b) -> a -> b
$ Int -> String
forall a. Show a => a -> String
show Int
start) Doc -> Doc -> Doc
<+> String -> Doc
PP.text String
"to" Doc -> Doc -> Doc
<+> (String -> Doc
PP.text (String -> Doc) -> String -> Doc
forall a b. (a -> b) -> a -> b
$ Int -> String
forall a. Show a => a -> String
show Int
end)
  pPrint (ReservedIdentifier String
i) = String -> Doc
PP.text (String -> Doc) -> String -> Doc
forall a b. (a -> b) -> a -> b
$ String -> String
forall a. Show a => a -> String
show String
i

-- | Render protobufs metadata as a .proto file stringy
toProtoFile :: RenderingOptions -> DotProto -> String
toProtoFile :: RenderingOptions -> DotProto -> String
toProtoFile RenderingOptions
opts = Doc -> String
PP.render (Doc -> String) -> (DotProto -> Doc) -> DotProto -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. RenderingOptions -> DotProto -> Doc
renderDotProto RenderingOptions
opts

-- | Render protobufs metadata as a .proto file string,
-- using the default rendering options.

toProtoFileDef :: DotProto -> String
toProtoFileDef :: DotProto -> String
toProtoFileDef = RenderingOptions -> DotProto -> String
toProtoFile RenderingOptions
defRenderingOptions

packageFromDefs :: String -> [DotProtoDefinition] -> DotProto
packageFromDefs :: String -> [DotProtoDefinition] -> DotProto
packageFromDefs String
package [DotProtoDefinition]
defs =
  [DotProtoImport]
-> [DotProtoOption]
-> DotProtoPackageSpec
-> [DotProtoDefinition]
-> DotProtoMeta
-> DotProto
DotProto [] [] (DotProtoIdentifier -> DotProtoPackageSpec
DotProtoPackageSpec (DotProtoIdentifier -> DotProtoPackageSpec)
-> DotProtoIdentifier -> DotProtoPackageSpec
forall a b. (a -> b) -> a -> b
$ String -> DotProtoIdentifier
Single String
package) [DotProtoDefinition]
defs (Path -> DotProtoMeta
DotProtoMeta Path
fakePath)