Open jakub-nlx opened 1 month ago
Actually I prototyped the following library for elm-syntax:
with this library, the problem turned quite nicely into a decoder/parser sort of scenario which was quite nice and flexible:
module DesignSystemUpgrade exposing (rule)
import Elm.CodeGen as CG
import Elm.Syntax.Expression exposing (Expression(..))
import Elm.Syntax.Node exposing (Node(..))
import List.Extra
import Syntax.Match as Match
import Upgrade
disabledInteraction =
Match.functionOrValue [] "Disabled"
|> Match.map (\_ -> Nothing)
pendingInteraction =
Match.functionOrValue [] "Pending"
|> Match.map (\_ -> Nothing)
ignoredInteractions =
Match.oneOf [ disabledInteraction, pendingInteraction ]
clickInteraction =
Match.call1 (Match.functionOrValue [] "OnClick") Match.any
|> Match.map (\( _, tag ) -> Just (CG.apply [ CG.fqVal [ "DesignSystem", "MainButton" ] "onClick", tag ]))
hrefInteraction =
Match.call1 (Match.functionOrValue [] "Href") Match.any
|> Match.map (\( _, tag ) -> Just (CG.apply [ CG.fqVal [ "DesignSystem", "MainButton" ] "linkTo", tag ]))
meaningfulInteraction =
Match.oneOf [ clickInteraction, hrefInteraction ]
baseIf =
Match.ifBlock Match.any meaningfulInteraction ignoredInteractions
|> Match.map
(\( condition, interaction_, _ ) ->
Maybe.map (\interaction -> CG.apply [ CG.fqVal [ "Attr" ] "if_", condition, interaction ]) interaction_
)
invertIf =
Match.ifBlock Match.any ignoredInteractions meaningfulInteraction
|> Match.map
(\( condition, _, interaction ) ->
Maybe.map (\int -> CG.apply [ CG.fqVal [ "Attr" ] "if_", CG.apply [ CG.fqVal [] "not", condition ], int ]) interaction
)
ifThenElse1 =
Match.ifBlock Match.any meaningfulInteraction (Match.ifBlock Match.any ignoredInteractions ignoredInteractions)
|> Match.map
(\( firstCond, a_, ( secondCond, _, _ ) ) ->
Maybe.map
(\a ->
CG.apply [ CG.fqVal [ "Attr" ] "if_", firstCond, a ]
)
a_
)
ifThenElse2 =
Match.ifBlock Match.any ignoredInteractions (Match.ifBlock Match.any meaningfulInteraction meaningfulInteraction)
|> Match.map
(\( firstCond, _, ( secondCond, b_, _ ) ) ->
Maybe.map (\b -> CG.apply [ CG.fqVal [ "Attr" ] "if_", CG.applyBinOp (CG.apply [ CG.fqVal [] "not", firstCond ]) CG.and secondCond, b ]) b_
)
ifThenElse3 =
Match.ifBlock Match.any ignoredInteractions (Match.ifBlock Match.any ignoredInteractions meaningfulInteraction)
|> Match.map
(\( firstCond, _, ( secondCond, _, c_ ) ) ->
Maybe.map (\c -> CG.apply [ CG.fqVal [ "Attr" ] "if_", CG.apply [ CG.fqVal [] "not", CG.applyBinOp firstCond CG.or secondCond ], c ]) c_
)
compositeMatch =
Match.oneOf
[ ifThenElse3
, ifThenElse2
, ifThenElse1
, invertIf
, baseIf
, meaningfulInteraction
, ignoredInteractions
]
|> Match.map
(\v ->
case v of
Just int ->
CG.list [ int ]
Nothing ->
CG.list []
)
parser =
Match.map3
(\label interaction isDanger ->
Upgrade.call
( "DesignSystem.MainButton"
, if isDanger then
"danger"
else
"primary"
)
[ interaction, CG.parens label ]
)
(Match.recordField "label" Match.any)
(Match.recordField "interaction" compositeMatch)
(Match.recordField "danger" Match.booleanLiteral)
rule =
Upgrade.rule
[ Upgrade.application
{ oldName = ( "Ui.Input", "button" )
, oldArgumentNames = [ "config" ]
, oldArgumentsToNew =
\arguments ->
case arguments of
[ rec ] ->
Match.match rec parser
_ ->
Nothing
}
]
So I wonder if we could provide a library like that?
We're trying to update some legacy APIs to our new fancy APIs.
For instance the following legacy API:
To the new
MainButton
. One thing where this package could assist is in helping to deconstruct record arguments, as that can be quite tricky. At the moment I'm doing something like:which is kind of OK, but probably could be made nicer.
The other issue which is tricky in updating this automatically is handling conditionals.
We have a lot of code that looks like:
which we'd like to upgrade to:
This gets pretty tricky pretty quickly. I wonder if what this needs is a more powerful pattern matching library for elm-syntax?