Something that can print the current value or field name of a symbol.
I’m my code I need to do something like:
type order = {
isProcessed: bool,
contact: contactPerson,
created: milliseconds,
tickets: array<ticket>,
food: foodCount,
}
await orderSnapshot->update_field("isProcessed", true)
To update a single value in my Firebase database, I need to pass the name of my field as a string.
It would be great if I could do something like await orderSnapshot->update_field(nameof(isProcessed), true)
Some time later, I’m not super excited anymore about this solution. My nameof operator, it always needs a type annotation like nameof((o: Domain.Firebase.order) => o.isProcessed) and I don’t find it very ergonomic.
I’m wondering if ppx would not be convenient to generate string constants of my fields instead.
%something
type foo = {
x: int,
y: string,
bar: array<string>
}
and generate
// Not sure about casing, but you get the idea
module FooKeys = {
let X = "x"
let Y = "y"
let BAR = "bar"
}
Would such a PPX thing be possible in ReScript? I would guess, AST wise it is not that hard to construct.
My latest shot at this is by using the rescript-tools doc command. (from @rescript/tools)
In a simple script I get the json ast from a file, traverse the nodes a bit and generate some simple code.
open Tools_Docgen
@scope(("import", "meta")) @val external importMetaDir: string = "dir"
let currentDir = Path.resolve([importMetaDir])
let domainFile = Path.join([currentDir, "..", "functions", "src", "Domain.res"])
let json = await (sh`bun rescript-tools doc ${domainFile}`)->ShellPromise.json
let doc = decodeFromJson(json)
let domainFirebase = doc.items->Array.find(item => {
switch item {
| Module({id: "Domain.Firebase"}) => true
| _ => false
}
})
let records = switch domainFirebase {
| Some(Module({items})) =>
items->ArrayX.choose(item => {
switch item {
| Type({name, detail: Record({items: fields})}) => {
let fieldNames = fields->Array.map(f => f.name)
Some(name, fieldNames)
}
| _ => None
}
})
| _ => []
}
let keysFile = Path.join([Path.dirname(domainFile), "Keys.res"])
let uppercase = (value: string) => {
let capital = value->String.substring(~start=0, ~end=1)->String.toUpperCase
let rest = String.substringToEnd(~start=1, value)
`${capital}${rest}`
}
let contents =
records
->Array.map(((name, properties)) => {
let keys = properties->Array.map(p => `let ${p} = "${p}"`)->Array.join("\n")
`module ${uppercase(name)} = {
${keys}
}`
})
->Array.join("\n\n")
->Bun.Write.Input.fromString
let _ = await Bun.Write.write(
~destination=Bun.Write.Destination.fromPath(keysFile),
~input=contents,
)
let _ = await sh`bun rescript format ${keysFile}`
Console.log("Keys.res was generated")