Skip to content

Commit e7251a0

Browse files
github-actions[bot]Repo AssistCopilotdsyme
authored
[Repo Assist] Add With* methods to CsvProvider Row and JsonProvider record types (closes #1431) (#1639)
* Add With* methods to CsvProvider Row and JsonProvider record types (closes #1431) - CsvGenerator.fs: Add With<PropName>(newValue) methods to Row type that return a new row with one field replaced, using tuple construction for multi-column CSVs or direct value return for single-column CSVs - JsonRuntime.fs: Add WithRecordProperty(doc, name, value, cultureStr) helper that updates a named property in a JSON record document, returning a new IJsonDocument with the modified field - JsonGenerator.fs: Generate With<PropName> methods for each JSON record property, calling JsonRuntime.WithRecordProperty with the original raw property name. Methods are generated in the same block as constructors (GenerateConstructors flag) - Tests: Added 6 tests covering With* methods for both CsvProvider and JsonProvider, verifying correct value updates and immutability of the original record Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * ci: trigger CI checks * Resolve merge conflicts with main: integrate OmitNullFields into With* PR Main merged the OmitNullFields PR (#1638) after this branch was created. Resolving conflicts by integrating both sets of changes: - JsonGenerator.fs: add OmitNullFields param + keep With* method generation - JsonProvider.fs: accept new OmitNullFields static parameter - JsonRuntime.fs: add both WithRecordProperty and CreateRecordOmitNulls - TypeProviderInstantiation.fs: update test struct with OmitNullFields - JsonProvider tests: include OmitNullFields tests alongside With* tests Build: 0 errors. Tests: 268/268 passed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * ci: trigger CI checks * Fix blank line before CreateRecordOmitNulls in JsonRuntime.fs Add missing blank line to match main branch formatting and ensure GitHub merge conflict evaluation resolves cleanly. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * ci: trigger CI checks * Update design-time signature expected files for With* methods Regenerate 112 expected files to include the With* methods added to CsvProvider Row types and JsonProvider record types. Fixes the design-time signature test failures reported in issue #1644. Root cause: the expected files were not updated when With* methods were added to CsvGenerator.fs and JsonGenerator.fs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * ci: trigger CI checks --------- Co-authored-by: Repo Assist <repo-assist@github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Don Syme <dsyme@users.noreply.github.com>
1 parent c6a017a commit e7251a0

117 files changed

Lines changed: 9486 additions & 2 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/FSharp.Data.DesignTime/Csv/CsvGenerator.fs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,35 @@ module internal CsvTypeBuilder =
9999
for field in fields do
100100
rowType.AddMember field.ProvidedProperty
101101

102+
// Add With* methods so users can create a modified copy of a row
103+
// e.g. myRow.WithAmount(42.0) returns a new Row identical to myRow except Amount = 42.0
104+
for targetIdx, targetField in List.indexed fields do
105+
let methodName = "With" + targetField.ProvidedProperty.Name
106+
107+
let withMethod =
108+
ProvidedMethod(
109+
methodName,
110+
[ ProvidedParameter(targetField.ProvidedParameter.Name, targetField.ProvidedProperty.PropertyType) ],
111+
rowType,
112+
invokeCode =
113+
fun args ->
114+
let row = args.[0]
115+
let newVal = args.[1]
116+
117+
match fields with
118+
| [ _ ] ->
119+
// Single-column CSV: Row erases to the value itself
120+
newVal
121+
| _ ->
122+
let tupleArgs =
123+
fields
124+
|> List.mapi (fun i _ -> if i = targetIdx then newVal else Expr.TupleGet(row, i))
125+
126+
Expr.NewTuple tupleArgs
127+
)
128+
129+
rowType.AddMember withMethod
130+
102131
// The erased csv type will be parameterised by the tuple type
103132
let csvErasedTypeWithRowErasedType =
104133
typedefof<CsvFile<_>>.MakeGenericType(rowErasedType)

src/FSharp.Data.DesignTime/Json/JsonGenerator.fs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -651,7 +651,7 @@ module JsonTypeBuilder =
651651

652652
let name = makeUnique prop.Name
653653

654-
prop.Name,
654+
(prop.Name, name),
655655
[ ProvidedProperty(name, convertedType, getterCode = getter) ],
656656
ProvidedParameter(
657657
(if ctx.UseOriginalNames then
@@ -661,7 +661,8 @@ module JsonTypeBuilder =
661661
replaceJDocWithJValue ctx convertedType
662662
) ]
663663

664-
let names, properties, parameters = List.unzip3 members
664+
let namePairs, properties, parameters = List.unzip3 members
665+
let names = namePairs |> List.map fst
665666
let properties = properties |> List.concat
666667
objectTy.AddMembers properties
667668

@@ -685,6 +686,34 @@ module JsonTypeBuilder =
685686
let ctor = ProvidedConstructor(parameters, invokeCode = ctorCode)
686687
objectTy.AddMember ctor
687688

689+
// Add With* methods: WithPropName(newValue) returns a new record with one field updated
690+
for i, (rawName, displayName) in List.indexed namePairs do
691+
let param = parameters.[i]
692+
let cultureStr = ctx.CultureStr
693+
694+
let withMethod =
695+
ProvidedMethod(
696+
"With" + displayName,
697+
[ ProvidedParameter(param.Name, param.ParameterType) ],
698+
objectTy,
699+
invokeCode =
700+
fun args ->
701+
let jDoc = args.[0]
702+
let newVal = args.[1]
703+
let newValObj = Expr.Coerce(newVal, typeof<obj>)
704+
705+
<@@
706+
JsonRuntime.WithRecordProperty(
707+
(%%jDoc: IJsonDocument),
708+
rawName,
709+
%%newValObj,
710+
cultureStr
711+
)
712+
@@>
713+
)
714+
715+
objectTy.AddMember withMethod
716+
688717
()
689718

690719
if ctx.GenerateConstructors then

src/FSharp.Data.Json.Core/JsonRuntime.fs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,31 @@ type JsonRuntime =
351351

352352
JsonDocument.Create(json, "")
353353

354+
// Returns a new JSON record document with one property replaced (or added if absent).
355+
// Used by generated With* methods.
356+
static member WithRecordProperty(doc: IJsonDocument, name: string, value: obj, cultureStr: string) =
357+
let cultureInfo = TextRuntime.GetCulture cultureStr
358+
let newPropValue = JsonRuntime.ToJsonValue cultureInfo value
359+
let existingProps = JsonRuntime.GetRecordProperties(doc)
360+
let mutable found = false
361+
362+
let updatedProps =
363+
existingProps
364+
|> Array.map (fun (k, v) ->
365+
if k = name then
366+
found <- true
367+
k, newPropValue
368+
else
369+
k, v)
370+
371+
let finalProps =
372+
if found then
373+
updatedProps
374+
else
375+
Array.append updatedProps [| name, newPropValue |]
376+
377+
JsonDocument.Create(JsonValue.Record finalProps, "")
378+
354379
// Creates a JsonValue.Record, omitting null fields, and wraps it in a json document
355380
static member CreateRecordOmitNulls(properties, cultureStr) =
356381
let cultureInfo = TextRuntime.GetCulture cultureStr

tests/FSharp.Data.DesignTime.Tests/expected/Csv,AirQuality.csv,;,,True,False,False,,,.expected

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,4 +246,52 @@ class CsvProvider+Row : float * float * decimal * int * int * int
246246
member Wind: decimal with get
247247
(let _,_,t3,_,_,_ = this in t3)
248248

249+
member WithDay: day:int -> CsvProvider+Row
250+
(let t1,_,_,_,_,_ = this in t1),
251+
(let _,t2,_,_,_,_ = this in t2),
252+
(let _,_,t3,_,_,_ = this in t3),
253+
(let _,_,_,t4,_,_ = this in t4),
254+
(let _,_,_,_,t5,_ = this in t5),
255+
day
256+
257+
member WithMonth: month:int -> CsvProvider+Row
258+
(let t1,_,_,_,_,_ = this in t1),
259+
(let _,t2,_,_,_,_ = this in t2),
260+
(let _,_,t3,_,_,_ = this in t3),
261+
(let _,_,_,t4,_,_ = this in t4),
262+
month,
263+
(let _,_,_,_,_,t6 = this in t6)
264+
265+
member WithOzone: ozone:float -> CsvProvider+Row
266+
ozone,
267+
(let _,t2,_,_,_,_ = this in t2),
268+
(let _,_,t3,_,_,_ = this in t3),
269+
(let _,_,_,t4,_,_ = this in t4),
270+
(let _,_,_,_,t5,_ = this in t5),
271+
(let _,_,_,_,_,t6 = this in t6)
272+
273+
member WithSolar.R: solarR:float -> CsvProvider+Row
274+
(let t1,_,_,_,_,_ = this in t1),
275+
solarR,
276+
(let _,_,t3,_,_,_ = this in t3),
277+
(let _,_,_,t4,_,_ = this in t4),
278+
(let _,_,_,_,t5,_ = this in t5),
279+
(let _,_,_,_,_,t6 = this in t6)
280+
281+
member WithTemp: temp:int -> CsvProvider+Row
282+
(let t1,_,_,_,_,_ = this in t1),
283+
(let _,t2,_,_,_,_ = this in t2),
284+
(let _,_,t3,_,_,_ = this in t3),
285+
temp,
286+
(let _,_,_,_,t5,_ = this in t5),
287+
(let _,_,_,_,_,t6 = this in t6)
288+
289+
member WithWind: wind:decimal -> CsvProvider+Row
290+
(let t1,_,_,_,_,_ = this in t1),
291+
(let _,t2,_,_,_,_ = this in t2),
292+
wind,
293+
(let _,_,_,t4,_,_ = this in t4),
294+
(let _,_,_,_,t5,_ = this in t5),
295+
(let _,_,_,_,_,t6 = this in t6)
296+
249297

tests/FSharp.Data.DesignTime.Tests/expected/Csv,DnbHistoriskeKurser.csv,,,True,False,False,,nb-NO,.expected

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,4 +406,147 @@ class CsvProvider+Row : System.DateTime * string * string * string * string * st
406406
member USD: string with get
407407
(let _,t2,_,_,_,_,_,_,_,_,_ = this in t2)
408408

409+
member WithAUD: aud:string -> CsvProvider+Row
410+
(let t1,_,_,_,_,_,_,_,_,_,_ = this in t1),
411+
(let _,t2,_,_,_,_,_,_,_,_,_ = this in t2),
412+
(let _,_,t3,_,_,_,_,_,_,_,_ = this in t3),
413+
(let _,_,_,t4,_,_,_,_,_,_,_ = this in t4),
414+
(let _,_,_,_,t5,_,_,_,_,_,_ = this in t5),
415+
(let _,_,_,_,_,t6,_,_,_,_,_ = this in t6),
416+
(let _,_,_,_,_,_,t7,_,_,_,_ = this in t7),
417+
(let _,_,_,_,_,_,_,t8,_,_,_ = this in t8),
418+
(let _,_,_,_,_,_,_,_,t9,_,_ = this in t9),
419+
(let _,_,_,_,_,_,_,_,_,t10,_ = this in t10),
420+
aud
421+
422+
member WithCAD: cad:string -> CsvProvider+Row
423+
(let t1,_,_,_,_,_,_,_,_,_,_ = this in t1),
424+
(let _,t2,_,_,_,_,_,_,_,_,_ = this in t2),
425+
(let _,_,t3,_,_,_,_,_,_,_,_ = this in t3),
426+
(let _,_,_,t4,_,_,_,_,_,_,_ = this in t4),
427+
(let _,_,_,_,t5,_,_,_,_,_,_ = this in t5),
428+
(let _,_,_,_,_,t6,_,_,_,_,_ = this in t6),
429+
(let _,_,_,_,_,_,t7,_,_,_,_ = this in t7),
430+
(let _,_,_,_,_,_,_,t8,_,_,_ = this in t8),
431+
cad,
432+
(let _,_,_,_,_,_,_,_,_,t10,_ = this in t10),
433+
(let _,_,_,_,_,_,_,_,_,_,t11 = this in t11)
434+
435+
member WithCHF: chf:string -> CsvProvider+Row
436+
(let t1,_,_,_,_,_,_,_,_,_,_ = this in t1),
437+
(let _,t2,_,_,_,_,_,_,_,_,_ = this in t2),
438+
(let _,_,t3,_,_,_,_,_,_,_,_ = this in t3),
439+
(let _,_,_,t4,_,_,_,_,_,_,_ = this in t4),
440+
(let _,_,_,_,t5,_,_,_,_,_,_ = this in t5),
441+
(let _,_,_,_,_,t6,_,_,_,_,_ = this in t6),
442+
chf,
443+
(let _,_,_,_,_,_,_,t8,_,_,_ = this in t8),
444+
(let _,_,_,_,_,_,_,_,t9,_,_ = this in t9),
445+
(let _,_,_,_,_,_,_,_,_,t10,_ = this in t10),
446+
(let _,_,_,_,_,_,_,_,_,_,t11 = this in t11)
447+
448+
member WithDKK: dkk:string -> CsvProvider+Row
449+
(let t1,_,_,_,_,_,_,_,_,_,_ = this in t1),
450+
(let _,t2,_,_,_,_,_,_,_,_,_ = this in t2),
451+
(let _,_,t3,_,_,_,_,_,_,_,_ = this in t3),
452+
(let _,_,_,t4,_,_,_,_,_,_,_ = this in t4),
453+
dkk,
454+
(let _,_,_,_,_,t6,_,_,_,_,_ = this in t6),
455+
(let _,_,_,_,_,_,t7,_,_,_,_ = this in t7),
456+
(let _,_,_,_,_,_,_,t8,_,_,_ = this in t8),
457+
(let _,_,_,_,_,_,_,_,t9,_,_ = this in t9),
458+
(let _,_,_,_,_,_,_,_,_,t10,_ = this in t10),
459+
(let _,_,_,_,_,_,_,_,_,_,t11 = this in t11)
460+
461+
member WithDato: dato:System.DateTime -> CsvProvider+Row
462+
dato,
463+
(let _,t2,_,_,_,_,_,_,_,_,_ = this in t2),
464+
(let _,_,t3,_,_,_,_,_,_,_,_ = this in t3),
465+
(let _,_,_,t4,_,_,_,_,_,_,_ = this in t4),
466+
(let _,_,_,_,t5,_,_,_,_,_,_ = this in t5),
467+
(let _,_,_,_,_,t6,_,_,_,_,_ = this in t6),
468+
(let _,_,_,_,_,_,t7,_,_,_,_ = this in t7),
469+
(let _,_,_,_,_,_,_,t8,_,_,_ = this in t8),
470+
(let _,_,_,_,_,_,_,_,t9,_,_ = this in t9),
471+
(let _,_,_,_,_,_,_,_,_,t10,_ = this in t10),
472+
(let _,_,_,_,_,_,_,_,_,_,t11 = this in t11)
473+
474+
member WithEUR: eur:string -> CsvProvider+Row
475+
(let t1,_,_,_,_,_,_,_,_,_,_ = this in t1),
476+
(let _,t2,_,_,_,_,_,_,_,_,_ = this in t2),
477+
eur,
478+
(let _,_,_,t4,_,_,_,_,_,_,_ = this in t4),
479+
(let _,_,_,_,t5,_,_,_,_,_,_ = this in t5),
480+
(let _,_,_,_,_,t6,_,_,_,_,_ = this in t6),
481+
(let _,_,_,_,_,_,t7,_,_,_,_ = this in t7),
482+
(let _,_,_,_,_,_,_,t8,_,_,_ = this in t8),
483+
(let _,_,_,_,_,_,_,_,t9,_,_ = this in t9),
484+
(let _,_,_,_,_,_,_,_,_,t10,_ = this in t10),
485+
(let _,_,_,_,_,_,_,_,_,_,t11 = this in t11)
486+
487+
member WithGBP: gbp:string -> CsvProvider+Row
488+
(let t1,_,_,_,_,_,_,_,_,_,_ = this in t1),
489+
(let _,t2,_,_,_,_,_,_,_,_,_ = this in t2),
490+
(let _,_,t3,_,_,_,_,_,_,_,_ = this in t3),
491+
(let _,_,_,t4,_,_,_,_,_,_,_ = this in t4),
492+
(let _,_,_,_,t5,_,_,_,_,_,_ = this in t5),
493+
gbp,
494+
(let _,_,_,_,_,_,t7,_,_,_,_ = this in t7),
495+
(let _,_,_,_,_,_,_,t8,_,_,_ = this in t8),
496+
(let _,_,_,_,_,_,_,_,t9,_,_ = this in t9),
497+
(let _,_,_,_,_,_,_,_,_,t10,_ = this in t10),
498+
(let _,_,_,_,_,_,_,_,_,_,t11 = this in t11)
499+
500+
member WithISK: isk:string -> CsvProvider+Row
501+
(let t1,_,_,_,_,_,_,_,_,_,_ = this in t1),
502+
(let _,t2,_,_,_,_,_,_,_,_,_ = this in t2),
503+
(let _,_,t3,_,_,_,_,_,_,_,_ = this in t3),
504+
(let _,_,_,t4,_,_,_,_,_,_,_ = this in t4),
505+
(let _,_,_,_,t5,_,_,_,_,_,_ = this in t5),
506+
(let _,_,_,_,_,t6,_,_,_,_,_ = this in t6),
507+
(let _,_,_,_,_,_,t7,_,_,_,_ = this in t7),
508+
(let _,_,_,_,_,_,_,t8,_,_,_ = this in t8),
509+
(let _,_,_,_,_,_,_,_,t9,_,_ = this in t9),
510+
isk,
511+
(let _,_,_,_,_,_,_,_,_,_,t11 = this in t11)
512+
513+
member WithJPY: jpy:string -> CsvProvider+Row
514+
(let t1,_,_,_,_,_,_,_,_,_,_ = this in t1),
515+
(let _,t2,_,_,_,_,_,_,_,_,_ = this in t2),
516+
(let _,_,t3,_,_,_,_,_,_,_,_ = this in t3),
517+
(let _,_,_,t4,_,_,_,_,_,_,_ = this in t4),
518+
(let _,_,_,_,t5,_,_,_,_,_,_ = this in t5),
519+
(let _,_,_,_,_,t6,_,_,_,_,_ = this in t6),
520+
(let _,_,_,_,_,_,t7,_,_,_,_ = this in t7),
521+
jpy,
522+
(let _,_,_,_,_,_,_,_,t9,_,_ = this in t9),
523+
(let _,_,_,_,_,_,_,_,_,t10,_ = this in t10),
524+
(let _,_,_,_,_,_,_,_,_,_,t11 = this in t11)
525+
526+
member WithSEK: sek:string -> CsvProvider+Row
527+
(let t1,_,_,_,_,_,_,_,_,_,_ = this in t1),
528+
(let _,t2,_,_,_,_,_,_,_,_,_ = this in t2),
529+
(let _,_,t3,_,_,_,_,_,_,_,_ = this in t3),
530+
sek,
531+
(let _,_,_,_,t5,_,_,_,_,_,_ = this in t5),
532+
(let _,_,_,_,_,t6,_,_,_,_,_ = this in t6),
533+
(let _,_,_,_,_,_,t7,_,_,_,_ = this in t7),
534+
(let _,_,_,_,_,_,_,t8,_,_,_ = this in t8),
535+
(let _,_,_,_,_,_,_,_,t9,_,_ = this in t9),
536+
(let _,_,_,_,_,_,_,_,_,t10,_ = this in t10),
537+
(let _,_,_,_,_,_,_,_,_,_,t11 = this in t11)
538+
539+
member WithUSD: usd:string -> CsvProvider+Row
540+
(let t1,_,_,_,_,_,_,_,_,_,_ = this in t1),
541+
usd,
542+
(let _,_,t3,_,_,_,_,_,_,_,_ = this in t3),
543+
(let _,_,_,t4,_,_,_,_,_,_,_ = this in t4),
544+
(let _,_,_,_,t5,_,_,_,_,_,_ = this in t5),
545+
(let _,_,_,_,_,t6,_,_,_,_,_ = this in t6),
546+
(let _,_,_,_,_,_,t7,_,_,_,_ = this in t7),
547+
(let _,_,_,_,_,_,_,t8,_,_,_ = this in t8),
548+
(let _,_,_,_,_,_,_,_,t9,_,_ = this in t9),
549+
(let _,_,_,_,_,_,_,_,_,t10,_ = this in t10),
550+
(let _,_,_,_,_,_,_,_,_,_,t11 = this in t11)
551+
409552

tests/FSharp.Data.DesignTime.Tests/expected/Csv,LastFM.tsv,,,False,False,False,,,.expected

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,4 +232,52 @@ class CsvProvider+Row : string * System.DateTimeOffset * System.Guid option * st
232232
member Column6: string with get
233233
(let _,_,_,_,_,t6 = this in t6)
234234

235+
member WithColumn1: column1:string -> CsvProvider+Row
236+
column1,
237+
(let _,t2,_,_,_,_ = this in t2),
238+
(let _,_,t3,_,_,_ = this in t3),
239+
(let _,_,_,t4,_,_ = this in t4),
240+
(let _,_,_,_,t5,_ = this in t5),
241+
(let _,_,_,_,_,t6 = this in t6)
242+
243+
member WithColumn2: column2:System.DateTimeOffset -> CsvProvider+Row
244+
(let t1,_,_,_,_,_ = this in t1),
245+
column2,
246+
(let _,_,t3,_,_,_ = this in t3),
247+
(let _,_,_,t4,_,_ = this in t4),
248+
(let _,_,_,_,t5,_ = this in t5),
249+
(let _,_,_,_,_,t6 = this in t6)
250+
251+
member WithColumn3: column3:System.Guid option -> CsvProvider+Row
252+
(let t1,_,_,_,_,_ = this in t1),
253+
(let _,t2,_,_,_,_ = this in t2),
254+
column3,
255+
(let _,_,_,t4,_,_ = this in t4),
256+
(let _,_,_,_,t5,_ = this in t5),
257+
(let _,_,_,_,_,t6 = this in t6)
258+
259+
member WithColumn4: column4:string -> CsvProvider+Row
260+
(let t1,_,_,_,_,_ = this in t1),
261+
(let _,t2,_,_,_,_ = this in t2),
262+
(let _,_,t3,_,_,_ = this in t3),
263+
column4,
264+
(let _,_,_,_,t5,_ = this in t5),
265+
(let _,_,_,_,_,t6 = this in t6)
266+
267+
member WithColumn5: column5:string -> CsvProvider+Row
268+
(let t1,_,_,_,_,_ = this in t1),
269+
(let _,t2,_,_,_,_ = this in t2),
270+
(let _,_,t3,_,_,_ = this in t3),
271+
(let _,_,_,t4,_,_ = this in t4),
272+
column5,
273+
(let _,_,_,_,_,t6 = this in t6)
274+
275+
member WithColumn6: column6:string -> CsvProvider+Row
276+
(let t1,_,_,_,_,_ = this in t1),
277+
(let _,t2,_,_,_,_ = this in t2),
278+
(let _,_,t3,_,_,_ = this in t3),
279+
(let _,_,_,t4,_,_ = this in t4),
280+
(let _,_,_,_,t5,_ = this in t5),
281+
column6
282+
235283

0 commit comments

Comments
 (0)