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/.stats.yml b/.stats.yml
index 92721c7..4465de7 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
+configured_endpoints: 4
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cas-parser%2Fcas-parser-7e6397bddc220d1a59b5e2c7e7c3ff38f1a6eb174f4e383e03bc49cf78c8c44f.yml
+openapi_spec_hash: cb852eeb4ce89c80f4246815cbe21f72
config_hash: cb5d75abef6264b5d86448caf7295afa
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 077729d..25b6824 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,30 @@
# Changelog
+## 0.1.0 (2026-01-05)
+
+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 ([4de0c4b](https://github.com/CASParser/cas-parser-go/commit/4de0c4b7936cbe00370980ab20aad02586090fcd))
+* **api:** api update ([c4b3ed4](https://github.com/CASParser/cas-parser-go/commit/c4b3ed40ff9e5d6ad1ac4ca57e0219d184038d20))
+* **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:** codegen related update ([b0d066b](https://github.com/CASParser/cas-parser-go/commit/b0d066be8e48533c4e0bf3f004c3c6ad96af0008))
+* **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/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/LICENSE b/LICENSE
index f1756ce..6bbb512 100644
--- a/LICENSE
+++ b/LICENSE
@@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
- Copyright 2025 Cas Parser
+ Copyright 2026 Cas Parser
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/README.md b/README.md
index 14db163..ff985d5 100644
--- a/README.md
+++ b/README.md
@@ -28,14 +28,14 @@ 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'
```
## Requirements
-This library requires Go 1.18+.
+This library requires Go 1.22+.
## Usage
@@ -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/api.md b/api.md
index ddecff9..cf396b9 100644
--- a/api.md
+++ b/api.md
@@ -12,11 +12,3 @@ Methods:
- client.CasParser.SmartParse(ctx context.Context, body casparser.CasParserSmartParseParams) (casparser.UnifiedResponse, error)
# CasGenerator
-
-Response Types:
-
-- casparser.CasGeneratorGenerateCasResponse
-
-Methods:
-
-- client.CasGenerator.GenerateCas(ctx context.Context, body casparser.CasGeneratorGenerateCasParams) (casparser.CasGeneratorGenerateCasResponse, error)
diff --git a/casgenerator.go b/casgenerator.go
index e1dea8b..4b3b086 100644
--- a/casgenerator.go
+++ b/casgenerator.go
@@ -3,14 +3,7 @@
package casparser
import (
- "context"
- "net/http"
-
- "github.com/CASParser/cas-parser-go/internal/apijson"
- "github.com/CASParser/cas-parser-go/internal/requestconfig"
"github.com/CASParser/cas-parser-go/option"
- "github.com/CASParser/cas-parser-go/packages/param"
- "github.com/CASParser/cas-parser-go/packages/respjson"
)
// CasGeneratorService contains methods and other services that help with
@@ -31,69 +24,3 @@ func NewCasGeneratorService(opts ...option.RequestOption) (r CasGeneratorService
r.Options = opts
return
}
-
-// This endpoint generates CAS (Consolidated Account Statement) documents by
-// 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...)
- path := "v4/generate"
- err = requestconfig.ExecuteNewRequest(ctx, http.MethodPost, path, body, &res, opts...)
- return
-}
-
-type CasGeneratorGenerateCasResponse struct {
- Msg string `json:"msg"`
- Status string `json:"status"`
- // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
- JSON struct {
- Msg respjson.Field
- Status respjson.Field
- ExtraFields map[string]respjson.Field
- raw string
- } `json:"-"`
-}
-
-// Returns the unmodified JSON received from the API
-func (r CasGeneratorGenerateCasResponse) RawJSON() string { return r.JSON.raw }
-func (r *CasGeneratorGenerateCasResponse) UnmarshalJSON(data []byte) error {
- return apijson.UnmarshalRoot(data, r)
-}
-
-type CasGeneratorGenerateCasParams struct {
- // Email address to receive the CAS document
- Email string `json:"email,required"`
- // Start date for the CAS period (format YYYY-MM-DD)
- FromDate string `json:"from_date,required"`
- // Password to protect the generated CAS PDF
- Password string `json:"password,required"`
- // End date for the CAS period (format YYYY-MM-DD)
- ToDate string `json:"to_date,required"`
- // PAN number (optional for some CAS authorities)
- PanNo param.Opt[string] `json:"pan_no,omitzero"`
- // CAS authority to generate the document from (currently only kfintech is
- // supported)
- //
- // Any of "kfintech", "cams", "cdsl", "nsdl".
- CasAuthority CasGeneratorGenerateCasParamsCasAuthority `json:"cas_authority,omitzero"`
- paramObj
-}
-
-func (r CasGeneratorGenerateCasParams) MarshalJSON() (data []byte, err error) {
- type shadow CasGeneratorGenerateCasParams
- return param.MarshalObject(r, (*shadow)(&r))
-}
-func (r *CasGeneratorGenerateCasParams) UnmarshalJSON(data []byte) error {
- return apijson.UnmarshalRoot(data, r)
-}
-
-// CAS authority to generate the document from (currently only kfintech is
-// supported)
-type CasGeneratorGenerateCasParamsCasAuthority string
-
-const (
- CasGeneratorGenerateCasParamsCasAuthorityKfintech CasGeneratorGenerateCasParamsCasAuthority = "kfintech"
- CasGeneratorGenerateCasParamsCasAuthorityCams CasGeneratorGenerateCasParamsCasAuthority = "cams"
- CasGeneratorGenerateCasParamsCasAuthorityCdsl CasGeneratorGenerateCasParamsCasAuthority = "cdsl"
- CasGeneratorGenerateCasParamsCasAuthorityNsdl CasGeneratorGenerateCasParamsCasAuthority = "nsdl"
-)
diff --git a/casgenerator_test.go b/casgenerator_test.go
deleted file mode 100644
index e093e18..0000000
--- a/casgenerator_test.go
+++ /dev/null
@@ -1,44 +0,0 @@
-// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-package casparser_test
-
-import (
- "context"
- "errors"
- "os"
- "testing"
-
- "github.com/CASParser/cas-parser-go"
- "github.com/CASParser/cas-parser-go/internal/testutil"
- "github.com/CASParser/cas-parser-go/option"
-)
-
-func TestCasGeneratorGenerateCasWithOptionalParams(t *testing.T) {
- t.Skip("Prism tests are disabled")
- baseURL := "http://localhost:4010"
- if envURL, ok := os.LookupEnv("TEST_API_BASE_URL"); ok {
- baseURL = envURL
- }
- if !testutil.CheckTestServer(t, baseURL) {
- return
- }
- client := casparser.NewClient(
- option.WithBaseURL(baseURL),
- option.WithAPIKey("My API Key"),
- )
- _, err := client.CasGenerator.GenerateCas(context.TODO(), casparser.CasGeneratorGenerateCasParams{
- Email: "user@example.com",
- FromDate: "2023-01-01",
- Password: "Abcdefghi12$",
- ToDate: "2023-12-31",
- CasAuthority: casparser.CasGeneratorGenerateCasParamsCasAuthorityKfintech,
- PanNo: casparser.String("ABCDE1234F"),
- })
- if err != nil {
- var apierr *casparser.Error
- if errors.As(err, &apierr) {
- t.Log(string(apierr.DumpRequest(true)))
- }
- t.Fatalf("err should be nil: %s", err.Error())
- }
-}
diff --git a/casparser.go b/casparser.go
index 66e6cca..78ebeaa 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
@@ -80,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
@@ -88,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
@@ -116,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].
@@ -127,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
@@ -204,11 +211,13 @@ func (r *UnifiedResponseDematAccountHoldings) UnmarshalJSON(data []byte) error {
type UnifiedResponseDematAccountHoldingsAif struct {
// Additional information specific to the AIF
- AdditionalInfo any `json:"additional_info"`
+ AdditionalInfo UnifiedResponseDematAccountHoldingsAifAdditionalInfo `json:"additional_info"`
// ISIN code of the AIF
Isin string `json:"isin"`
// Name of the AIF
Name string `json:"name"`
+ // List of transactions for this holding (beta)
+ Transactions []UnifiedResponseDematAccountHoldingsAifTransaction `json:"transactions"`
// Number of units held
Units float64 `json:"units"`
// Current market value of the holding
@@ -218,6 +227,7 @@ type UnifiedResponseDematAccountHoldingsAif struct {
AdditionalInfo respjson.Field
Isin respjson.Field
Name respjson.Field
+ Transactions respjson.Field
Units respjson.Field
Value respjson.Field
ExtraFields map[string]respjson.Field
@@ -231,13 +241,125 @@ func (r *UnifiedResponseDematAccountHoldingsAif) UnmarshalJSON(data []byte) erro
return apijson.UnmarshalRoot(data, r)
}
+// Additional information specific to the AIF
+type UnifiedResponseDematAccountHoldingsAifAdditionalInfo struct {
+ // Closing balance units for the statement period (beta)
+ CloseUnits float64 `json:"close_units,nullable"`
+ // Opening balance units for the statement period (beta)
+ OpenUnits float64 `json:"open_units,nullable"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ CloseUnits respjson.Field
+ OpenUnits respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r UnifiedResponseDematAccountHoldingsAifAdditionalInfo) RawJSON() string { return r.JSON.raw }
+func (r *UnifiedResponseDematAccountHoldingsAifAdditionalInfo) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+// Unified transaction schema for all holding types (MF folios, equities, bonds,
+// etc.)
+type UnifiedResponseDematAccountHoldingsAifTransaction struct {
+ // Additional transaction-specific fields that vary by source
+ AdditionalInfo UnifiedResponseDematAccountHoldingsAifTransactionAdditionalInfo `json:"additional_info"`
+ // Transaction amount in currency (computed from units × price/NAV)
+ Amount float64 `json:"amount,nullable"`
+ // Balance units after transaction
+ Balance float64 `json:"balance"`
+ // Transaction date (YYYY-MM-DD)
+ Date time.Time `json:"date" format:"date"`
+ // Transaction description/particulars
+ Description string `json:"description"`
+ // Dividend rate (for DIVIDEND_PAYOUT transactions)
+ DividendRate float64 `json:"dividend_rate,nullable"`
+ // NAV/price per unit on transaction date
+ Nav float64 `json:"nav,nullable"`
+ // Transaction type. Possible values are PURCHASE, PURCHASE_SIP, REDEMPTION,
+ // SWITCH_IN, SWITCH_IN_MERGER, SWITCH_OUT, SWITCH_OUT_MERGER, DIVIDEND_PAYOUT,
+ // DIVIDEND_REINVEST, SEGREGATION, STAMP_DUTY_TAX, TDS_TAX, STT_TAX, MISC,
+ // REVERSAL, UNKNOWN.
+ //
+ // Any of "PURCHASE", "PURCHASE_SIP", "REDEMPTION", "SWITCH_IN",
+ // "SWITCH_IN_MERGER", "SWITCH_OUT", "SWITCH_OUT_MERGER", "DIVIDEND_PAYOUT",
+ // "DIVIDEND_REINVEST", "SEGREGATION", "STAMP_DUTY_TAX", "TDS_TAX", "STT_TAX",
+ // "MISC", "REVERSAL", "UNKNOWN".
+ Type string `json:"type"`
+ // Number of units involved in transaction
+ Units float64 `json:"units"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ AdditionalInfo respjson.Field
+ Amount respjson.Field
+ Balance respjson.Field
+ Date respjson.Field
+ Description respjson.Field
+ DividendRate respjson.Field
+ Nav respjson.Field
+ Type respjson.Field
+ Units respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r UnifiedResponseDematAccountHoldingsAifTransaction) RawJSON() string { return r.JSON.raw }
+func (r *UnifiedResponseDematAccountHoldingsAifTransaction) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+// Additional transaction-specific fields that vary by source
+type UnifiedResponseDematAccountHoldingsAifTransactionAdditionalInfo struct {
+ // Capital withdrawal amount (CDSL MF transactions)
+ CapitalWithdrawal float64 `json:"capital_withdrawal"`
+ // Units credited (demat transactions)
+ Credit float64 `json:"credit"`
+ // Units debited (demat transactions)
+ Debit float64 `json:"debit"`
+ // Income distribution amount (CDSL MF transactions)
+ IncomeDistribution float64 `json:"income_distribution"`
+ // Order/transaction reference number (demat transactions)
+ OrderNo string `json:"order_no"`
+ // Price per unit (NSDL/CDSL MF transactions)
+ Price float64 `json:"price"`
+ // Stamp duty charged
+ StampDuty float64 `json:"stamp_duty"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ CapitalWithdrawal respjson.Field
+ Credit respjson.Field
+ Debit respjson.Field
+ IncomeDistribution respjson.Field
+ OrderNo respjson.Field
+ Price respjson.Field
+ StampDuty respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r UnifiedResponseDematAccountHoldingsAifTransactionAdditionalInfo) RawJSON() string {
+ return r.JSON.raw
+}
+func (r *UnifiedResponseDematAccountHoldingsAifTransactionAdditionalInfo) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
type UnifiedResponseDematAccountHoldingsCorporateBond struct {
// Additional information specific to the corporate bond
- AdditionalInfo any `json:"additional_info"`
+ AdditionalInfo UnifiedResponseDematAccountHoldingsCorporateBondAdditionalInfo `json:"additional_info"`
// ISIN code of the corporate bond
Isin string `json:"isin"`
// Name of the corporate bond
Name string `json:"name"`
+ // List of transactions for this holding (beta)
+ Transactions []UnifiedResponseDematAccountHoldingsCorporateBondTransaction `json:"transactions"`
// Number of units held
Units float64 `json:"units"`
// Current market value of the holding
@@ -247,6 +369,7 @@ type UnifiedResponseDematAccountHoldingsCorporateBond struct {
AdditionalInfo respjson.Field
Isin respjson.Field
Name respjson.Field
+ Transactions respjson.Field
Units respjson.Field
Value respjson.Field
ExtraFields map[string]respjson.Field
@@ -260,13 +383,129 @@ func (r *UnifiedResponseDematAccountHoldingsCorporateBond) UnmarshalJSON(data []
return apijson.UnmarshalRoot(data, r)
}
+// Additional information specific to the corporate bond
+type UnifiedResponseDematAccountHoldingsCorporateBondAdditionalInfo struct {
+ // Closing balance units for the statement period (beta)
+ CloseUnits float64 `json:"close_units,nullable"`
+ // Opening balance units for the statement period (beta)
+ OpenUnits float64 `json:"open_units,nullable"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ CloseUnits respjson.Field
+ OpenUnits respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r UnifiedResponseDematAccountHoldingsCorporateBondAdditionalInfo) RawJSON() string {
+ return r.JSON.raw
+}
+func (r *UnifiedResponseDematAccountHoldingsCorporateBondAdditionalInfo) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+// Unified transaction schema for all holding types (MF folios, equities, bonds,
+// etc.)
+type UnifiedResponseDematAccountHoldingsCorporateBondTransaction struct {
+ // Additional transaction-specific fields that vary by source
+ AdditionalInfo UnifiedResponseDematAccountHoldingsCorporateBondTransactionAdditionalInfo `json:"additional_info"`
+ // Transaction amount in currency (computed from units × price/NAV)
+ Amount float64 `json:"amount,nullable"`
+ // Balance units after transaction
+ Balance float64 `json:"balance"`
+ // Transaction date (YYYY-MM-DD)
+ Date time.Time `json:"date" format:"date"`
+ // Transaction description/particulars
+ Description string `json:"description"`
+ // Dividend rate (for DIVIDEND_PAYOUT transactions)
+ DividendRate float64 `json:"dividend_rate,nullable"`
+ // NAV/price per unit on transaction date
+ Nav float64 `json:"nav,nullable"`
+ // Transaction type. Possible values are PURCHASE, PURCHASE_SIP, REDEMPTION,
+ // SWITCH_IN, SWITCH_IN_MERGER, SWITCH_OUT, SWITCH_OUT_MERGER, DIVIDEND_PAYOUT,
+ // DIVIDEND_REINVEST, SEGREGATION, STAMP_DUTY_TAX, TDS_TAX, STT_TAX, MISC,
+ // REVERSAL, UNKNOWN.
+ //
+ // Any of "PURCHASE", "PURCHASE_SIP", "REDEMPTION", "SWITCH_IN",
+ // "SWITCH_IN_MERGER", "SWITCH_OUT", "SWITCH_OUT_MERGER", "DIVIDEND_PAYOUT",
+ // "DIVIDEND_REINVEST", "SEGREGATION", "STAMP_DUTY_TAX", "TDS_TAX", "STT_TAX",
+ // "MISC", "REVERSAL", "UNKNOWN".
+ Type string `json:"type"`
+ // Number of units involved in transaction
+ Units float64 `json:"units"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ AdditionalInfo respjson.Field
+ Amount respjson.Field
+ Balance respjson.Field
+ Date respjson.Field
+ Description respjson.Field
+ DividendRate respjson.Field
+ Nav respjson.Field
+ Type respjson.Field
+ Units respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r UnifiedResponseDematAccountHoldingsCorporateBondTransaction) RawJSON() string {
+ return r.JSON.raw
+}
+func (r *UnifiedResponseDematAccountHoldingsCorporateBondTransaction) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+// Additional transaction-specific fields that vary by source
+type UnifiedResponseDematAccountHoldingsCorporateBondTransactionAdditionalInfo struct {
+ // Capital withdrawal amount (CDSL MF transactions)
+ CapitalWithdrawal float64 `json:"capital_withdrawal"`
+ // Units credited (demat transactions)
+ Credit float64 `json:"credit"`
+ // Units debited (demat transactions)
+ Debit float64 `json:"debit"`
+ // Income distribution amount (CDSL MF transactions)
+ IncomeDistribution float64 `json:"income_distribution"`
+ // Order/transaction reference number (demat transactions)
+ OrderNo string `json:"order_no"`
+ // Price per unit (NSDL/CDSL MF transactions)
+ Price float64 `json:"price"`
+ // Stamp duty charged
+ StampDuty float64 `json:"stamp_duty"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ CapitalWithdrawal respjson.Field
+ Credit respjson.Field
+ Debit respjson.Field
+ IncomeDistribution respjson.Field
+ OrderNo respjson.Field
+ Price respjson.Field
+ StampDuty respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r UnifiedResponseDematAccountHoldingsCorporateBondTransactionAdditionalInfo) RawJSON() string {
+ return r.JSON.raw
+}
+func (r *UnifiedResponseDematAccountHoldingsCorporateBondTransactionAdditionalInfo) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
type UnifiedResponseDematAccountHoldingsDematMutualFund struct {
// Additional information specific to the mutual fund
- AdditionalInfo any `json:"additional_info"`
+ AdditionalInfo UnifiedResponseDematAccountHoldingsDematMutualFundAdditionalInfo `json:"additional_info"`
// ISIN code of the mutual fund
Isin string `json:"isin"`
// Name of the mutual fund
Name string `json:"name"`
+ // List of transactions for this holding (beta)
+ Transactions []UnifiedResponseDematAccountHoldingsDematMutualFundTransaction `json:"transactions"`
// Number of units held
Units float64 `json:"units"`
// Current market value of the holding
@@ -276,6 +515,7 @@ type UnifiedResponseDematAccountHoldingsDematMutualFund struct {
AdditionalInfo respjson.Field
Isin respjson.Field
Name respjson.Field
+ Transactions respjson.Field
Units respjson.Field
Value respjson.Field
ExtraFields map[string]respjson.Field
@@ -289,13 +529,129 @@ func (r *UnifiedResponseDematAccountHoldingsDematMutualFund) UnmarshalJSON(data
return apijson.UnmarshalRoot(data, r)
}
+// Additional information specific to the mutual fund
+type UnifiedResponseDematAccountHoldingsDematMutualFundAdditionalInfo struct {
+ // Closing balance units for the statement period (beta)
+ CloseUnits float64 `json:"close_units,nullable"`
+ // Opening balance units for the statement period (beta)
+ OpenUnits float64 `json:"open_units,nullable"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ CloseUnits respjson.Field
+ OpenUnits respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r UnifiedResponseDematAccountHoldingsDematMutualFundAdditionalInfo) RawJSON() string {
+ return r.JSON.raw
+}
+func (r *UnifiedResponseDematAccountHoldingsDematMutualFundAdditionalInfo) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+// Unified transaction schema for all holding types (MF folios, equities, bonds,
+// etc.)
+type UnifiedResponseDematAccountHoldingsDematMutualFundTransaction struct {
+ // Additional transaction-specific fields that vary by source
+ AdditionalInfo UnifiedResponseDematAccountHoldingsDematMutualFundTransactionAdditionalInfo `json:"additional_info"`
+ // Transaction amount in currency (computed from units × price/NAV)
+ Amount float64 `json:"amount,nullable"`
+ // Balance units after transaction
+ Balance float64 `json:"balance"`
+ // Transaction date (YYYY-MM-DD)
+ Date time.Time `json:"date" format:"date"`
+ // Transaction description/particulars
+ Description string `json:"description"`
+ // Dividend rate (for DIVIDEND_PAYOUT transactions)
+ DividendRate float64 `json:"dividend_rate,nullable"`
+ // NAV/price per unit on transaction date
+ Nav float64 `json:"nav,nullable"`
+ // Transaction type. Possible values are PURCHASE, PURCHASE_SIP, REDEMPTION,
+ // SWITCH_IN, SWITCH_IN_MERGER, SWITCH_OUT, SWITCH_OUT_MERGER, DIVIDEND_PAYOUT,
+ // DIVIDEND_REINVEST, SEGREGATION, STAMP_DUTY_TAX, TDS_TAX, STT_TAX, MISC,
+ // REVERSAL, UNKNOWN.
+ //
+ // Any of "PURCHASE", "PURCHASE_SIP", "REDEMPTION", "SWITCH_IN",
+ // "SWITCH_IN_MERGER", "SWITCH_OUT", "SWITCH_OUT_MERGER", "DIVIDEND_PAYOUT",
+ // "DIVIDEND_REINVEST", "SEGREGATION", "STAMP_DUTY_TAX", "TDS_TAX", "STT_TAX",
+ // "MISC", "REVERSAL", "UNKNOWN".
+ Type string `json:"type"`
+ // Number of units involved in transaction
+ Units float64 `json:"units"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ AdditionalInfo respjson.Field
+ Amount respjson.Field
+ Balance respjson.Field
+ Date respjson.Field
+ Description respjson.Field
+ DividendRate respjson.Field
+ Nav respjson.Field
+ Type respjson.Field
+ Units respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r UnifiedResponseDematAccountHoldingsDematMutualFundTransaction) RawJSON() string {
+ return r.JSON.raw
+}
+func (r *UnifiedResponseDematAccountHoldingsDematMutualFundTransaction) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+// Additional transaction-specific fields that vary by source
+type UnifiedResponseDematAccountHoldingsDematMutualFundTransactionAdditionalInfo struct {
+ // Capital withdrawal amount (CDSL MF transactions)
+ CapitalWithdrawal float64 `json:"capital_withdrawal"`
+ // Units credited (demat transactions)
+ Credit float64 `json:"credit"`
+ // Units debited (demat transactions)
+ Debit float64 `json:"debit"`
+ // Income distribution amount (CDSL MF transactions)
+ IncomeDistribution float64 `json:"income_distribution"`
+ // Order/transaction reference number (demat transactions)
+ OrderNo string `json:"order_no"`
+ // Price per unit (NSDL/CDSL MF transactions)
+ Price float64 `json:"price"`
+ // Stamp duty charged
+ StampDuty float64 `json:"stamp_duty"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ CapitalWithdrawal respjson.Field
+ Credit respjson.Field
+ Debit respjson.Field
+ IncomeDistribution respjson.Field
+ OrderNo respjson.Field
+ Price respjson.Field
+ StampDuty respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r UnifiedResponseDematAccountHoldingsDematMutualFundTransactionAdditionalInfo) RawJSON() string {
+ return r.JSON.raw
+}
+func (r *UnifiedResponseDematAccountHoldingsDematMutualFundTransactionAdditionalInfo) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
type UnifiedResponseDematAccountHoldingsEquity struct {
// Additional information specific to the equity
- AdditionalInfo any `json:"additional_info"`
+ AdditionalInfo UnifiedResponseDematAccountHoldingsEquityAdditionalInfo `json:"additional_info"`
// ISIN code of the equity
Isin string `json:"isin"`
// Name of the equity
Name string `json:"name"`
+ // List of transactions for this holding (beta)
+ Transactions []UnifiedResponseDematAccountHoldingsEquityTransaction `json:"transactions"`
// Number of units held
Units float64 `json:"units"`
// Current market value of the holding
@@ -305,6 +661,7 @@ type UnifiedResponseDematAccountHoldingsEquity struct {
AdditionalInfo respjson.Field
Isin respjson.Field
Name respjson.Field
+ Transactions respjson.Field
Units respjson.Field
Value respjson.Field
ExtraFields map[string]respjson.Field
@@ -318,13 +675,125 @@ func (r *UnifiedResponseDematAccountHoldingsEquity) UnmarshalJSON(data []byte) e
return apijson.UnmarshalRoot(data, r)
}
+// Additional information specific to the equity
+type UnifiedResponseDematAccountHoldingsEquityAdditionalInfo struct {
+ // Closing balance units for the statement period (beta)
+ CloseUnits float64 `json:"close_units,nullable"`
+ // Opening balance units for the statement period (beta)
+ OpenUnits float64 `json:"open_units,nullable"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ CloseUnits respjson.Field
+ OpenUnits respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r UnifiedResponseDematAccountHoldingsEquityAdditionalInfo) RawJSON() string { return r.JSON.raw }
+func (r *UnifiedResponseDematAccountHoldingsEquityAdditionalInfo) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+// Unified transaction schema for all holding types (MF folios, equities, bonds,
+// etc.)
+type UnifiedResponseDematAccountHoldingsEquityTransaction struct {
+ // Additional transaction-specific fields that vary by source
+ AdditionalInfo UnifiedResponseDematAccountHoldingsEquityTransactionAdditionalInfo `json:"additional_info"`
+ // Transaction amount in currency (computed from units × price/NAV)
+ Amount float64 `json:"amount,nullable"`
+ // Balance units after transaction
+ Balance float64 `json:"balance"`
+ // Transaction date (YYYY-MM-DD)
+ Date time.Time `json:"date" format:"date"`
+ // Transaction description/particulars
+ Description string `json:"description"`
+ // Dividend rate (for DIVIDEND_PAYOUT transactions)
+ DividendRate float64 `json:"dividend_rate,nullable"`
+ // NAV/price per unit on transaction date
+ Nav float64 `json:"nav,nullable"`
+ // Transaction type. Possible values are PURCHASE, PURCHASE_SIP, REDEMPTION,
+ // SWITCH_IN, SWITCH_IN_MERGER, SWITCH_OUT, SWITCH_OUT_MERGER, DIVIDEND_PAYOUT,
+ // DIVIDEND_REINVEST, SEGREGATION, STAMP_DUTY_TAX, TDS_TAX, STT_TAX, MISC,
+ // REVERSAL, UNKNOWN.
+ //
+ // Any of "PURCHASE", "PURCHASE_SIP", "REDEMPTION", "SWITCH_IN",
+ // "SWITCH_IN_MERGER", "SWITCH_OUT", "SWITCH_OUT_MERGER", "DIVIDEND_PAYOUT",
+ // "DIVIDEND_REINVEST", "SEGREGATION", "STAMP_DUTY_TAX", "TDS_TAX", "STT_TAX",
+ // "MISC", "REVERSAL", "UNKNOWN".
+ Type string `json:"type"`
+ // Number of units involved in transaction
+ Units float64 `json:"units"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ AdditionalInfo respjson.Field
+ Amount respjson.Field
+ Balance respjson.Field
+ Date respjson.Field
+ Description respjson.Field
+ DividendRate respjson.Field
+ Nav respjson.Field
+ Type respjson.Field
+ Units respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r UnifiedResponseDematAccountHoldingsEquityTransaction) RawJSON() string { return r.JSON.raw }
+func (r *UnifiedResponseDematAccountHoldingsEquityTransaction) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+// Additional transaction-specific fields that vary by source
+type UnifiedResponseDematAccountHoldingsEquityTransactionAdditionalInfo struct {
+ // Capital withdrawal amount (CDSL MF transactions)
+ CapitalWithdrawal float64 `json:"capital_withdrawal"`
+ // Units credited (demat transactions)
+ Credit float64 `json:"credit"`
+ // Units debited (demat transactions)
+ Debit float64 `json:"debit"`
+ // Income distribution amount (CDSL MF transactions)
+ IncomeDistribution float64 `json:"income_distribution"`
+ // Order/transaction reference number (demat transactions)
+ OrderNo string `json:"order_no"`
+ // Price per unit (NSDL/CDSL MF transactions)
+ Price float64 `json:"price"`
+ // Stamp duty charged
+ StampDuty float64 `json:"stamp_duty"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ CapitalWithdrawal respjson.Field
+ Credit respjson.Field
+ Debit respjson.Field
+ IncomeDistribution respjson.Field
+ OrderNo respjson.Field
+ Price respjson.Field
+ StampDuty respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r UnifiedResponseDematAccountHoldingsEquityTransactionAdditionalInfo) RawJSON() string {
+ return r.JSON.raw
+}
+func (r *UnifiedResponseDematAccountHoldingsEquityTransactionAdditionalInfo) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
type UnifiedResponseDematAccountHoldingsGovernmentSecurity struct {
// Additional information specific to the government security
- AdditionalInfo any `json:"additional_info"`
+ AdditionalInfo UnifiedResponseDematAccountHoldingsGovernmentSecurityAdditionalInfo `json:"additional_info"`
// ISIN code of the government security
Isin string `json:"isin"`
// Name of the government security
Name string `json:"name"`
+ // List of transactions for this holding (beta)
+ Transactions []UnifiedResponseDematAccountHoldingsGovernmentSecurityTransaction `json:"transactions"`
// Number of units held
Units float64 `json:"units"`
// Current market value of the holding
@@ -334,6 +803,7 @@ type UnifiedResponseDematAccountHoldingsGovernmentSecurity struct {
AdditionalInfo respjson.Field
Isin respjson.Field
Name respjson.Field
+ Transactions respjson.Field
Units respjson.Field
Value respjson.Field
ExtraFields map[string]respjson.Field
@@ -347,6 +817,140 @@ func (r *UnifiedResponseDematAccountHoldingsGovernmentSecurity) UnmarshalJSON(da
return apijson.UnmarshalRoot(data, r)
}
+// Additional information specific to the government security
+type UnifiedResponseDematAccountHoldingsGovernmentSecurityAdditionalInfo struct {
+ // Closing balance units for the statement period (beta)
+ CloseUnits float64 `json:"close_units,nullable"`
+ // Opening balance units for the statement period (beta)
+ OpenUnits float64 `json:"open_units,nullable"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ CloseUnits respjson.Field
+ OpenUnits respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r UnifiedResponseDematAccountHoldingsGovernmentSecurityAdditionalInfo) RawJSON() string {
+ return r.JSON.raw
+}
+func (r *UnifiedResponseDematAccountHoldingsGovernmentSecurityAdditionalInfo) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+// Unified transaction schema for all holding types (MF folios, equities, bonds,
+// etc.)
+type UnifiedResponseDematAccountHoldingsGovernmentSecurityTransaction struct {
+ // Additional transaction-specific fields that vary by source
+ AdditionalInfo UnifiedResponseDematAccountHoldingsGovernmentSecurityTransactionAdditionalInfo `json:"additional_info"`
+ // Transaction amount in currency (computed from units × price/NAV)
+ Amount float64 `json:"amount,nullable"`
+ // Balance units after transaction
+ Balance float64 `json:"balance"`
+ // Transaction date (YYYY-MM-DD)
+ Date time.Time `json:"date" format:"date"`
+ // Transaction description/particulars
+ Description string `json:"description"`
+ // Dividend rate (for DIVIDEND_PAYOUT transactions)
+ DividendRate float64 `json:"dividend_rate,nullable"`
+ // NAV/price per unit on transaction date
+ Nav float64 `json:"nav,nullable"`
+ // Transaction type. Possible values are PURCHASE, PURCHASE_SIP, REDEMPTION,
+ // SWITCH_IN, SWITCH_IN_MERGER, SWITCH_OUT, SWITCH_OUT_MERGER, DIVIDEND_PAYOUT,
+ // DIVIDEND_REINVEST, SEGREGATION, STAMP_DUTY_TAX, TDS_TAX, STT_TAX, MISC,
+ // REVERSAL, UNKNOWN.
+ //
+ // Any of "PURCHASE", "PURCHASE_SIP", "REDEMPTION", "SWITCH_IN",
+ // "SWITCH_IN_MERGER", "SWITCH_OUT", "SWITCH_OUT_MERGER", "DIVIDEND_PAYOUT",
+ // "DIVIDEND_REINVEST", "SEGREGATION", "STAMP_DUTY_TAX", "TDS_TAX", "STT_TAX",
+ // "MISC", "REVERSAL", "UNKNOWN".
+ Type string `json:"type"`
+ // Number of units involved in transaction
+ Units float64 `json:"units"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ AdditionalInfo respjson.Field
+ Amount respjson.Field
+ Balance respjson.Field
+ Date respjson.Field
+ Description respjson.Field
+ DividendRate respjson.Field
+ Nav respjson.Field
+ Type respjson.Field
+ Units respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r UnifiedResponseDematAccountHoldingsGovernmentSecurityTransaction) RawJSON() string {
+ return r.JSON.raw
+}
+func (r *UnifiedResponseDematAccountHoldingsGovernmentSecurityTransaction) UnmarshalJSON(data []byte) error {
+ return apijson.UnmarshalRoot(data, r)
+}
+
+// Additional transaction-specific fields that vary by source
+type UnifiedResponseDematAccountHoldingsGovernmentSecurityTransactionAdditionalInfo struct {
+ // Capital withdrawal amount (CDSL MF transactions)
+ CapitalWithdrawal float64 `json:"capital_withdrawal"`
+ // Units credited (demat transactions)
+ Credit float64 `json:"credit"`
+ // Units debited (demat transactions)
+ Debit float64 `json:"debit"`
+ // Income distribution amount (CDSL MF transactions)
+ IncomeDistribution float64 `json:"income_distribution"`
+ // Order/transaction reference number (demat transactions)
+ OrderNo string `json:"order_no"`
+ // Price per unit (NSDL/CDSL MF transactions)
+ Price float64 `json:"price"`
+ // Stamp duty charged
+ StampDuty float64 `json:"stamp_duty"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ CapitalWithdrawal respjson.Field
+ Credit respjson.Field
+ Debit respjson.Field
+ IncomeDistribution respjson.Field
+ OrderNo respjson.Field
+ Price respjson.Field
+ StampDuty respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r UnifiedResponseDematAccountHoldingsGovernmentSecurityTransactionAdditionalInfo) RawJSON() string {
+ return r.JSON.raw
+}
+func (r *UnifiedResponseDematAccountHoldingsGovernmentSecurityTransactionAdditionalInfo) UnmarshalJSON(data []byte) error {
+ 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].
@@ -490,6 +1094,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"`
@@ -500,6 +1106,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
@@ -538,6 +1145,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"`
@@ -591,10 +1218,10 @@ type UnifiedResponseMutualFundSchemeAdditionalInfo struct {
Advisor string `json:"advisor"`
// AMFI code for the scheme (CAMS/KFintech)
Amfi string `json:"amfi"`
- // Closing balance units (CAMS/KFintech)
- CloseUnits float64 `json:"close_units"`
- // Opening balance units (CAMS/KFintech)
- OpenUnits float64 `json:"open_units"`
+ // Closing balance units for the statement period
+ CloseUnits float64 `json:"close_units,nullable"`
+ // Opening balance units for the statement period
+ OpenUnits float64 `json:"open_units,nullable"`
// RTA code for the scheme (CAMS/KFintech)
RtaCode string `json:"rta_code"`
// JSON contains metadata for fields, check presence with [respjson.Field.Valid].
@@ -635,38 +1262,48 @@ func (r *UnifiedResponseMutualFundSchemeGain) UnmarshalJSON(data []byte) error {
return apijson.UnmarshalRoot(data, r)
}
+// Unified transaction schema for all holding types (MF folios, equities, bonds,
+// etc.)
type UnifiedResponseMutualFundSchemeTransaction struct {
- // Transaction amount
- Amount float64 `json:"amount"`
+ // Additional transaction-specific fields that vary by source
+ AdditionalInfo UnifiedResponseMutualFundSchemeTransactionAdditionalInfo `json:"additional_info"`
+ // Transaction amount in currency (computed from units × price/NAV)
+ Amount float64 `json:"amount,nullable"`
// Balance units after transaction
Balance float64 `json:"balance"`
- // Transaction date
+ // Transaction date (YYYY-MM-DD)
Date time.Time `json:"date" format:"date"`
- // Transaction description
+ // Transaction description/particulars
Description string `json:"description"`
- // Dividend rate (for dividend transactions)
- DividendRate float64 `json:"dividend_rate"`
- // NAV on transaction date
- Nav float64 `json:"nav"`
- // Transaction type detected based on description. Possible values are
- // PURCHASE,PURCHASE_SIP,REDEMPTION,SWITCH_IN,SWITCH_IN_MERGER,SWITCH_OUT,SWITCH_OUT_MERGER,DIVIDEND_PAYOUT,DIVIDEND_REINVESTMENT,SEGREGATION,STAMP_DUTY_TAX,TDS_TAX,STT_TAX,MISC.
- // If dividend_rate is present, then possible values are dividend_rate is
- // applicable only for DIVIDEND_PAYOUT and DIVIDEND_REINVESTMENT.
+ // Dividend rate (for DIVIDEND_PAYOUT transactions)
+ DividendRate float64 `json:"dividend_rate,nullable"`
+ // NAV/price per unit on transaction date
+ Nav float64 `json:"nav,nullable"`
+ // Transaction type. Possible values are PURCHASE, PURCHASE_SIP, REDEMPTION,
+ // SWITCH_IN, SWITCH_IN_MERGER, SWITCH_OUT, SWITCH_OUT_MERGER, DIVIDEND_PAYOUT,
+ // DIVIDEND_REINVEST, SEGREGATION, STAMP_DUTY_TAX, TDS_TAX, STT_TAX, MISC,
+ // REVERSAL, UNKNOWN.
+ //
+ // Any of "PURCHASE", "PURCHASE_SIP", "REDEMPTION", "SWITCH_IN",
+ // "SWITCH_IN_MERGER", "SWITCH_OUT", "SWITCH_OUT_MERGER", "DIVIDEND_PAYOUT",
+ // "DIVIDEND_REINVEST", "SEGREGATION", "STAMP_DUTY_TAX", "TDS_TAX", "STT_TAX",
+ // "MISC", "REVERSAL", "UNKNOWN".
Type string `json:"type"`
- // Number of units involved
+ // Number of units involved in transaction
Units float64 `json:"units"`
// JSON contains metadata for fields, check presence with [respjson.Field.Valid].
JSON struct {
- Amount respjson.Field
- Balance respjson.Field
- Date respjson.Field
- Description respjson.Field
- DividendRate respjson.Field
- Nav respjson.Field
- Type respjson.Field
- Units respjson.Field
- ExtraFields map[string]respjson.Field
- raw string
+ AdditionalInfo respjson.Field
+ Amount respjson.Field
+ Balance respjson.Field
+ Date respjson.Field
+ Description respjson.Field
+ DividendRate respjson.Field
+ Nav respjson.Field
+ Type respjson.Field
+ Units respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
} `json:"-"`
}
@@ -676,6 +1313,148 @@ func (r *UnifiedResponseMutualFundSchemeTransaction) UnmarshalJSON(data []byte)
return apijson.UnmarshalRoot(data, r)
}
+// Additional transaction-specific fields that vary by source
+type UnifiedResponseMutualFundSchemeTransactionAdditionalInfo struct {
+ // Capital withdrawal amount (CDSL MF transactions)
+ CapitalWithdrawal float64 `json:"capital_withdrawal"`
+ // Units credited (demat transactions)
+ Credit float64 `json:"credit"`
+ // Units debited (demat transactions)
+ Debit float64 `json:"debit"`
+ // Income distribution amount (CDSL MF transactions)
+ IncomeDistribution float64 `json:"income_distribution"`
+ // Order/transaction reference number (demat transactions)
+ OrderNo string `json:"order_no"`
+ // Price per unit (NSDL/CDSL MF transactions)
+ Price float64 `json:"price"`
+ // Stamp duty charged
+ StampDuty float64 `json:"stamp_duty"`
+ // JSON contains metadata for fields, check presence with [respjson.Field.Valid].
+ JSON struct {
+ CapitalWithdrawal respjson.Field
+ Credit respjson.Field
+ Debit respjson.Field
+ IncomeDistribution respjson.Field
+ OrderNo respjson.Field
+ Price respjson.Field
+ StampDuty respjson.Field
+ ExtraFields map[string]respjson.Field
+ raw string
+ } `json:"-"`
+}
+
+// Returns the unmodified JSON received from the API
+func (r UnifiedResponseMutualFundSchemeTransactionAdditionalInfo) RawJSON() string { return r.JSON.raw }
+func (r *UnifiedResponseMutualFundSchemeTransactionAdditionalInfo) UnmarshalJSON(data []byte) error {
+ 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
@@ -699,11 +1478,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:"-"`
@@ -775,6 +1556,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"`
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...)
}
diff --git a/go.mod b/go.mod
index d01a0e4..967552e 100644
--- a/go.mod
+++ b/go.mod
@@ -1,9 +1,9 @@
module github.com/CASParser/cas-parser-go
-go 1.21
+go 1.22
require (
- github.com/tidwall/gjson v1.14.4
+ github.com/tidwall/gjson v1.18.0
github.com/tidwall/sjson v1.2.5
)
diff --git a/go.sum b/go.sum
index a70a5e0..32ba293 100644
--- a/go.sum
+++ b/go.sum
@@ -1,6 +1,6 @@
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
-github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
-github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
+github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
+github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
diff --git a/internal/apiform/encoder.go b/internal/apiform/encoder.go
index 82f21a7..c2bfd45 100644
--- a/internal/apiform/encoder.go
+++ b/internal/apiform/encoder.go
@@ -60,6 +60,7 @@ type encoderField struct {
type encoderEntry struct {
reflect.Type
dateFormat string
+ arrayFmt string
root bool
}
@@ -77,6 +78,7 @@ func (e *encoder) typeEncoder(t reflect.Type) encoderFunc {
entry := encoderEntry{
Type: t,
dateFormat: e.dateFormat,
+ arrayFmt: e.arrayFmt,
root: e.root,
}
@@ -178,34 +180,9 @@ func (e *encoder) newPrimitiveTypeEncoder(t reflect.Type) encoderFunc {
}
}
-func arrayKeyEncoder(arrayFmt string) func(string, int) string {
- var keyFn func(string, int) string
- switch arrayFmt {
- case "comma", "repeat":
- keyFn = func(k string, _ int) string { return k }
- case "brackets":
- keyFn = func(key string, _ int) string { return key + "[]" }
- case "indices:dots":
- keyFn = func(k string, i int) string {
- if k == "" {
- return strconv.Itoa(i)
- }
- return k + "." + strconv.Itoa(i)
- }
- case "indices:brackets":
- keyFn = func(k string, i int) string {
- if k == "" {
- return strconv.Itoa(i)
- }
- return k + "[" + strconv.Itoa(i) + "]"
- }
- }
- return keyFn
-}
-
func (e *encoder) newArrayTypeEncoder(t reflect.Type) encoderFunc {
itemEncoder := e.typeEncoder(t.Elem())
- keyFn := arrayKeyEncoder(e.arrayFmt)
+ keyFn := e.arrayKeyEncoder()
return func(key string, v reflect.Value, writer *multipart.Writer) error {
if keyFn == nil {
return fmt.Errorf("apiform: unsupported array format")
@@ -303,13 +280,10 @@ func (e *encoder) newStructTypeEncoder(t reflect.Type) encoderFunc {
})
return func(key string, value reflect.Value, writer *multipart.Writer) error {
- if key != "" {
- key = key + "."
- }
-
+ keyFn := e.objKeyEncoder(key)
for _, ef := range encoderFields {
field := value.FieldByIndex(ef.idx)
- err := ef.fn(key+ef.tag.name, field, writer)
+ err := ef.fn(keyFn(ef.tag.name), field, writer)
if err != nil {
return err
}
@@ -405,6 +379,43 @@ func (e *encoder) newReaderTypeEncoder() encoderFunc {
}
}
+func (e encoder) arrayKeyEncoder() func(string, int) string {
+ var keyFn func(string, int) string
+ switch e.arrayFmt {
+ case "comma", "repeat":
+ keyFn = func(k string, _ int) string { return k }
+ case "brackets":
+ keyFn = func(key string, _ int) string { return key + "[]" }
+ case "indices:dots":
+ keyFn = func(k string, i int) string {
+ if k == "" {
+ return strconv.Itoa(i)
+ }
+ return k + "." + strconv.Itoa(i)
+ }
+ case "indices:brackets":
+ keyFn = func(k string, i int) string {
+ if k == "" {
+ return strconv.Itoa(i)
+ }
+ return k + "[" + strconv.Itoa(i) + "]"
+ }
+ }
+ return keyFn
+}
+
+func (e encoder) objKeyEncoder(parent string) func(string) string {
+ if parent == "" {
+ return func(child string) string { return child }
+ }
+ switch e.arrayFmt {
+ case "brackets":
+ return func(child string) string { return parent + "[" + child + "]" }
+ default:
+ return func(child string) string { return parent + "." + child }
+ }
+}
+
// Given a []byte of json (may either be an empty object or an object that already contains entries)
// encode all of the entries in the map to the json byte array.
func (e *encoder) encodeMapEntries(key string, v reflect.Value, writer *multipart.Writer) error {
@@ -413,10 +424,6 @@ func (e *encoder) encodeMapEntries(key string, v reflect.Value, writer *multipar
value reflect.Value
}
- if key != "" {
- key = key + "."
- }
-
pairs := []mapPair{}
iter := v.MapRange()
@@ -434,8 +441,9 @@ func (e *encoder) encodeMapEntries(key string, v reflect.Value, writer *multipar
})
elementEncoder := e.typeEncoder(v.Type().Elem())
+ keyFn := e.objKeyEncoder(key)
for _, p := range pairs {
- err := elementEncoder(key+string(p.key), p.value, writer)
+ err := elementEncoder(keyFn(p.key), p.value, writer)
if err != nil {
return err
}
diff --git a/internal/apiform/form_test.go b/internal/apiform/form_test.go
index 227e50a..a660e74 100644
--- a/internal/apiform/form_test.go
+++ b/internal/apiform/form_test.go
@@ -123,6 +123,18 @@ type StructUnion struct {
param.APIUnion
}
+type MultipartMarshalerParent struct {
+ Middle MultipartMarshalerMiddleNext `form:"middle"`
+}
+
+type MultipartMarshalerMiddleNext struct {
+ MiddleNext MultipartMarshalerMiddle `form:"middleNext"`
+}
+
+type MultipartMarshalerMiddle struct {
+ Child int `form:"child"`
+}
+
var tests = map[string]struct {
buf string
val any
@@ -366,6 +378,19 @@ true
},
},
},
+ "recursive_struct,brackets": {
+ `--xxx
+Content-Disposition: form-data; name="child[name]"
+
+Alex
+--xxx
+Content-Disposition: form-data; name="name"
+
+Robert
+--xxx--
+`,
+ Recursive{Name: "Robert", Child: &Recursive{Name: "Alex"}},
+ },
"recursive_struct": {
`--xxx
@@ -529,6 +554,30 @@ Content-Disposition: form-data; name="union"
Union: UnionTime(time.Date(2010, 05, 23, 0, 0, 0, 0, time.UTC)),
},
},
+ "deeply-nested-struct,brackets": {
+ `--xxx
+Content-Disposition: form-data; name="middle[middleNext][child]"
+
+10
+--xxx--
+`,
+ MultipartMarshalerParent{
+ Middle: MultipartMarshalerMiddleNext{
+ MiddleNext: MultipartMarshalerMiddle{
+ Child: 10,
+ },
+ },
+ },
+ },
+ "deeply-nested-map,brackets": {
+ `--xxx
+Content-Disposition: form-data; name="middle[middleNext][child]"
+
+10
+--xxx--
+`,
+ map[string]any{"middle": map[string]any{"middleNext": map[string]any{"child": 10}}},
+ },
}
func TestEncode(t *testing.T) {
@@ -553,7 +602,7 @@ func TestEncode(t *testing.T) {
}
raw := buf.Bytes()
if string(raw) != strings.ReplaceAll(test.buf, "\n", "\r\n") {
- t.Errorf("expected %+#v to serialize to '%s' but got '%s'", test.val, test.buf, string(raw))
+ t.Errorf("expected %+#v to serialize to '%s' but got '%s' (with format %s)", test.val, test.buf, string(raw), arrayFmt)
}
})
}
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/enum.go b/internal/apijson/enum.go
index 18b218a..5bef11c 100644
--- a/internal/apijson/enum.go
+++ b/internal/apijson/enum.go
@@ -29,7 +29,7 @@ type validatorFunc func(reflect.Value) exactness
var validators sync.Map
var validationRegistry = map[reflect.Type][]validationEntry{}
-func RegisterFieldValidator[T any, V string | bool | int](fieldName string, values ...V) {
+func RegisterFieldValidator[T any, V string | bool | int | float64](fieldName string, values ...V) {
var t T
parentType := reflect.TypeOf(t)
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/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(...): ...
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
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
}
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"`
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
diff --git a/usage_test.go b/usage_test.go
index 071d9f8..5b017e4 100644
--- a/usage_test.go
+++ b/usage_test.go
@@ -24,6 +24,7 @@ func TestUsage(t *testing.T) {
option.WithBaseURL(baseURL),
option.WithAPIKey("My API Key"),
)
+ t.Skip("Prism tests are disabled")
unifiedResponse, err := client.CasParser.SmartParse(context.TODO(), casparser.CasParserSmartParseParams{
Password: casparser.String("ABCDF"),
PdfURL: casparser.String("https://your-cas-pdf-url-here.com"),