@@ -4,51 +4,127 @@ module CLI.Commands (
44where
55
66import CLI.Types
7- import Control.Monad.Except (runExceptT )
87import Control.Monad.Reader (runReaderT )
8+ import Control.Monad.Validate (runValidate )
99import Data.Aeson (encode )
1010import Data.Aeson.Encode.Pretty (encodePretty )
1111import Data.ByteString.Lazy qualified as BL
12+ import Data.List.NonEmpty qualified as NE
1213import Data.Maybe (fromMaybe )
14+ import Data.Monoid (Last (.. ))
1315import Data.Text qualified as T
1416import Prettyprinter (Doc , annotate , hardline , pipe , pretty , vcat , vsep , (<+>) )
1517import Prettyprinter.Render.Terminal (AnsiStyle , Color (.. ), bold , color , hPutDoc , putDoc )
1618import System.Exit (exitFailure )
1719import System.IO (hPutStrLn , stderr )
20+ import TerranixCodegen.Config (
21+ Config ,
22+ PartialConfig ,
23+ ValidationError (.. ),
24+ completeConfig ,
25+ defaultConfig ,
26+ emptyConfig ,
27+ getOutputDirectory ,
28+ mergeConfigs ,
29+ readConfigFile ,
30+ readEnvConfig ,
31+ pattern PartialConfig ,
32+ )
1833import TerranixCodegen.FileOrganizer
1934import TerranixCodegen.PrettyPrint
2035import TerranixCodegen.ProviderSchema
2136import TerranixCodegen.ProviderSpec
22- import TerranixCodegen.TerraformGenerator (GeneratorConfig (.. ), TerraformError (.. ), defaultGeneratorConfig , extractSchemaFromProviders )
37+ import TerranixCodegen.TerraformGenerator (TerraformError (.. ), extractSchemaFromProviders , generatorConfigFromComplete )
38+
39+ {- | Load and merge configuration from multiple sources
40+
41+ Precedence (highest to lowest):
42+ 1. CLI arguments
43+ 2. Environment variables
44+ 3. Config file
45+ 4. Defaults
46+ -}
47+ loadCompleteConfig ::
48+ Maybe FilePath -> -- Config file path
49+ Maybe FilePath -> -- CLI terraform executable override
50+ Maybe FilePath -> -- CLI output directory override
51+ IO Config
52+ loadCompleteConfig configFilePath cliTfExe cliOutput = do
53+ -- Load config file if specified
54+ fileConfig <- case configFilePath of
55+ Nothing -> pure emptyConfig
56+ Just path -> do
57+ result <- readConfigFile path
58+ case result of
59+ Left err -> do
60+ hPutDoc stderr $
61+ vsep
62+ [ annotate (color Red <> bold) " Error:" <+> " Failed to read config file"
63+ , hardline
64+ , pretty err
65+ , mempty
66+ ]
67+ exitFailure
68+ Right config -> pure config
69+
70+ -- Load environment variables
71+ envConfig <- readEnvConfig
72+
73+ -- Build CLI config from arguments
74+ let cliConfig = PartialConfig cliTfExe cliOutput
75+
76+ -- Merge all configs with proper precedence
77+ -- Later configs override earlier ones, with defaults providing base values
78+ case runValidate (completeConfig [defaultConfig, fileConfig, envConfig, cliConfig]) of
79+ Left errors -> do
80+ hPutDoc stderr $
81+ vsep
82+ [ annotate (color Red <> bold) " Error:" <+> " Missing required configuration fields"
83+ , hardline
84+ , vcat $ NE. toList $ fmap formatConfigError errors
85+ , hardline
86+ , " This should not happen if default config is properly merged."
87+ , mempty
88+ ]
89+ exitFailure
90+ Right cfg -> pure cfg
91+ where
92+ formatConfigError :: ValidationError -> Doc AnsiStyle
93+ formatConfigError (MissingField _typeName field) =
94+ " •" <+> annotate (color Yellow ) (pretty field) <+> " is required but was not provided"
2395
2496-- | Execute a command
2597runCommand :: Command -> IO ()
2698runCommand cmd = case cmd of
27- Generate input output printSchema tfExe -> do
28- schemas <- loadSchemas tfExe input
99+ Generate input output printSchema tfExe configFile -> do
100+ config <- loadCompleteConfig configFile tfExe (Just output)
101+ schemas <- loadSchemasWithConfig config input
29102 if printSchema
30103 then do
31104 putDoc $ prettyProviderSchemas schemas
32105 hPutStrLn stderr " Done"
33106 else do
34- hPutStrLn stderr $ " Generating modules to: " <> output
35- organizeFiles output schemas
107+ let outDir = getOutputDirectory config
108+ hPutStrLn stderr $ " Generating modules to: " <> outDir
109+ organizeFiles outDir schemas
36110 hPutStrLn stderr " ✓ Module generation complete!"
37- Show input tfExe -> do
38- schemas <- loadSchemas tfExe input
111+ Show input tfExe configFile -> do
112+ config <- loadCompleteConfig configFile tfExe Nothing
113+ schemas <- loadSchemasWithConfig config input
39114 putDoc $ prettyProviderSchemas schemas
40- ExtractSchema input prettyJson tfExe -> do
41- schemas <- loadSchemas tfExe input
115+ ExtractSchema input prettyJson tfExe configFile -> do
116+ config <- loadCompleteConfig configFile tfExe Nothing
117+ schemas <- loadSchemasWithConfig config input
42118 let jsonOutput
43119 | prettyJson = encodePretty schemas
44120 | otherwise = encode schemas
45121 BL. putStr jsonOutput
46122
47- -- | Load schemas from various input sources
48- loadSchemas :: Maybe FilePath -> SchemaInput -> IO ProviderSchemas
49- loadSchemas tfExe input = case input of
123+ -- | Load schemas from various input sources using complete configuration
124+ loadSchemasWithConfig :: Config -> SchemaInput -> IO ProviderSchemas
125+ loadSchemasWithConfig config input = case input of
50126 FromFile maybePath -> loadFromFile maybePath
51- FromProviderSpecs specs -> loadFromProviderSpecs tfExe specs
127+ FromProviderSpecs specs -> loadFromProviderSpecs config specs
52128 FromProvidersFile path -> loadFromProvidersFile path
53129
54130-- | Load schemas from a file or stdin
@@ -74,8 +150,8 @@ loadFromFile maybePath = do
74150 pure schemas
75151
76152-- | Load schemas by generating minimal Terraform from provider specs
77- loadFromProviderSpecs :: Maybe FilePath -> [T. Text ] -> IO ProviderSchemas
78- loadFromProviderSpecs tfExe specTexts = do
153+ loadFromProviderSpecs :: Config -> [T. Text ] -> IO ProviderSchemas
154+ loadFromProviderSpecs completeConfig specTexts = do
79155 -- Parse provider specifications
80156 specs <- case mapM parseProviderSpecText specTexts of
81157 Left err -> do
@@ -97,11 +173,9 @@ loadFromProviderSpecs tfExe specTexts = do
97173 , mempty
98174 ]
99175
100- -- Extract schemas using Terraform with custom executable if provided
101- let config = case tfExe of
102- Nothing -> defaultGeneratorConfig
103- Just exe -> defaultGeneratorConfig {terraformExecutable = exe}
104- result <- runExceptT $ runReaderT (extractSchemaFromProviders specs) config
176+ -- Extract schemas using Terraform with the complete config
177+ let genConfig = generatorConfigFromComplete completeConfig
178+ result <- runExceptT $ runReaderT (extractSchemaFromProviders specs) genConfig
105179 case result of
106180 Left err -> do
107181 hPutDoc stderr $ formatTerraformError err
0 commit comments