@@ -5,26 +5,42 @@ import Whine.Runner.Prelude
55import Codec.JSON.DecodeError as DecodeError
66import Control.Monad.Error.Class (throwError )
77import Control.Monad.Writer (runWriter )
8- import Data.Array.NonEmpty as NEA
98import Data.Codec as Codec
109import Data.Codec.JSON as CJ
1110import Data.Codec.JSON.Common as CJ.Common
1211import Data.Codec.JSON.Strict as CJS
1312import Data.Map as Map
13+ import Data.String.NonEmpty as NES
1414import Effect.Exception as Err
1515import JSON as JSON
1616import Node.FS.Sync (readTextFile )
1717import Record (merge )
18- import Whine.Types ( RuleFactories , RuleSet , Violations , WithFile , WithRule )
18+ import Whine.Runner.Glob ( Globs )
1919import Whine.Runner.Yaml (parseYaml )
20+ import Whine.Types (RuleFactories , RuleId , Violations , WithFile , WithRule , Rule )
2021
2122data PackageSpec
2223 = JustPackage
2324 | PackageVersion String
2425 | LocalPackage { path :: FilePath , module :: Maybe String }
2526
26- type Config =
27+ type Config m =
28+ { rules :: RuleSet m
29+ , files :: Globs
30+ }
31+
32+ -- | For every rule ID we have a rule implementation `Rule m` and some common
33+ -- | config values that can be applied to any rule and are handled by the
34+ -- | framework.
35+ type RuleSet m = Map RuleId
36+ { rule :: Rule m
37+ , globs :: Globs
38+ }
39+
40+ type ConfigJson =
2741 { rulePackages :: Map { package :: String } PackageSpec
42+ , include :: Maybe (Array NonEmptyString )
43+ , exclude :: Maybe (Array NonEmptyString )
2844 , rules :: Maybe (Map String JSON )
2945 }
3046
@@ -54,8 +70,8 @@ type Config =
5470-- | The whole thing runs in a writer monad to which it can report any errors
5571-- | while parsing the config. The errors doesn't stop the parsing, but
5672-- | erroneous rules do not make it into the resulting map.
57- parseConfig :: ∀ m n . MonadWriter (Violations (WithRule + ())) m => RuleFactories n -> Map String JSON -> m (RuleSet n )
58- parseConfig factories config =
73+ parseRuleConfigs :: ∀ m n . MonadWriter (Violations (WithRule + ())) m => RuleFactories n -> Map String JSON -> m (RuleSet n )
74+ parseRuleConfigs factories config =
5975 Map .fromFoldable <$> catMaybes <$>
6076 for factories \(ruleId /\ factory) -> do
6177
@@ -77,17 +93,17 @@ parseConfig factories config =
7793 Just value ->
7894 value # CJ .decode codec # lmap DecodeError .print # orFail (" Malformed '" <> name <> " '" )
7995
80- include <- commonProp " include" (CJ .array CJ .string )
81- exclude <- commonProp " exclude" (CJ .array CJ .string )
96+ include <- commonProp " include" (CJ .array CJ.Common .nonEmptyString )
97+ exclude <- commonProp " exclude" (CJ .array CJ.Common .nonEmptyString )
8298 enabled <- commonProp " enabled" CJ .boolean <#> fromMaybe true
8399
84100 if enabled then
85101 factory (fromMaybe JSON .null ruleConfig)
86102 <#> (\rule -> ruleId /\
87103 { rule
88104 , globs:
89- { include: include >>= NEA .fromArray
90- , exclude: exclude >>= NEA .fromArray
105+ { include: include # fromMaybe []
106+ , exclude: exclude # fromMaybe []
91107 }
92108 }
93109 )
@@ -96,7 +112,7 @@ parseConfig factories config =
96112 pure Nothing
97113
98114-- | Reads config from a given file and parses it.
99- readConfig :: ∀ m n . MonadEffect m => MonadWriter (Violations (WithRule + WithFile + ())) m => RuleFactories n -> FilePath -> m (RuleSet n )
115+ readConfig :: ∀ m n . MonadEffect m => MonadWriter (Violations (WithRule + WithFile + ())) m => RuleFactories n -> FilePath -> m (Config n )
100116readConfig factories configFile = do
101117 text <- lmap Err .message <$> liftEffect (try $ readTextFile UTF8 configFile)
102118
@@ -114,14 +130,22 @@ readConfig factories configFile = do
114130 Right c ->
115131 pure c
116132
117- let res /\ violations = runWriter $ parseConfig factories (config.rules # fromMaybe Map .empty)
133+ let res /\ violations = runWriter $ parseRuleConfigs factories (config.rules # fromMaybe Map .empty)
118134
119135 tell $ violations <#> merge { file: { path: configFile, lines: Nothing } }
120- pure res
121-
122- configCodec :: CJ.Codec Config
136+ pure
137+ { rules: res
138+ , files:
139+ { include: config.include # fromMaybe (NES .fromString `mapMaybe` [" src/**/*.purs" , " test/**/*.purs" ])
140+ , exclude: config.exclude # fromMaybe []
141+ }
142+ }
143+
144+ configCodec :: CJ.Codec ConfigJson
123145configCodec = CJS .objectStrict $ CJS .record
124146 # CJS .recordProp @" rulePackages" packagesCodec
147+ # CJS .recordPropOptional @" include" (CJ .array CJ.Common .nonEmptyString)
148+ # CJS .recordPropOptional @" exclude" (CJ .array CJ.Common .nonEmptyString)
125149 # CJS .recordPropOptional @" rules" (CJ.Common .strMap CJ .json)
126150
127151packagesCodec :: CJ.Codec (Map { package :: String } PackageSpec )
@@ -157,8 +181,10 @@ packagesCodec = dimap Map.toUnfoldable Map.fromFoldable $ CJ.array packageCodec
157181 # CJS .recordProp @" local" CJ .string
158182 # CJS .recordPropOptional @" module" CJ .string
159183
160- defaultConfig :: Config
184+ defaultConfig :: ConfigJson
161185defaultConfig =
162186 { rulePackages: Map .singleton { package: " whine-core" } JustPackage
187+ , include: Nothing
188+ , exclude: Nothing
163189 , rules: Nothing
164190 }
0 commit comments