11module Orb.Main
22 ( main
3+ , mainWithOptions
34 , mainParserInfo
45 , mainParser
56 , mainParserWithCommands
7+ , openApiOptionsParser
68 , openApiLabelArgument
79 , generateOpenApiCommand
810 , generateOpenApiMain
911 ) where
1012
1113import Data.Aeson.Encode.Pretty qualified as AesonPretty
1214import Data.ByteString.Lazy qualified as LBS
15+ import Data.Foldable (traverse_ )
1316import Data.List qualified as List
17+ import Data.Set qualified as Set
1418import Options.Applicative qualified as Opt
1519import System.Exit qualified as Exit
1620import System.IO qualified as IO
@@ -30,25 +34,64 @@ import Orb.OpenApi qualified as OpenApi
3034 of the labeled routes as JSON to stdout.
3135-}
3236main :: OpenApi. OpenApiRouter a -> IO () -> IO ()
33- main routes appMain = do
34- io <- Opt. customExecParser parserPrefs (mainParserInfo appMain routes)
37+ main =
38+ mainWithOptions OpenApi. defaultOpenApiOptions
39+
40+ {- |
41+ Constructs a main function that parses the command line arguments and
42+ provides two subcommands in the executable:
43+
44+ - @api@ - runs the IO action provided as the main. Presumably this
45+ invokes some form of 'Orb.Wai.runOrb' (or otherwise runs a way server)
46+ that serves an application handling the routes provided to this function.
47+
48+ - @generate-open-api@ - accepts a argument matching one of the labels
49+ provided to 'OpenApi.provideOpenApi' and prints an OpenApi description
50+ of the labeled routes as JSON to stdout.
51+
52+
53+ A version of 'main' takes an 'OpenApi.OpenApiOptions'
54+ value to use as the default options to the @generate-open-api@
55+ command. If you use this function, you probably also want
56+ to use 'Orb.SwaggerUI.swaggerUIRoutesWithOptions' instead of
57+ 'Orb.SwaggerUI.swaggerUIRoutes' and specify the same options that
58+ you're passing to this function.
59+ -}
60+ mainWithOptions :: OpenApi. OpenApiOptions -> OpenApi. OpenApiRouter a -> IO () -> IO ()
61+ mainWithOptions defaultOptions routes appMain = do
62+ io <- Opt. customExecParser parserPrefs (mainParserInfo defaultOptions appMain routes)
3563 io
3664
3765{- |
3866 Constructs a 'Opt.ParserInfo' that will execute as description in 'main', but
3967 can be used as an argument to 'Opt.command' to use it as a subcommand.
68+
69+ The options passed will be used as the default values for when no options
70+ are specified on the command line.
4071-}
41- mainParserInfo :: IO () -> OpenApi. OpenApiRouter a -> Opt. ParserInfo (IO () )
42- mainParserInfo apiMain routes =
43- Opt. info (mainParser apiMain routes) mempty
72+ mainParserInfo ::
73+ OpenApi. OpenApiOptions ->
74+ IO () ->
75+ OpenApi. OpenApiRouter a ->
76+ Opt. ParserInfo (IO () )
77+ mainParserInfo defaultOptions apiMain routes =
78+ Opt. info (mainParser defaultOptions apiMain routes) mempty
4479
4580{- |
4681 Constructs a 'Opt.Parser' that will execute as description in 'main', but
4782 can be used directly in other option parsers.
83+
84+ The options passed will be used as the default values for when no options
85+ are specified on the command line.
4886-}
49- mainParser :: IO () -> OpenApi. OpenApiRouter a -> Opt. Parser (IO () )
50- mainParser apiMain =
87+ mainParser ::
88+ OpenApi. OpenApiOptions ->
89+ IO () ->
90+ OpenApi. OpenApiRouter a ->
91+ Opt. Parser (IO () )
92+ mainParser defaultOptions apiMain =
5193 mainParserWithCommands
94+ defaultOptions
5295 [ Opt. command " api" (Opt. info (pure apiMain) mempty )
5396 ]
5497
@@ -57,15 +100,19 @@ mainParser apiMain =
57100 the 'main' function along with the other commands passed. In this case no
58101 @api@ command is added by orb. It is up to the application to passed
59102 whatever set of other commands it desires.
103+
104+ The options passed will be used as the default values for when no options
105+ are specified on the command line.
60106-}
61107mainParserWithCommands ::
108+ OpenApi. OpenApiOptions ->
62109 [Opt. Mod Opt. CommandFields (IO () )] ->
63110 OpenApi. OpenApiRouter a ->
64111 Opt. Parser (IO () )
65- mainParserWithCommands commands routes =
112+ mainParserWithCommands defaultOptions commands routes =
66113 Opt. hsubparser $
67114 mconcat
68- ( generateOpenApiCommand routes
115+ ( generateOpenApiCommandWithOptions defaultOptions routes
69116 : commands
70117 )
71118
@@ -74,9 +121,67 @@ mainParserWithCommands commands routes =
74121 can be used along with 'Opt.hsubparser' include the command wherever the user
75122 chooses in their options parsing.
76123-}
77- generateOpenApiCommand :: OpenApi. OpenApiRouter a -> Opt. Mod Opt. CommandFields (IO () )
124+ generateOpenApiCommand ::
125+ OpenApi. OpenApiRouter a ->
126+ Opt. Mod Opt. CommandFields (IO () )
78127generateOpenApiCommand routes =
79- Opt. command " generate-open-api" (Opt. info (generateOpenApiMain routes <$> openApiLabelArgument routes) mempty )
128+ let
129+ parser =
130+ generateOpenApiMain
131+ <$> openApiOptionsParser OpenApi. defaultOpenApiOptions
132+ <*> pure routes
133+ <*> openApiLabelArgument routes
134+ in
135+ Opt. command " generate-open-api" (Opt. info parser mempty )
136+
137+ {- |
138+ Constructs an 'Opt.command' modifier for the @generate-open-api@ command that
139+ can be used along with 'Opt.hsubparser' include the command wherever the user
140+ chooses in their options parsing.
141+
142+ The options passed will be used as the default values for when no options
143+ are specified on the command line.
144+
145+ A version of 'generateOpenApiCommand' that takes the options argument.
146+ -}
147+ generateOpenApiCommandWithOptions ::
148+ OpenApi. OpenApiOptions ->
149+ OpenApi. OpenApiRouter a ->
150+ Opt. Mod Opt. CommandFields (IO () )
151+ generateOpenApiCommandWithOptions defaultOptions routes =
152+ let
153+ parser =
154+ generateOpenApiMain
155+ <$> openApiOptionsParser defaultOptions
156+ <*> pure routes
157+ <*> openApiLabelArgument routes
158+ in
159+ Opt. command " generate-open-api" (Opt. info parser mempty )
160+
161+ {- |
162+ Constructs a 'Opt.Parser' that will parse command line options to control
163+ how the OpenApi spec is generated.
164+
165+ The options passed will be used as the default values for when no options
166+ are specified on the command line.
167+ -}
168+ openApiOptionsParser :: OpenApi. OpenApiOptions -> Opt. Parser OpenApi. OpenApiOptions
169+ openApiOptionsParser defaultOptions =
170+ let
171+ mkOpts allowedChars =
172+ OpenApi. defaultOpenApiOptions
173+ { OpenApi. openApiAllowedSchemaNameChars = Set. fromList allowedChars
174+ }
175+ in
176+ mkOpts
177+ <$> Opt. option
178+ Opt. str
179+ ( Opt. long " allowed-chars"
180+ <> Opt. metavar " CHARS"
181+ <> Opt. help " e.g. abcdefg12345"
182+ <> Opt. value (Set. toList (OpenApi. openApiAllowedSchemaNameChars defaultOptions))
183+ <> Opt. showDefault
184+ )
80185
81186{- |
82187 Constructs a 'Opt.Parser' than will parse an argument representing one
@@ -110,12 +215,12 @@ parserPrefs =
110215 the @generate-open-api@ command in their own main functions without
111216 using @optparse-applicative@.
112217-}
113- generateOpenApiMain :: OpenApi. OpenApiRouter a -> String -> IO ()
114- generateOpenApiMain routes label =
115- case OpenApi. mkOpenApi routes label of
116- Left err -> do
218+ generateOpenApiMain :: OpenApi. OpenApiOptions -> OpenApi. OpenApiRouter a -> String -> IO ()
219+ generateOpenApiMain options routes label =
220+ case OpenApi. mkOpenApi options routes label of
221+ Left errs -> do
117222 IO. hPutStrLn IO. stderr (" Unable to generate OpenApi Spec for " <> label <> " !" )
118- IO. hPutStrLn IO. stderr err
223+ traverse_ ( IO. hPutStrLn IO. stderr . OpenApi. renderOpenApiError) errs
119224 Exit. exitWith (Exit. ExitFailure 1 )
120225 Right openApi ->
121226 LBS. putStr (AesonPretty. encodePretty openApi)
0 commit comments