From 54f1ae1bedb207621a92b70a64220456781a8e37 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 20 Sep 2025 02:32:02 +0000 Subject: [PATCH 1/8] chore: bump minimum go version to 1.22 --- go.mod | 2 +- internal/encoding/json/shims/shims.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index d01a0e4..052220b 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/CASParser/cas-parser-go -go 1.21 +go 1.22 require ( github.com/tidwall/gjson v1.14.4 diff --git a/internal/encoding/json/shims/shims.go b/internal/encoding/json/shims/shims.go index b65a016..fe9a71a 100644 --- a/internal/encoding/json/shims/shims.go +++ b/internal/encoding/json/shims/shims.go @@ -1,5 +1,5 @@ // This package provides shims over Go 1.2{2,3} APIs -// which are missing from Go 1.21, and used by the Go 1.24 encoding/json package. +// which are missing from Go 1.22, and used by the Go 1.24 encoding/json package. // // Inside the vendored package, all shim code has comments that begin look like // // SHIM(...): ... From fc3b56e861603f65fd1a61d6c843b220e94154cc Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 20 Sep 2025 02:33:24 +0000 Subject: [PATCH 2/8] chore: update more docs for 1.22 --- CONTRIBUTING.md | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a02f386..5be4390 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,7 +9,7 @@ $ ./scripts/lint This will install all the required dependencies and build the SDK. -You can also [install go 1.18+ manually](https://go.dev/doc/install). +You can also [install go 1.22+ manually](https://go.dev/doc/install). ## Modifying/Adding code diff --git a/README.md b/README.md index 14db163..8e86618 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ go get -u 'github.com/CASParser/cas-parser-go@v0.0.3' ## Requirements -This library requires Go 1.18+. +This library requires Go 1.22+. ## Usage From c30d79aa4eb1f8b4d793fe5e641db7d0f385a9dc Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 20 Sep 2025 02:34:00 +0000 Subject: [PATCH 3/8] fix: use slices.Concat instead of sometimes modifying r.Options --- casgenerator.go | 3 ++- casparser.go | 9 +++++---- client.go | 3 ++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/casgenerator.go b/casgenerator.go index e1dea8b..6fb8461 100644 --- a/casgenerator.go +++ b/casgenerator.go @@ -5,6 +5,7 @@ package casparser import ( "context" "net/http" + "slices" "github.com/CASParser/cas-parser-go/internal/apijson" "github.com/CASParser/cas-parser-go/internal/requestconfig" @@ -36,7 +37,7 @@ func NewCasGeneratorService(opts ...option.RequestOption) (r CasGeneratorService // submitting a mailback request to the specified CAS authority. Currently only // supports KFintech, with plans to support CAMS, CDSL, and NSDL in the future. func (r *CasGeneratorService) GenerateCas(ctx context.Context, body CasGeneratorGenerateCasParams, opts ...option.RequestOption) (res *CasGeneratorGenerateCasResponse, err error) { - opts = append(r.Options[:], opts...) + opts = slices.Concat(r.Options, opts) path := "v4/generate" err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...) return diff --git a/casparser.go b/casparser.go index 66e6cca..1bb8cbc 100644 --- a/casparser.go +++ b/casparser.go @@ -5,6 +5,7 @@ package casparser import ( "context" "net/http" + "slices" "time" "github.com/CASParser/cas-parser-go/internal/apijson" @@ -37,7 +38,7 @@ func NewCasParserService(opts ...option.RequestOption) (r CasParserService) { // Statement) PDF files and returns data in a unified format. Use this endpoint // when you know the PDF is from CAMS or KFintech. func (r *CasParserService) CamsKfintech(ctx context.Context, body CasParserCamsKfintechParams, opts ...option.RequestOption) (res *UnifiedResponse, err error) { - opts = append(r.Options[:], opts...) + opts = slices.Concat(r.Options, opts) path := "v4/cams_kfintech/parse" err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...) return @@ -47,7 +48,7 @@ func (r *CasParserService) CamsKfintech(ctx context.Context, body CasParserCamsK // files and returns data in a unified format. Use this endpoint when you know the // PDF is from CDSL. func (r *CasParserService) Cdsl(ctx context.Context, body CasParserCdslParams, opts ...option.RequestOption) (res *UnifiedResponse, err error) { - opts = append(r.Options[:], opts...) + opts = slices.Concat(r.Options, opts) path := "v4/cdsl/parse" err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...) return @@ -57,7 +58,7 @@ func (r *CasParserService) Cdsl(ctx context.Context, body CasParserCdslParams, o // files and returns data in a unified format. Use this endpoint when you know the // PDF is from NSDL. func (r *CasParserService) Nsdl(ctx context.Context, body CasParserNsdlParams, opts ...option.RequestOption) (res *UnifiedResponse, err error) { - opts = append(r.Options[:], opts...) + opts = slices.Concat(r.Options, opts) path := "v4/nsdl/parse" err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...) return @@ -68,7 +69,7 @@ func (r *CasParserService) Nsdl(ctx context.Context, body CasParserNsdlParams, o // CAS type and transforms the data into a consistent structure regardless of the // source. func (r *CasParserService) SmartParse(ctx context.Context, body CasParserSmartParseParams, opts ...option.RequestOption) (res *UnifiedResponse, err error) { - opts = append(r.Options[:], opts...) + opts = slices.Concat(r.Options, opts) path := "v4/smart/parse" err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...) return diff --git a/client.go b/client.go index a757680..ab47a50 100644 --- a/client.go +++ b/client.go @@ -6,6 +6,7 @@ import ( "context" "net/http" "os" + "slices" "github.com/CASParser/cas-parser-go/internal/requestconfig" "github.com/CASParser/cas-parser-go/option" @@ -80,7 +81,7 @@ func NewClient(opts ...option.RequestOption) (r Client) { // For even greater flexibility, see [option.WithResponseInto] and // [option.WithResponseBodyInto]. func (r *Client) Execute(ctx context.Context, method string, path string, params any, res any, opts ...option.RequestOption) error { - opts = append(r.Options, opts...) + opts = slices.Concat(r.Options, opts) return requestconfig.ExecuteNewRequest(ctx, method, path, params, res, opts...) } From a665c6aa0d14754a7f4ea644b2b53ac49dec0f0c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Sat, 20 Sep 2025 02:34:48 +0000 Subject: [PATCH 4/8] chore: do not install brew dependencies in ./scripts/bootstrap by default --- scripts/bootstrap | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/scripts/bootstrap b/scripts/bootstrap index d6ac165..5ab3066 100755 --- a/scripts/bootstrap +++ b/scripts/bootstrap @@ -4,10 +4,18 @@ set -e cd "$(dirname "$0")/.." -if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "$SKIP_BREW" != "1" ]; then +if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "$SKIP_BREW" != "1" ] && [ -t 0 ]; then brew bundle check >/dev/null 2>&1 || { - echo "==> Installing Homebrew dependencies…" - brew bundle + echo -n "==> Install Homebrew dependencies? (y/N): " + read -r response + case "$response" in + [yY][eE][sS]|[yY]) + brew bundle + ;; + *) + ;; + esac + echo } fi From 4cafc4e634e376310e635eeeacd9c6bc1656d7ca Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 22 Sep 2025 00:18:12 +0000 Subject: [PATCH 5/8] feat(api): api update --- .stats.yml | 4 +- casparser.go | 179 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 180 insertions(+), 3 deletions(-) diff --git a/.stats.yml b/.stats.yml index 92721c7..06e7614 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 5 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cas-parser%2Fcas-parser-b7fdba3d3f97c7debc22c7ca30b828bce81bcd64648df8c94029b27a3321ebb9.yml -openapi_spec_hash: 03f1315f1d32ada42445ca920f047dff +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cas-parser%2Fcas-parser-9eaed98ce5934f11e901cef376a28257d2c196bd3dba7c690babc6741a730ded.yml +openapi_spec_hash: b76e4e830c4d03ba4cf9429bb9fb9c8a config_hash: cb5d75abef6264b5d86448caf7295afa diff --git a/casparser.go b/casparser.go index 1bb8cbc..243f21f 100644 --- a/casparser.go +++ b/casparser.go @@ -81,7 +81,9 @@ type UnifiedResponse struct { Investor UnifiedResponseInvestor `json:"investor"` Meta UnifiedResponseMeta `json:"meta"` MutualFunds []UnifiedResponseMutualFund `json:"mutual_funds"` - Summary UnifiedResponseSummary `json:"summary"` + // List of NPS accounts + Nps []UnifiedResponseNp `json:"nps"` + Summary UnifiedResponseSummary `json:"summary"` // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. JSON struct { DematAccounts respjson.Field @@ -89,6 +91,7 @@ type UnifiedResponse struct { Investor respjson.Field Meta respjson.Field MutualFunds respjson.Field + Nps respjson.Field Summary respjson.Field ExtraFields map[string]respjson.Field raw string @@ -117,6 +120,8 @@ type UnifiedResponseDematAccount struct { // Depository Participant name DpName string `json:"dp_name"` Holdings UnifiedResponseDematAccountHoldings `json:"holdings"` + // List of account holders linked to this demat account + LinkedHolders []UnifiedResponseDematAccountLinkedHolder `json:"linked_holders"` // Total value of the demat account Value float64 `json:"value"` // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. @@ -128,6 +133,7 @@ type UnifiedResponseDematAccount struct { DpID respjson.Field DpName respjson.Field Holdings respjson.Field + LinkedHolders respjson.Field Value respjson.Field ExtraFields map[string]respjson.Field raw string @@ -348,6 +354,26 @@ func (r *UnifiedResponseDematAccountHoldingsGovernmentSecurity) UnmarshalJSON(da return apijson.UnmarshalRoot(data, r) } +type UnifiedResponseDematAccountLinkedHolder struct { + // Name of the account holder + Name string `json:"name"` + // PAN of the account holder + Pan string `json:"pan"` + // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. + JSON struct { + Name respjson.Field + Pan respjson.Field + ExtraFields map[string]respjson.Field + raw string + } `json:"-"` +} + +// Returns the unmodified JSON received from the API +func (r UnifiedResponseDematAccountLinkedHolder) RawJSON() string { return r.JSON.raw } +func (r *UnifiedResponseDematAccountLinkedHolder) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + type UnifiedResponseInsurance struct { LifeInsurancePolicies []UnifiedResponseInsuranceLifeInsurancePolicy `json:"life_insurance_policies"` // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. @@ -491,6 +517,8 @@ type UnifiedResponseMutualFund struct { Amc string `json:"amc"` // Folio number FolioNumber string `json:"folio_number"` + // List of account holders linked to this mutual fund folio + LinkedHolders []UnifiedResponseMutualFundLinkedHolder `json:"linked_holders"` // Registrar and Transfer Agent name Registrar string `json:"registrar"` Schemes []UnifiedResponseMutualFundScheme `json:"schemes"` @@ -501,6 +529,7 @@ type UnifiedResponseMutualFund struct { AdditionalInfo respjson.Field Amc respjson.Field FolioNumber respjson.Field + LinkedHolders respjson.Field Registrar respjson.Field Schemes respjson.Field Value respjson.Field @@ -539,6 +568,26 @@ func (r *UnifiedResponseMutualFundAdditionalInfo) UnmarshalJSON(data []byte) err return apijson.UnmarshalRoot(data, r) } +type UnifiedResponseMutualFundLinkedHolder struct { + // Name of the account holder + Name string `json:"name"` + // PAN of the account holder + Pan string `json:"pan"` + // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. + JSON struct { + Name respjson.Field + Pan respjson.Field + ExtraFields map[string]respjson.Field + raw string + } `json:"-"` +} + +// Returns the unmodified JSON received from the API +func (r UnifiedResponseMutualFundLinkedHolder) RawJSON() string { return r.JSON.raw } +func (r *UnifiedResponseMutualFundLinkedHolder) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + type UnifiedResponseMutualFundScheme struct { // Additional information specific to the scheme AdditionalInfo UnifiedResponseMutualFundSchemeAdditionalInfo `json:"additional_info"` @@ -677,6 +726,112 @@ func (r *UnifiedResponseMutualFundSchemeTransaction) UnmarshalJSON(data []byte) return apijson.UnmarshalRoot(data, r) } +type UnifiedResponseNp struct { + // Additional information specific to the NPS account + AdditionalInfo any `json:"additional_info"` + // Central Record Keeping Agency name + Cra string `json:"cra"` + Funds []UnifiedResponseNpFund `json:"funds"` + // List of account holders linked to this NPS account + LinkedHolders []UnifiedResponseNpLinkedHolder `json:"linked_holders"` + // Permanent Retirement Account Number (PRAN) + Pran string `json:"pran"` + // Total value of the NPS account + Value float64 `json:"value"` + // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. + JSON struct { + AdditionalInfo respjson.Field + Cra respjson.Field + Funds respjson.Field + LinkedHolders respjson.Field + Pran respjson.Field + Value respjson.Field + ExtraFields map[string]respjson.Field + raw string + } `json:"-"` +} + +// Returns the unmodified JSON received from the API +func (r UnifiedResponseNp) RawJSON() string { return r.JSON.raw } +func (r *UnifiedResponseNp) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + +type UnifiedResponseNpFund struct { + // Additional information specific to the NPS fund + AdditionalInfo UnifiedResponseNpFundAdditionalInfo `json:"additional_info"` + // Cost of investment + Cost float64 `json:"cost"` + // Name of the NPS fund + Name string `json:"name"` + // Net Asset Value per unit + Nav float64 `json:"nav"` + // Number of units held + Units float64 `json:"units"` + // Current market value of the holding + Value float64 `json:"value"` + // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. + JSON struct { + AdditionalInfo respjson.Field + Cost respjson.Field + Name respjson.Field + Nav respjson.Field + Units respjson.Field + Value respjson.Field + ExtraFields map[string]respjson.Field + raw string + } `json:"-"` +} + +// Returns the unmodified JSON received from the API +func (r UnifiedResponseNpFund) RawJSON() string { return r.JSON.raw } +func (r *UnifiedResponseNpFund) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + +// Additional information specific to the NPS fund +type UnifiedResponseNpFundAdditionalInfo struct { + // Fund manager name + Manager string `json:"manager"` + // NPS tier (Tier I or Tier II) + // + // Any of 1, 2. + Tier float64 `json:"tier,nullable"` + // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. + JSON struct { + Manager respjson.Field + Tier respjson.Field + ExtraFields map[string]respjson.Field + raw string + } `json:"-"` +} + +// Returns the unmodified JSON received from the API +func (r UnifiedResponseNpFundAdditionalInfo) RawJSON() string { return r.JSON.raw } +func (r *UnifiedResponseNpFundAdditionalInfo) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + +type UnifiedResponseNpLinkedHolder struct { + // Name of the account holder + Name string `json:"name"` + // PAN of the account holder + Pan string `json:"pan"` + // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. + JSON struct { + Name respjson.Field + Pan respjson.Field + ExtraFields map[string]respjson.Field + raw string + } `json:"-"` +} + +// Returns the unmodified JSON received from the API +func (r UnifiedResponseNpLinkedHolder) RawJSON() string { return r.JSON.raw } +func (r *UnifiedResponseNpLinkedHolder) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + type UnifiedResponseSummary struct { Accounts UnifiedResponseSummaryAccounts `json:"accounts"` // Total portfolio value across all accounts @@ -700,11 +855,13 @@ type UnifiedResponseSummaryAccounts struct { Demat UnifiedResponseSummaryAccountsDemat `json:"demat"` Insurance UnifiedResponseSummaryAccountsInsurance `json:"insurance"` MutualFunds UnifiedResponseSummaryAccountsMutualFunds `json:"mutual_funds"` + Nps UnifiedResponseSummaryAccountsNps `json:"nps"` // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. JSON struct { Demat respjson.Field Insurance respjson.Field MutualFunds respjson.Field + Nps respjson.Field ExtraFields map[string]respjson.Field raw string } `json:"-"` @@ -776,6 +933,26 @@ func (r *UnifiedResponseSummaryAccountsMutualFunds) UnmarshalJSON(data []byte) e return apijson.UnmarshalRoot(data, r) } +type UnifiedResponseSummaryAccountsNps struct { + // Number of NPS accounts + Count int64 `json:"count"` + // Total value of NPS accounts + TotalValue float64 `json:"total_value"` + // JSON contains metadata for fields, check presence with [respjson.Field.Valid]. + JSON struct { + Count respjson.Field + TotalValue respjson.Field + ExtraFields map[string]respjson.Field + raw string + } `json:"-"` +} + +// Returns the unmodified JSON received from the API +func (r UnifiedResponseSummaryAccountsNps) RawJSON() string { return r.JSON.raw } +func (r *UnifiedResponseSummaryAccountsNps) UnmarshalJSON(data []byte) error { + return apijson.UnmarshalRoot(data, r) +} + type CasParserCamsKfintechParams struct { // Password for the PDF file (if required) Password param.Opt[string] `json:"password,omitzero"` From 48f9ffda3a0053adcf1a68c5378bbeac9de9027c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 26 Sep 2025 02:21:39 +0000 Subject: [PATCH 6/8] fix: bugfix for setting JSON keys with special characters --- internal/apijson/encoder.go | 14 +++++++------- internal/apijson/union.go | 4 ++-- packages/param/encoder.go | 7 ++++++- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/internal/apijson/encoder.go b/internal/apijson/encoder.go index 8358a2f..ab7a3c1 100644 --- a/internal/apijson/encoder.go +++ b/internal/apijson/encoder.go @@ -16,6 +16,10 @@ import ( var encoders sync.Map // map[encoderEntry]encoderFunc +// If we want to set a literal key value into JSON using sjson, we need to make sure it doesn't have +// special characters that sjson interprets as a path. +var EscapeSJSONKey = strings.NewReplacer("\\", "\\\\", "|", "\\|", "#", "\\#", "@", "\\@", "*", "\\*", ".", "\\.", ":", "\\:", "?", "\\?").Replace + func Marshal(value any) ([]byte, error) { e := &encoder{dateFormat: time.RFC3339} return e.marshal(value) @@ -270,7 +274,7 @@ func (e *encoder) newStructTypeEncoder(t reflect.Type) encoderFunc { if encoded == nil { continue } - json, err = sjson.SetRawBytes(json, ef.tag.name, encoded) + json, err = sjson.SetRawBytes(json, EscapeSJSONKey(ef.tag.name), encoded) if err != nil { return nil, err } @@ -348,7 +352,7 @@ func (e *encoder) encodeMapEntries(json []byte, v reflect.Value) ([]byte, error) } encodedKeyString = string(encodedKeyBytes) } - encodedKey := []byte(sjsonReplacer.Replace(encodedKeyString)) + encodedKey := []byte(encodedKeyString) pairs = append(pairs, mapPair{key: encodedKey, value: iter.Value()}) } @@ -366,7 +370,7 @@ func (e *encoder) encodeMapEntries(json []byte, v reflect.Value) ([]byte, error) if len(encodedValue) == 0 { continue } - json, err = sjson.SetRawBytes(json, string(p.key), encodedValue) + json, err = sjson.SetRawBytes(json, EscapeSJSONKey(string(p.key)), encodedValue) if err != nil { return nil, err } @@ -386,7 +390,3 @@ func (e *encoder) newMapEncoder(_ reflect.Type) encoderFunc { return json, nil } } - -// If we want to set a literal key value into JSON using sjson, we need to make sure it doesn't have -// special characters that sjson interprets as a path. -var sjsonReplacer *strings.Replacer = strings.NewReplacer(".", "\\.", ":", "\\:", "*", "\\*") diff --git a/internal/apijson/union.go b/internal/apijson/union.go index 87eeb20..7b37be1 100644 --- a/internal/apijson/union.go +++ b/internal/apijson/union.go @@ -78,7 +78,7 @@ func (d *decoderBuilder) newStructUnionDecoder(t reflect.Type) decoderFunc { return func(n gjson.Result, v reflect.Value, state *decoderState) error { if discriminated && n.Type == gjson.JSON && len(unionEntry.discriminatorKey) != 0 { - discriminator := n.Get(unionEntry.discriminatorKey).Value() + discriminator := n.Get(EscapeSJSONKey(unionEntry.discriminatorKey)).Value() for _, decoder := range discriminatedDecoders { if discriminator == decoder.discriminator { inner := v.FieldByIndex(decoder.field.Index) @@ -162,7 +162,7 @@ func (d *decoderBuilder) newUnionDecoder(t reflect.Type) decoderFunc { } if len(unionEntry.discriminatorKey) != 0 { - discriminatorValue := n.Get(unionEntry.discriminatorKey).Value() + discriminatorValue := n.Get(EscapeSJSONKey(unionEntry.discriminatorKey)).Value() if discriminatorValue == variant.DiscriminatorValue { inner := reflect.New(variant.Type).Elem() err := decoder(n, inner, state) diff --git a/packages/param/encoder.go b/packages/param/encoder.go index 3ec5f41..42f2ae7 100644 --- a/packages/param/encoder.go +++ b/packages/param/encoder.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "reflect" + "strings" "time" shimjson "github.com/CASParser/cas-parser-go/internal/encoding/json" @@ -14,6 +15,10 @@ import ( // EncodedAsDate is not be stable and shouldn't be relied upon type EncodedAsDate Opt[time.Time] +// If we want to set a literal key value into JSON using sjson, we need to make sure it doesn't have +// special characters that sjson interprets as a path. +var EscapeSJSONKey = strings.NewReplacer("\\", "\\\\", "|", "\\|", "#", "\\#", "@", "\\@", "*", "\\*", ".", "\\.", ":", "\\:", "?", "\\?").Replace + type forceOmit int func (m EncodedAsDate) MarshalJSON() ([]byte, error) { @@ -52,7 +57,7 @@ func MarshalWithExtras[T ParamStruct, R any](f T, underlying any, extras map[str } continue } - bytes, err = sjson.SetBytes(bytes, k, v) + bytes, err = sjson.SetBytes(bytes, EscapeSJSONKey(k), v) if err != nil { return nil, err } From 0af28e95581cfd4c2d9f729ee59f8213253da54a Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 4 Nov 2025 03:36:19 +0000 Subject: [PATCH 7/8] chore(internal): grammar fix (it's -> its) --- README.md | 2 +- packages/respjson/respjson.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 8e86618..4ba57e5 100644 --- a/README.md +++ b/README.md @@ -128,7 +128,7 @@ custom := param.Override[casparser.FooParams](12) ### Request unions -Unions are represented as a struct with fields prefixed by "Of" for each of it's variants, +Unions are represented as a struct with fields prefixed by "Of" for each of its variants, only one field can be non-zero. The non-zero field will be serialized. Sub-properties of the union can be accessed via methods on the union struct. diff --git a/packages/respjson/respjson.go b/packages/respjson/respjson.go index cc0088c..9e61c5c 100644 --- a/packages/respjson/respjson.go +++ b/packages/respjson/respjson.go @@ -5,7 +5,7 @@ package respjson // Use [Field.Valid] to check if an optional value was null or omitted. // // A Field will always occur in the following structure, where it -// mirrors the original field in it's parent struct: +// mirrors the original field in its parent struct: // // type ExampleObject struct { // Foo bool `json:"foo"` From 2c7de05d196e1cc8b44240e6bbb77192bba33adc Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 4 Nov 2025 03:36:34 +0000 Subject: [PATCH 8/8] release: 0.1.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 22 ++++++++++++++++++++++ README.md | 2 +- internal/version.go | 2 +- 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 28e831b..3d2ac0b 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.0.3" + ".": "0.1.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 077729d..bfb33bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,27 @@ # Changelog +## 0.1.0 (2025-11-04) + +Full Changelog: [v0.0.3...v0.1.0](https://github.com/CASParser/cas-parser-go/compare/v0.0.3...v0.1.0) + +### Features + +* **api:** api update ([4cafc4e](https://github.com/CASParser/cas-parser-go/commit/4cafc4e634e376310e635eeeacd9c6bc1656d7ca)) + + +### Bug Fixes + +* bugfix for setting JSON keys with special characters ([48f9ffd](https://github.com/CASParser/cas-parser-go/commit/48f9ffda3a0053adcf1a68c5378bbeac9de9027c)) +* use slices.Concat instead of sometimes modifying r.Options ([c30d79a](https://github.com/CASParser/cas-parser-go/commit/c30d79aa4eb1f8b4d793fe5e641db7d0f385a9dc)) + + +### Chores + +* bump minimum go version to 1.22 ([54f1ae1](https://github.com/CASParser/cas-parser-go/commit/54f1ae1bedb207621a92b70a64220456781a8e37)) +* do not install brew dependencies in ./scripts/bootstrap by default ([a665c6a](https://github.com/CASParser/cas-parser-go/commit/a665c6aa0d14754a7f4ea644b2b53ac49dec0f0c)) +* **internal:** grammar fix (it's -> its) ([0af28e9](https://github.com/CASParser/cas-parser-go/commit/0af28e95581cfd4c2d9f729ee59f8213253da54a)) +* update more docs for 1.22 ([fc3b56e](https://github.com/CASParser/cas-parser-go/commit/fc3b56e861603f65fd1a61d6c843b220e94154cc)) + ## 0.0.3 (2025-09-06) Full Changelog: [v0.0.2...v0.0.3](https://github.com/CASParser/cas-parser-go/compare/v0.0.2...v0.0.3) diff --git a/README.md b/README.md index 4ba57e5..ff985d5 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ Or to pin the version: ```sh -go get -u 'github.com/CASParser/cas-parser-go@v0.0.3' +go get -u 'github.com/CASParser/cas-parser-go@v0.1.0' ``` diff --git a/internal/version.go b/internal/version.go index b5b3e63..02eac73 100644 --- a/internal/version.go +++ b/internal/version.go @@ -2,4 +2,4 @@ package internal -const PackageVersion = "0.0.3" // x-release-please-version +const PackageVersion = "0.1.0" // x-release-please-version