diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index bfe45fa6b168e..308ffd39a00dd 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -13,6 +13,7 @@ on: - 'rust/cubeorchestrator/**' - 'rust/cubeshared/**' - 'rust/cubesqlplanner/**' + - 'rust/cubetranspilers/**' - '.eslintrc.js' - '.prettierrc' - 'package.json' @@ -32,6 +33,7 @@ on: - 'rust/cubeorchestrator/**' - 'rust/cubeshared/**' - 'rust/cubesqlplanner/**' + - 'rust/cubetranspilers/**' - '.eslintrc.js' - '.prettierrc' - 'package.json' @@ -55,10 +57,15 @@ jobs: node-version: [20.x, 22.x] python-version: [3.11] transpile-worker-threads: [false, true] + transpile-native: [false, true] + exclude: + - transpile-worker-threads: true + transpile-native: true fail-fast: false env: CUBEJS_TRANSPILATION_WORKER_THREADS: ${{ matrix.transpile-worker-threads }} + CUBEJS_TRANSPILATION_NATIVE: ${{ matrix.transpile-native }} steps: - id: get-tag-out run: echo "$OUT" @@ -133,6 +140,9 @@ jobs: - name: Cargo test cubeshared run: | cargo test --manifest-path rust/cubeshared/Cargo.toml -j 1 + - name: Cargo test cubetranspilers + run: | + cargo test --manifest-path rust/cubetranspilers/Cargo.toml -j 1 # - name: Cargo test cubesql # run: | # cargo test --manifest-path rust/cubesql/Cargo.toml -j 1 @@ -196,6 +206,9 @@ jobs: - name: Cargo fmt cubeshared run: | cargo fmt --manifest-path rust/cubeshared/Cargo.toml -- --check + - name: Cargo fmt cubetranspilers + run: | + cargo fmt --manifest-path rust/cubetranspilers/Cargo.toml -- --check # - name: Cargo fmt cubesql # run: | # cargo fmt --manifest-path rust/cubesql/Cargo.toml -- --check @@ -263,6 +276,9 @@ jobs: - name: Cargo build cubeshared run: | cargo build --manifest-path rust/cubeshared/Cargo.toml -j 4 + - name: Cargo build cubetranspilers + run: | + cargo build --manifest-path rust/cubetranspilers/Cargo.toml -j 4 # - name: Cargo build cubesql # run: | # cargo build --manifest-path rust/cubesql/Cargo.toml -j 4 diff --git a/docs/pages/guides/recipes/auth/sql-api-ldap.mdx b/docs/pages/guides/recipes/auth/sql-api-ldap.mdx index 5820da26e59f9..0524c677ccbe4 100644 --- a/docs/pages/guides/recipes/auth/sql-api-ldap.mdx +++ b/docs/pages/guides/recipes/auth/sql-api-ldap.mdx @@ -26,6 +26,13 @@ Here's a step-by-step breakdown of the authentication flow: * Cube verifies the credentials via the Secure LDAP service. * If credentials are correct, users get access to the dataset. + + +In this recipe, the SQL API is used to connect to [Power BI][ref-powerbi]. +Currently, it's recommended to use the [DAX API][ref-dax-api]. + + + ## Configuration To verify the credentials, use the @@ -177,4 +184,8 @@ module.exports = { } }; -``` \ No newline at end of file +``` + + +[ref-dax-api]: /product/apis-integrations/dax-api +[ref-powerbi]: /product/configuration/visualization-tools/powerbi \ No newline at end of file diff --git a/docs/pages/product/apis-integrations.mdx b/docs/pages/product/apis-integrations.mdx index 82c7b3fe48c8c..22abec4f2cf90 100644 --- a/docs/pages/product/apis-integrations.mdx +++ b/docs/pages/product/apis-integrations.mdx @@ -9,37 +9,36 @@ With a rich set of APIs, Cube can power and deliver data to all kinds of data applications. [Data APIs](#data-apis) are used to run queries against the data model. -Despite using various transports and query formats, all data APIs share common +Despite varying protocols and query formats, all data APIs share common [querying concepts][ref-queries]. - + Also, there are [management APIs](#management-apis) to control Cube deployments externally. - + ## Data APIs A few rules of thumb to help you choose an API: -* When implementing internal or self-serve [business intelligence][cube-issbi] use -case, pick the [SQL API][ref-sql-api] and [Semantic Layer Sync][ref-sls]. The -SQL API allows querying Cube with a Postgres-compatible [dialect of -SQL][ref-sql-syntax], either by writing queries manually or generating them with -BI tools. +* To connect to [Microsoft Power BI][ref-powerbi], use the [DAX API][ref-dax-api]. -* To connect to Microsoft Excel, use the [MDX API][ref-mdx-api]. To connect to -Google Sheets, use [Cube Cloud for Sheets][ref-cube-cloud-for-sheets]. +* To connect to [Microsoft Excel][ref-excel], use either the [MDX API][ref-mdx-api] +or [Cube Cloud for Excel][ref-cube-cloud-for-excel]. -* When implementing [embedded analytics][cube-ea] and [real-time -analytics][cube-rta] use cases, pick [REST API][ref-rest-api] or [GraphQL -API][ref-graphql-api]. Also, the [JavaScript SDK][ref-js-sdk] will simplify -integration with your front-end code. The REST API uses a [JSON-based query -format][ref-json-syntax], and the GraphQL API accepts [GraphQL -queries][ref-graphql-syntax]. +* To connect to [Google Sheets][ref-sheets], use [Cube Cloud for Sheets][ref-cube-cloud-for-sheets]. -* For AI use cases, consider using the [AI API][ref-ai-api]. +* For internal or self-serve [business intelligence][cube-issbi], use +[Semantic Layer Sync][ref-sls] in case it supports your BI tools. +Otherwise, connect via the [SQL API][ref-sql-api] directly. + +* For [embedded analytics][cube-ea] and [real-time analytics][cube-rta], use +[REST API][ref-rest-api] or [GraphQL API][ref-graphql-api]. When using the REST API, +the [JavaScript SDK][ref-js-sdk] can simplify integration with your front-end code. + +* For AI use cases, use the [AI API][ref-ai-api]. @@ -53,21 +52,21 @@ for an unofficial, community-maintained [client library for Python](https://gith Support for data modeling features differ across APIs, integrations, and [visualization tools][ref-viz-tools]. Some of the features with partial support are listed below: -| Feature | Supported in | Not supported in | +| Feature | ✅ Supported in | ❌ Not supported in | | --- | --- | --- | -| [Hierarchies][ref-hierarchies] | ✅ [Tableau][ref-tableau] via [Semantic Layer Sync][ref-sls]
✅ Cube Cloud for Excel
✅ [Cube Cloud for Sheets][ref-cube-cloud-for-sheets] | ❌ All other tools | -| [Folders][ref-folders] | ✅ [Tableau][ref-tableau] via [Semantic Layer Sync][ref-sls]
✅ Cube Cloud for Excel
✅ [Cube Cloud for Sheets][ref-cube-cloud-for-sheets] | ❌ All other tools | +| [Hierarchies][ref-hierarchies] | [Microsoft Power BI][ref-powerbi] via the [DAX API][ref-dax-api]
[Cube Cloud for Excel][ref-cube-cloud-for-excel]
[Cube Cloud for Sheets][ref-cube-cloud-for-sheets]
[Tableau][ref-tableau] via [Semantic Layer Sync][ref-sls] | All other tools | +| [Folders][ref-folders] | [Microsoft Power BI][ref-powerbi] via the [DAX API][ref-dax-api]
[Cube Cloud for Excel][ref-cube-cloud-for-excel]
[Cube Cloud for Sheets][ref-cube-cloud-for-sheets]
[Tableau][ref-tableau] via [Semantic Layer Sync][ref-sls] | All other tools | ### Authentication methods Support for authentication methods differ across APIs, integrations, and [visualization tools][ref-viz-tools]: -| Method | Supported in | +| Method | ✅ Supported in | | --- | --- | -| [User name and password][ref-auth-user-pass] | ✅ [SQL API][ref-sql-api] and [Semantic Layer Sync][ref-sls]
✅ [MDX API][ref-mdx-api] | -| [Identity provider][ref-auth-idp] | ✅ Cube Cloud for Excel
✅ [Cube Cloud for Sheets][ref-cube-cloud-for-sheets] | -| [Access token][ref-auth-tokens] | ✅ [REST API][ref-rest-api]
✅ [GraphQL API][ref-graphql-api]
✅ [AI API][ref-ai-api] | +| [User name and password][ref-auth-user-pass] | [DAX API][ref-dax-api]
[MDX API][ref-mdx-api]
[Semantic Layer Sync][ref-sls]
[SQL API][ref-sql-api] | +| [Identity provider][ref-auth-idp] | [Cube Cloud for Excel][ref-cube-cloud-for-excel]
[Cube Cloud for Sheets][ref-cube-cloud-for-sheets] | +| [Access token][ref-auth-tokens] | [REST API][ref-rest-api]
[GraphQL API][ref-graphql-api]
[AI API][ref-ai-api] | ## Management APIs @@ -79,6 +78,7 @@ API][ref-orchestration-api]. [cube-ea]: https://cube.dev/use-cases/embedded-analytics [cube-rta]: https://cube.dev/use-cases/real-time-analytics [ref-queries]: /product/apis-integrations/queries +[ref-dax-api]: /product/apis-integrations/dax-api [ref-sql-api]: /product/apis-integrations/sql-api [ref-rest-api]: /product/apis-integrations/rest-api [ref-graphql-api]: /product/apis-integrations/graphql-api @@ -87,13 +87,14 @@ API][ref-orchestration-api]. [ref-orchestration-api]: /product/apis-integrations/orchestration-api [ref-sls]: /product/apis-integrations/semantic-layer-sync [ref-js-sdk]: /product/apis-integrations/javascript-sdk -[ref-sql-syntax]: /product/apis-integrations/sql-api#querying-fundamentals -[ref-json-syntax]: /product/apis-integrations/rest-api/query-format -[ref-graphql-syntax]: /product/apis-integrations/graphql-api#getting-started +[ref-cube-cloud-for-excel]: /product/apis-integrations/microsoft-excel [ref-cube-cloud-for-sheets]: /product/apis-integrations/google-sheets [ref-viz-tools]: /product/configuration/visualization-tools [ref-hierarchies]: /reference/data-model/hierarchies [ref-folders]: /reference/data-model/view#folders +[ref-powerbi]: /product/configuration/visualization-tools/powerbi +[ref-excel]: /product/configuration/visualization-tools/excel +[ref-sheets]: /product/configuration/visualization-tools/google-sheets [ref-tableau]: /product/configuration/visualization-tools/tableau [ref-auth-user-pass]: /product/auth#user-name-and-password [ref-auth-idp]: /product/auth#identity-provider diff --git a/docs/pages/product/apis-integrations/_meta.js b/docs/pages/product/apis-integrations/_meta.js index 46a8375df03ea..1061f3fdcd807 100644 --- a/docs/pages/product/apis-integrations/_meta.js +++ b/docs/pages/product/apis-integrations/_meta.js @@ -1,11 +1,13 @@ module.exports = { "queries": "Queries", + "dax-api": "DAX API", + "mdx-api": "MDX API", + "microsoft-excel": "Cube Cloud for Excel", + "google-sheets": "Cube Cloud for Sheets", "semantic-layer-sync": "Semantic Layer Sync", "sql-api": "SQL API", "rest-api": "REST API", "graphql-api": "GraphQL API", - "mdx-api": "MDX API", - "google-sheets": "Cube Cloud for Sheets", "ai-api": "AI API", "javascript-sdk": "JavaScript SDK", "orchestration-api": "Orchestration API", diff --git a/docs/pages/product/apis-integrations/dax-api.mdx b/docs/pages/product/apis-integrations/dax-api.mdx new file mode 100644 index 0000000000000..c3178697f769c --- /dev/null +++ b/docs/pages/product/apis-integrations/dax-api.mdx @@ -0,0 +1,87 @@ +# DAX API + +The DAX API enables Cube to connect to [Microsoft Power BI][ref-powerbi]. +It derives its name from [data analysis expressions][link-dax], a query language +for Power BI and SQL Server Analysis Services. + +Unlike the [SQL API][ref-sql-api], it provides a native experience and superior +support for Power BI features. + + + +The DAX API is available in Cube Cloud on [Enterprise and above](https://cube.dev/pricing) product tiers. +It also requires the M [deployment tier](/product/deployment/cloud/pricing#deployment-tiers). + + + + + +The DAX API is currently in preview. Please [contact us](https://cube.dev/contact) to enable it for your account. + + + +Read below about the DAX API [configuration](#configuration), +[authentication](#authentication), and [using it](#using-dax-api-with-power-bi) with Power BI. + +See the [DAX API reference][ref-ref-dax-api] for the list of supported DAX functions. + + + +## Configuration + + + +Please contact the customer success team in Cube Cloud so we can guide you +through the configuration. + + + +To enable or disable the DAX API on a specific deployment, go to Settings +in the Cube Cloud sidebar, then Configuration, and then toggle the +Enable DAX API option. + +## Authentication + +The DAX API supports the user name and password authentication method. + +## Using DAX API with Power BI + + + +The DAX API works only with [views][ref-views], not cubes. + + + +{/* + +TODO + +*/} + +### Connection methods + +Power BI provides [three methods][link-powerbi-connection] to connect to data +sources: _live connection_, _DirectQuery_, and _import mode_. + +| | Live connection | DirectQuery | Import mode | +| --- | --- | --- | --- | +| Data location | 🟢 Data source | 🟢 Data source | 🟡 Power BI | +| Data freshness | 🟢 Real-time data | 🟢 Real-time data | 🟡 Stale copy | +| Semantic model | 🟢 Up-to-date model | 🟡 Stale copy | 🟡 Stale copy | +| Queries | 🟢 Composed in UI
🔴 No custom queries | 🟢 Composed in UI
🟢 Custom queries | 🟢 Composed in UI
🟢 Custom queries | + +__It's recommended to use a _live connection_ whenever possible.__ Use _DirectQuery_ +if you need to write your own DAX queries, but be aware that you must manually +synchronize semantic models as they evolve. _Import mode_ is not recommended, as +it removes the benefits of using a semantic layer. + + +[ref-powerbi]: /product/configuration/visualization-tools/powerbi +[link-dax]: https://learn.microsoft.com/en-us/dax/ +[ref-sql-api]: /product/apis-integrations/sql-api +[ref-ref-dax-api]: /product/apis-integrations/dax-api/reference +[ref-views]: /product/data-modeling/concepts#views +[link-powerbi-connection]: https://learn.microsoft.com/en-us/power-bi/connect-data/service-live-connect-dq-datasets \ No newline at end of file diff --git a/docs/pages/product/apis-integrations/dax-api/_meta.js b/docs/pages/product/apis-integrations/dax-api/_meta.js new file mode 100644 index 0000000000000..84ba2fd7d3fdf --- /dev/null +++ b/docs/pages/product/apis-integrations/dax-api/_meta.js @@ -0,0 +1,3 @@ +module.exports = { + "reference": "Reference" +} \ No newline at end of file diff --git a/docs/pages/product/apis-integrations/dax-api/reference.mdx b/docs/pages/product/apis-integrations/dax-api/reference.mdx new file mode 100644 index 0000000000000..80a7c40b6d6f2 --- /dev/null +++ b/docs/pages/product/apis-integrations/dax-api/reference.mdx @@ -0,0 +1,237 @@ +# DAX API reference + +The [DAX API][ref-dax-api] supports the following [functions](#dax-functions). + + + +If you'd like to propose a function to be supported in the DAX API, +please contact the customer success team in Cube Cloud. + + + +## DAX functions + +The DAX API currently implements a subset of functions [supported by DAX][link-dax-func-reference]. + +### Aggregation functions + + + +Learn more in the +[relevant section](https://learn.microsoft.com/en-us/dax/aggregation-functions-dax) +of the DAX documentation. + + + +| Function | Unsupported features | Caveats | +| --- | --- | --- | +| [`AVERAGE`](https://learn.microsoft.com/en-us/dax/average-function-dax) | — | — | +| [`COUNT`](https://learn.microsoft.com/en-us/dax/count-function-dax) | — | Counts all rows instead of only non-blank ones | +| [`COUNTROWS`](https://learn.microsoft.com/en-us/dax/countrows-function-dax) | — | Disregards input table expression and always returns 1 | +| [`MAX`](https://learn.microsoft.com/en-us/dax/max-function-dax) | 2 arguments | Blanks are disregarded instead of being treated as 0 | +| [`MIN`](https://learn.microsoft.com/en-us/dax/min-function-dax) | 2 arguments | Blanks are disregarded instead of being treated as 0 | +| [`SUM`](https://learn.microsoft.com/en-us/dax/sum-function-dax) | — | — | + +### Date and time functions + + + +Learn more in the +[relevant section](https://learn.microsoft.com/en-us/dax/date-and-time-functions-dax) +of the DAX documentation. + + + +| Function | Unsupported features | Caveats | +| --- | --- | --- | +| [`DATE`](https://learn.microsoft.com/en-us/dax/date-function-dax) | Non-literal date parts | — | +| [`TIME`](https://learn.microsoft.com/en-us/dax/time-function-dax) | Non-literal date parts. Execution outside `DATE(...) + TIME(...)` expression | — | + +### Filter functions + + + +Learn more in the +[relevant section](https://learn.microsoft.com/en-us/dax/filter-functions-dax) +of the DAX documentation. + + + +| Function | Unsupported features | Caveats | +| --- | --- | --- | +| [`ALL`](https://learn.microsoft.com/en-us/dax/all-function-dax) | 2+ arguments | Doesn't clear filter context, silently ignored | +| [`CALCULATE`](https://learn.microsoft.com/en-us/dax/calculate-function-dax) | Creating a nested filter context | — | +| [`CALCULATETABLE`](https://learn.microsoft.com/en-us/dax/calculatetable-function-dax) | Creating a nested filter context | — | +| [`FILTER`](https://learn.microsoft.com/en-us/dax/filter-function-dax) | — | — | +| [`KEEPFILTERS`](https://learn.microsoft.com/en-us/dax/keepfilters-function-dax) | — | Doesn't modify behavior, silently ignored | + +### Financial functions + + + +Learn more in the +[relevant section](https://learn.microsoft.com/en-us/dax/financial-functions-dax) +of the DAX documentation. + + + +No financial functions currently supported. + +### Information functions + + + +Learn more in the +[relevant section](https://learn.microsoft.com/en-us/dax/information-functions-dax) +of the DAX documentation. + + + +| Function | Unsupported features | Caveats | +| --- | --- | --- | +| [`ISBLANK`](https://learn.microsoft.com/en-us/dax/isblank-function-dax) | — | Blanks are treated as equivalent to nulls and vice versa | +| [`ISONORAFTER`](https://learn.microsoft.com/en-us/dax/isonorafter-function-dax) | — | — | + +### Logical functions + + + +Learn more in the +[relevant section](https://learn.microsoft.com/en-us/dax/logical-functions-dax) +of the DAX documentation. + + + +| Function | Unsupported features | Caveats | +| --- | --- | --- | +| [`AND`](https://learn.microsoft.com/en-us/dax/and-function-dax) | — | — | +| [`NOT`](https://learn.microsoft.com/en-us/dax/not-function-dax) | — | — | +| [`OR`](https://learn.microsoft.com/en-us/dax/or-function-dax) | — | — | + +### Math and trig functions + + + +Learn more in the +[relevant section](https://learn.microsoft.com/en-us/dax/math-and-trig-functions-dax) +of the DAX documentation. + + + +No math and trig functions currently supported. + +### Other functions + + + +Learn more in the +[relevant section](https://learn.microsoft.com/en-us/dax/other-functions-dax) +of the DAX documentation. + + + +| Function | Unsupported features | Caveats | +| --- | --- | --- | +| [`BLANK`](https://learn.microsoft.com/en-us/dax/blank-function-dax) | — | Blanks are treated as equivalent to nulls and vice versa | + +### Parent and child functions + + + +Learn more in the +[relevant section](https://learn.microsoft.com/en-us/dax/parent-and-child-functions-dax) +of the DAX documentation. + + + +No parent and child functions currently supported. + +### Relationship functions + + + +Learn more in the +[relevant section](https://learn.microsoft.com/en-us/dax/relationship-functions-dax) +of the DAX documentation. + + + +No relationship functions currently supported. + +### Statistical functions + + + +Learn more in the +[relevant section](https://learn.microsoft.com/en-us/dax/statistical-functions-dax) +of the DAX documentation. + + + +| Function | Unsupported features | Caveats | +| --- | --- | --- | +| [`SAMPLE`](https://learn.microsoft.com/en-us/dax/sample-function-dax) | — | Doesn't evenly distribute rows, taking top N instead (acts as the `TOPN` function) | + +### Table manipulation functions + + + +Learn more in the +[relevant section](https://learn.microsoft.com/en-us/dax/table-manipulation-functions-dax) +of the DAX documentation. + + + +| Function | Unsupported features | Caveats | +| --- | --- | --- | +| [`ADDCOLUMNS`](https://learn.microsoft.com/en-us/dax/addcolumns-function-dax) | Aggregate expressions | — | +| [`DISTINCT`](https://learn.microsoft.com/en-us/dax/distinct-function-dax) (column) | — | — | +| [`DISTINCT`](https://learn.microsoft.com/en-us/dax/distinct-table-function-dax) (table) | Filter context for table expressions | — | +| [`IGNORE`](https://learn.microsoft.com/en-us/dax/ignore-function-dax) | 2+ arguments | Doesn't modify filter context, silently ignored | +| [`NATURALLEFTOUTERJOIN`](https://learn.microsoft.com/en-us/dax/naturalleftouterjoin-function-dax) | — | — | +| [`ROLLUPADDISSUBTOTAL`](https://learn.microsoft.com/en-us/dax/rollupaddissubtotal-function-dax) | `grandtotalFilter`, `groupLevelFilter` | — | +| [`ROLLUPGROUP`](https://learn.microsoft.com/en-us/dax/rollupgroup-function-dax) | — | — | +| [`ROW`](https://learn.microsoft.com/en-us/dax/row-function-dax) | — | — | +| [`SELECTCOLUMNS`](https://learn.microsoft.com/en-us/dax/selectcolumns-function-dax) | Optional aliases (currently mandatory) | — | +| [`SUBSTITUTEWITHINDEX`](https://learn.microsoft.com/en-us/dax/substitutewithindex-function-dax) | — | — | +| [`SUMMARIZE`](https://learn.microsoft.com/en-us/dax/summarize-function-dax) | Named expressions (aggregations/measures) | — | +| [`SUMMARIZECOLUMNS`](https://learn.microsoft.com/en-us/dax/summarizecolumns-function-dax) | — | — | +| [`TOPN`](https://learn.microsoft.com/en-us/dax/topn-function-dax) | Non-literal N value | — | +| [`TREATAS`](https://learn.microsoft.com/en-us/dax/treatas-function-dax) | Non-literal tables as table expressions | — | +| [`VALUES`](https://learn.microsoft.com/en-us/dax/values-function-dax) | — | — | + +### Text functions + + + +Learn more in the +[relevant section](https://learn.microsoft.com/en-us/dax/text-functions-dax) +of the DAX documentation. + + + +| Function | Unsupported features | Caveats | +| --- | --- | --- | +| [`SEARCH`](https://learn.microsoft.com/en-us/dax/search-function-dax) | Execution outside filter context functions | — | + +### Time intelligence functions + + + +Learn more in the +[relevant section](https://learn.microsoft.com/en-us/dax/time-intelligence-functions-dax) +of the DAX documentation. + + + +No time intelligence functions currently supported. + +### Miscellaneous functions + +| Function | Unsupported features | Caveats | +| --- | --- | --- | +| [`SAMPLEAXISWITHLOCALMINMAX`](https://learn.microsoft.com/en-us/dax/sampleaxiswithlocalminmax-function-dax) | — | Silently ignored, returning input table expression | + + +[ref-dax-api]: /product/apis-integrations/dax-api +[link-dax-func-reference]: https://learn.microsoft.com/en-us/dax/dax-function-reference \ No newline at end of file diff --git a/docs/pages/product/apis-integrations/google-sheets.mdx b/docs/pages/product/apis-integrations/google-sheets.mdx index f4ff6fa3a61cb..eef870c49e63a 100644 --- a/docs/pages/product/apis-integrations/google-sheets.mdx +++ b/docs/pages/product/apis-integrations/google-sheets.mdx @@ -1,6 +1,6 @@ # Cube Cloud for Sheets -Cube Cloud for Sheets is a native [Google Sheets][link-google-sheets] add-on for +Cube Cloud for Sheets is the native [Google Sheets][link-google-sheets] add-on for Cube Cloud. @@ -42,20 +42,18 @@ document, open the Extensions menu, and check that there is the ## Authentication -You need to authenticate Cube Cloud for Sheets to retrieve data from your -semantic layer in Cube Cloud. To do so, open the sidebar by going to the -Extensions menu and choosing Cube Cloud for Sheets → Open -Sidebar. Then, click Sign in. +You need to authenticate Cube Cloud for Sheets to retrieve data from Cube Cloud. +To do so, open the sidebar by going to the Extensions menu and choosing +Cube Cloud for Sheets → Open Sidebar. Then, click Sign in. -A modal window with an authentication prompt will open. Choose the deployments -within your Cube Cloud account that you want to work with in Google Sheets and -click Authorize. Once you see the `Access Granted` message, close the -modal window. +A modal window with an authentication prompt will appear. Choose the deployments +that you want to work with in Google Sheets and click Authorize. +Once you see the `Access Granted` message, close the modal window. If you want to revoke the authentication, open the add-on menu and click Sign out. @@ -65,11 +63,11 @@ If you want to revoke the authentication, open the add-on menu and click To create a report, go to the add-on menu and click Create report via pivot table. Then, select a Cube Cloud deployment from the drop-down. Finally, you can start building a query by selecting a view and its members in the UI that -looks and feel exactly like [Playground][ref-playground]. +looks and feels like [Playground][ref-playground]. -Google Cloud for Sheets only works with [views][ref-views] rather than cubes. +Google Cloud for Sheets works only with [views][ref-views], not cubes. diff --git a/docs/pages/product/apis-integrations/mdx-api.mdx b/docs/pages/product/apis-integrations/mdx-api.mdx index 2e5de9ffd9c9b..3bae888137c19 100644 --- a/docs/pages/product/apis-integrations/mdx-api.mdx +++ b/docs/pages/product/apis-integrations/mdx-api.mdx @@ -1,12 +1,12 @@ # MDX API -The MDX (MultiDimensional eXpressions) API provides a robust interface for connecting applications, such as Microsoft Excel, to the Cube Cloud semantic layer via the XMLA (XML for Analysis) standard. +The MDX API enables Cube to connect to [Microsoft Excel][ref-excel]. It derives +its name from [multidimensional data expressions][link-mdx], a query language +for OLAP in the Microsoft ecosystem. -Key Features: - -- Direct connectivity: Connect Excel directly to Cube Cloud using standard XMLA protocols. -- Advanced analytical functions: Utilize the power of MDX to execute sophisticated queries that include slicing, dicing, drilling down, and rolling up of data. -- Real-time access: Fetch live data from Cube Cloud, ensuring that your analyses and reports always reflect the most current information. +Unlike [Cube Cloud for Excel][ref-cube-cloud-for-excel], it only works with Excel +on Microsoft Windows. However, it allows using the data from the MDX API with the +native [PivotTable][link-pivottable] in Excel. @@ -22,22 +22,30 @@ The MDX API is currently in preview. +Key features: + +- Direct connectivity: Connect Excel directly to Cube Cloud using standard XMLA protocols. +- Advanced analytical functions: Utilize the power of MDX to execute sophisticated queries that include slicing, dicing, drilling down, and rolling up of data. +- Real-time access: Fetch live data from Cube Cloud, ensuring that your analyses and reports always reflect the most current information. + ## Configuration + + While the MDX API is in preview, your Cube account team will enable and configure it for you. -If you wish to enable or disable the MDX API on a specific Cube deployment, you may do so by going to "Settings" in the Cube Cloud sidebar, then "Configuration", and then toggling the "Enable MDX API" configuration flag switch. + -To find your MDX API credentials and the XMLA endpoint in Cube Cloud, go to the -Overview page, click API credentials, and choose the -AI API tab. +To enable or disable the MDX API on a specific deployment, go to Settings +in the Cube Cloud sidebar, then Configuration, and then toggle the +Enable MDX API option. ## Using MDX API with Excel -Once MDX API is enabled, you can use it to connect to Cube from Excel. In Excel, go to the **Data tab**, click **Get Data** and select **From Database -> From Analysis Services**. - - Please note that only the [views](/reference/data-model/view) will be available for connections via MDX API + +The MDX API works only with [views][ref-views], not cubes. + The following section describes Excel-specific configuration options. @@ -164,5 +172,10 @@ This is going to be harmonized in the future. Authentication and authorization work the same as for the [SQL API](/product/apis-integrations/sql-api/security). +[ref-excel]: /product/configuration/visualization-tools/excel +[link-mdx]: https://learn.microsoft.com/en-us/analysis-services/multidimensional-models/mdx/multidimensional-model-data-access-analysis-services-multidimensional-data?view=asallproducts-allversions#bkmk_querylang +[link-pivottable]: https://support.microsoft.com/en-us/office/create-a-pivottable-to-analyze-worksheet-data-a9a84538-bfe9-40a9-a8e9-f99134456576 +[ref-cube-cloud-for-excel]: /product/apis-integrations/microsoft-excel [ref-hierarchies]: /reference/data-model/hierarchies -[ref-folders]: /reference/data-model/view#folders \ No newline at end of file +[ref-folders]: /reference/data-model/view#folders +[ref-views]: /product/data-modeling/concepts#views \ No newline at end of file diff --git a/docs/pages/product/apis-integrations/microsoft-excel.mdx b/docs/pages/product/apis-integrations/microsoft-excel.mdx new file mode 100644 index 0000000000000..94d413df8876e --- /dev/null +++ b/docs/pages/product/apis-integrations/microsoft-excel.mdx @@ -0,0 +1,119 @@ +# Cube Cloud for Excel + +Cube Cloud for Excel is the native [Microsoft Excel][ref-excel] add-in for Cube Cloud. + +Unlike the [MDX API][ref-mdx-api], it works with Excel on all operating systems, +including macOS, and all platforms, including web and mobile. It doesn't integrate +with the native [PivotTable][link-pivottable] in Excel but provides a custom +[pivot table](#create-reports-via-pivot-table) UI. + + + +Cube Cloud for Excel is available in Cube Cloud on [Enterprise and above](https://cube.dev/pricing) product tiers. + + + + + +Cube Cloud for Excel is currently in preview. Please [contact us](https://cube.dev/contact) to get access to it. + + + +After [installing](#installation) and [authenticating](#authentication) this +add-in, you will be able to [create reports via pivot +table](#create-reports-via-pivot-table) and work with [saved +reports](#work-with-saved-reports). + + + +## Installation + + + +Please contact the customer success team in Cube Cloud so we can guide you +through the installation. + + + +## Authentication + +You need to authenticate Cube Cloud for Excel to retrieve data from Cube Cloud. +To do so, open the sidebar by clicking on the Cube Cloud button in +the Home ribbon. Then, click Sign in. + + + +A modal window with an authentication prompt will appear. Choose the deployments +that you want to work with in Microsoft Excel and click Authorize. +Once you see the `Access Granted` message, close the modal window. + +If you want to revoke the authentication, open the add-in menu and click +Sign out. + +## Create reports via pivot table + +To create a report, go to the add-in menu and click Create report via pivot +table. Then, select a Cube Cloud deployment from the drop-down. Finally, +you can start building a query by selecting a view and its members in the UI that +looks and feels like [Playground][ref-playground]. + + + +Google Cloud for Excel works only with [views][ref-views], not cubes. + + + +Click on members to add them to Rows and Measures. +If needed, drag dimensions from Rows to Columns. Click on +the funnel buttons to add members to Filters. Click on × to +remove members from a query. + + + +Use Order and Filters panes below to sort and filter the +data in the report. + +If you'd like to move the report to a new location, click on the desired top-left +cell and then confirm with the target button under Result location. + + + +With every change to your query, Cube Cloud for Excel will update the report on +the sheet after a slight delay. If you'd like to minimize it, consider +implementing [pre-aggregations][ref-pre-aggs]. + +When your report is ready, you can optionally move it to [saved reports](#work-with-saved-reports) +by clicking Save. + +## Work with saved reports + +Go to the add-in menu and click View saved reports to see a list of +reports. + +Click Refresh to manually refresh the data in the report's location. +Click Edit to chnage the query or the location. + + + + +[ref-excel]: /product/configuration/visualization-tools/excel +[ref-mdx-api]: /product/apis-integrations/mdx-api +[link-pivottable]: https://support.microsoft.com/en-us/office/create-a-pivottable-to-analyze-worksheet-data-a9a84538-bfe9-40a9-a8e9-f99134456576 +[ref-playground]: /product/workspace/playground +[ref-views]: /product/data-modeling/concepts#views +[ref-pre-aggs]: /product/caching/using-pre-aggregations \ No newline at end of file diff --git a/docs/pages/product/apis-integrations/semantic-layer-sync.mdx b/docs/pages/product/apis-integrations/semantic-layer-sync.mdx index a25622423dc96..a28479fe9a357 100644 --- a/docs/pages/product/apis-integrations/semantic-layer-sync.mdx +++ b/docs/pages/product/apis-integrations/semantic-layer-sync.mdx @@ -7,7 +7,7 @@ layer from Cube to BI tools. It's the easiest way to connect a BI tool to Cube. Semantic Layer Sync is available in Cube Cloud.
Support for Metabase, Preset, and Superset is available on [all product tiers](https://cube.dev/pricing). -
Support for Tableau and Power BI is available on [Enterprise and above](https://cube.dev/pricing) product tiers. +
Support for Tableau is available on [Enterprise and above](https://cube.dev/pricing) product tiers.
@@ -42,11 +42,6 @@ on configuration and features for specific tools: imageUrl="https://static.cube.dev/icons/metabase.svg" title="Metabase" /> - + + +Previously, Semantic Layer Sync provided a way to connect to [Microsoft Power BI][ref-powerbi]. +Currently, it's recommended to use the [DAX API][ref-dax-api]. + + + ## Creating syncs You can create a new sync by navigating to the Semantic Layer Sync @@ -396,10 +398,12 @@ on, i.e., your development mode branch, shared branch, or main branch. [ref-data-model]: /product/data-modeling/overview [ref-sql-api]: /product/apis-integrations/sql-api +[ref-powerbi]: /product/configuration/visualization-tools/powerbi +[ref-dax-api]: /product/apis-integrations/dax-api [ref-config-file]: /product/configuration#configuration-options [ref-config-sls]: /reference/configuration/config#semanticlayersync [ref-config-contexts]: /reference/configuration/config#scheduledrefreshcontexts [ref-config-schemaversion]: /reference/configuration/config#schema_version [ref-workspace-sls]: /workspace/bi-integrations [ref-dev-mode]: /product/workspace/dev-mode -[ref-auto-sus]: /product/deployment/cloud/auto-suspension +[ref-auto-sus]: /product/deployment/cloud/auto-suspension \ No newline at end of file diff --git a/docs/pages/product/apis-integrations/semantic-layer-sync/_meta.js b/docs/pages/product/apis-integrations/semantic-layer-sync/_meta.js index 09dd4df00bf65..2440b6e1d23c9 100644 --- a/docs/pages/product/apis-integrations/semantic-layer-sync/_meta.js +++ b/docs/pages/product/apis-integrations/semantic-layer-sync/_meta.js @@ -1,7 +1,6 @@ module.exports = { "superset": "Apache Superset", - "metabase": "Metabase", - "power-bi": "Power BI", + "metabase": "Metabase",≈ "preset": "Preset", "tableau": "Tableau" } \ No newline at end of file diff --git a/docs/pages/product/apis-integrations/semantic-layer-sync/power-bi.mdx b/docs/pages/product/apis-integrations/semantic-layer-sync/power-bi.mdx deleted file mode 100644 index ddb159e63b06a..0000000000000 --- a/docs/pages/product/apis-integrations/semantic-layer-sync/power-bi.mdx +++ /dev/null @@ -1,34 +0,0 @@ -# Semantic Layer Sync with Power BI - -This page details the support for [Power BI](https://www.microsoft.com/en-gb/power-platform/products/power-bi/) -in [Semantic Layer Sync](/product/apis-integrations/semantic-layer-sync). - - - -Semantic Layer Sync with Power BI is available on -[Enterprise and above](https://cube.dev/pricing) product tiers. - - - -## Configuration - -To create a new sync, choose Power BI: - - - -## Features - -Click Download .pbit to download a Power BI [report template][pbit]: - - - -Use can open it in Power BI to create a report. - - -[pbit]: https://learn.microsoft.com/en-us/power-bi/create-reports/desktop-templates \ No newline at end of file diff --git a/docs/pages/product/apis-integrations/sql-api.mdx b/docs/pages/product/apis-integrations/sql-api.mdx index b0461168f9ccb..bc8c57355d28e 100644 --- a/docs/pages/product/apis-integrations/sql-api.mdx +++ b/docs/pages/product/apis-integrations/sql-api.mdx @@ -1,35 +1,33 @@ ---- -redirect_from: - - /backend/sql ---- - # SQL API -SQL API enables Cube to deliver data over the [Postgres-compatible -protocol][postgres-protocol] to certain kinds of data applications, including -but not limited to the following ones: +The SQL API enables Cube to deliver data over the [Postgres-compatible +protocol][postgres-protocol] to a wide range of [data visualization tools][ref-dataviz-tools]. +In general, if an application connects to [PostgreSQL][link-postgres] database, +it can connect to Cube as well. -- Most commonly, [business intelligence tools][ref-bi], e.g., - [Tableau][ref-tableau], [Power BI][ref-powerbi], [ThoughtSpot][ref-thoughtspot], - [Sigma][ref-sigma], [Looker Studio][ref-looker-studio], [Superset / - Preset][ref-superset], and [Metabase][ref-metabase] -- [Data notebooks][ref-notebooks], e.g., [Jupyter][ref-jupyter], [Hex][ref-hex], - or [Deepnote][ref-deepnote] -- Reverse ETL tools, e.g., Census or Hightouch +Unlike [DAX API][ref-dax-api], [MDX API][ref-mdx-api], and [Semantic Layer Sync][ref-sls], +the SQL API provides a more generic way to work with Cube, limited to what the SQL +query language and the Postgres protocol can offer. -Often, the SQL API is used to enable internal or self-serve [business -intelligence][cube-bi-use-case] use cases. +The SQL API works with the following kinds of data applications: -See [SQL API reference][ref-ref-sql-api] for the list of supported SQL commands, -functions, and operators. Also, check [query format][ref-sql-query-format] for -details about supported queries. +- [BI tools][ref-bi], e.g., [ThoughtSpot][ref-thoughtspot], [Sigma][ref-sigma], +[Looker Studio][ref-looker-studio], enabling internal or self-serve [business +intelligence][cube-bi-use-case] use cases. +- [Data notebooks][ref-notebooks], e.g., [Jupyter][ref-jupyter], [Hex][ref-hex], +or [Deepnote][ref-deepnote]. +- Reverse ETL tools, e.g., Census or Hightouch. - + Please use [this GitHub issue](https://github.com/cube-js/cube/issues/3906) to suggest tools of your interest and vote for already proposed ones. - +
+ +See [SQL API reference][ref-ref-sql-api] for the list of supported SQL commands, +functions, and operators. Also, check [query format][ref-sql-query-format] for +details about supported queries. ## Example request @@ -233,17 +231,18 @@ You can use the `CUBEJS_MAX_SESSIONS` environment variable to adjust the session limit. +[link-postgres]: https://www.postgresql.org +[ref-dax-api]: /product/apis-integrations/dax-api +[ref-mdx-api]: /product/apis-integrations/mdx-api +[ref-sls]: /product/apis-integrations/semantic-layer-sync [ref-sql-api-auth]: /product/apis-integrations/sql-api/security [ref-config-checksqlauth]: /reference/configuration/config#checksqlauth [ref-config-canswitchsqluser]: /reference/configuration/config#canswitchsqluser +[ref-dataviz-tools]: /product/configuration/visualization-tools [ref-bi]: /product/configuration/visualization-tools#bi-data-exploration-tools -[ref-superset]: /product/configuration/visualization-tools/superset -[ref-tableau]: /product/configuration/visualization-tools/tableau -[ref-powerbi]: /product/configuration/visualization-tools/powerbi [ref-thoughtspot]: /product/configuration/visualization-tools/thoughtspot [ref-sigma]: /product/configuration/visualization-tools/sigma [ref-looker-studio]: /product/configuration/visualization-tools/looker-studio -[ref-metabase]: /product/configuration/visualization-tools/metabase [ref-notebooks]: /product/configuration/visualization-tools#notebooks [ref-jupyter]: /product/configuration/visualization-tools/jupyter [ref-hex]: /product/configuration/visualization-tools/hex diff --git a/docs/pages/product/apis-integrations/sql-api/reference.mdx b/docs/pages/product/apis-integrations/sql-api/reference.mdx index 7f6c0a1f9f6fb..28f7d57747749 100644 --- a/docs/pages/product/apis-integrations/sql-api/reference.mdx +++ b/docs/pages/product/apis-integrations/sql-api/reference.mdx @@ -5,7 +5,7 @@ as [functions and operators](#sql-functions-and-operators). -If you'd like to propose a function or an operator to be implemented by the SQL API, +If you'd like to propose a function or an operator to be supported in the SQL API, check the existing [issues on GitHub][link-github-sql-api]. If there are no relevant issues, please [file a new one][link-github-new-sql-api-issue]. @@ -156,7 +156,7 @@ PostgreSQL][link-postgres-funcs]. -You can learn more in the +Learn more in the [relevant section](https://www.postgresql.org/docs/current/functions-comparison.html#FUNCTIONS-COMPARISON-OP-TABLE) of the PostgreSQL documentation. @@ -175,7 +175,7 @@ of the PostgreSQL documentation. -You can learn more in the +Learn more in the [relevant section](https://www.postgresql.org/docs/current/functions-comparison.html#FUNCTIONS-COMPARISON-PRED-TABLE) of the PostgreSQL documentation. @@ -191,7 +191,7 @@ of the PostgreSQL documentation. -You can learn more in the +Learn more in the [relevant section](https://www.postgresql.org/docs/current/functions-math.html#FUNCTIONS-MATH-FUNC-TABLE) of the PostgreSQL documentation. @@ -219,7 +219,7 @@ of the PostgreSQL documentation. -You can learn more in the +Learn more in the [relevant section](https://www.postgresql.org/docs/current/functions-math.html#FUNCTIONS-MATH-TRIG-TABLE) of the PostgreSQL documentation. @@ -240,7 +240,7 @@ of the PostgreSQL documentation. -You can learn more in the +Learn more in the [relevant section](https://www.postgresql.org/docs/current/functions-string.html#FUNCTIONS-STRING-SQL) of the PostgreSQL documentation. @@ -265,7 +265,7 @@ of the PostgreSQL documentation. -You can learn more in the +Learn more in the [relevant section](https://www.postgresql.org/docs/current/functions-string.html#FUNCTIONS-STRING-OTHER) of the PostgreSQL documentation. @@ -285,7 +285,7 @@ of the PostgreSQL documentation. -You can learn more in the +Learn more in the [relevant section](https://www.postgresql.org/docs/current/functions-matching.html) of the PostgreSQL documentation. @@ -300,7 +300,7 @@ of the PostgreSQL documentation. -You can learn more in the +Learn more in the [relevant section](https://www.postgresql.org/docs/current/functions-formatting.html) of the PostgreSQL documentation. @@ -314,7 +314,7 @@ of the PostgreSQL documentation. -You can learn more in the +Learn more in the [relevant section](https://www.postgresql.org/docs/current/functions-datetime.html#FUNCTIONS-DATETIME-TABLE) of the PostgreSQL documentation. @@ -333,7 +333,7 @@ of the PostgreSQL documentation. -You can learn more in the +Learn more in the [relevant section](https://www.postgresql.org/docs/current/functions-conditional.html) of the PostgreSQL documentation. @@ -351,7 +351,7 @@ of the PostgreSQL documentation. -You can learn more in the +Learn more in the [relevant section](https://www.postgresql.org/docs/current/functions-aggregate.html#FUNCTIONS-AGGREGATE-TABLE) of the PostgreSQL documentation. @@ -377,7 +377,7 @@ of the relevant cube. -You can learn more in the +Learn more in the [relevant section](https://www.postgresql.org/docs/current/functions-aggregate.html#FUNCTIONS-AGGREGATE-STATISTICS-TABLE) of the PostgreSQL documentation. @@ -396,7 +396,7 @@ of the PostgreSQL documentation. -You can learn more in the +Learn more in the [relevant section](https://www.postgresql.org/docs/current/functions-comparisons.html) of the PostgreSQL documentation. diff --git a/docs/pages/product/configuration/data-sources/ksqldb.mdx b/docs/pages/product/configuration/data-sources/ksqldb.mdx index e5d003f512d3e..6aa71bbac319e 100644 --- a/docs/pages/product/configuration/data-sources/ksqldb.mdx +++ b/docs/pages/product/configuration/data-sources/ksqldb.mdx @@ -1,8 +1,3 @@ ---- -redirect_from: - - /config/databases/ksqldb ---- - # ksqlDB [ksqlDB](https://ksqldb.io) is a purpose-built database for stream processing @@ -23,6 +18,13 @@ See how you can use ksqlDB and Cube Cloud to power real-time analytics in Power aspectRatio={4/3.55} /> + + +In this video, the SQL API is used to connect to [Power BI][ref-powerbi]. +Currently, it's recommended to use the [DAX API][ref-dax-api]. + + + ## Prerequisites - Hostname for the ksqlDB server @@ -72,3 +74,6 @@ CUBEJS_DB_PASS=password ksqlDB supports only [streaming pre-aggregations](/product/caching/using-pre-aggregations#streaming-pre-aggregations). + +[ref-powerbi]: /product/configuration/visualization-tools/powerbi +[ref-dax-api]: /product/apis-integrations/dax-api \ No newline at end of file diff --git a/docs/pages/product/configuration/visualization-tools.mdx b/docs/pages/product/configuration/visualization-tools.mdx index 959d0475e9e1c..59d24d6c855bf 100644 --- a/docs/pages/product/configuration/visualization-tools.mdx +++ b/docs/pages/product/configuration/visualization-tools.mdx @@ -54,7 +54,7 @@ page, please [file an issue](https://github.com/cube-js/cube/issues) on GitHub. /> + ## Connect from Cube Cloud -Cube Cloud provides the [MDX API][ref-mdx-api] for Excel. +Cube Cloud provides two options to connect to Excel: + +| | [MDX API][ref-mdx-api] | [Cube Cloud for Excel][ref-cube-cloud-for-excel] | +| --- | --- | --- | +| Operating systems | Microsoft Windows | All, including macOS | +| Platforms | Desktop | All, including web and mobile | +| Pivot table support | Native [PivotTable][link-pivottable] | Custom [pivot table][ref-cube-cloud-for-excel-pivot] UI | -Navigate to the [Integrations](/product/workspace/integrations#connect-specific-tools) -page, click Connect to Cube, and choose Microsoft Excel to get -detailed instructions. +Navigate to the [Integrations][ref-integrations-tools] page, click Connect to Cube, +and choose Microsoft Excel to get detailed instructions. - +## Connect from Cube Core There's no way to connect a Cube Core deployment to Excel. - +## Connect from Microsoft Excel -## Connecting from Excel +### MDX API In Excel, go to the Data tab of the ribbon, click Get Data -and select From Database → From Analysis Services. +and select From Database → From Analysis Services. Then, enter the +credentials. + +To find your MDX API credentials, go to the [Integrations][ref-integrations-apis] page, click +API credentials, and choose the MDX API tab. -For additional configuration options, check the [MDX API][ref-mdx-api] page. +### Cube Cloud for Excel +Follow the instructions to [install][ref-cube-cloud-for-excel-installation] the +add-in and [authenticate][ref-cube-cloud-for-excel-authentication] to Cube Cloud. [ref-mdx-api]: /product/apis-integrations/mdx-api +[link-pivottable]: https://support.microsoft.com/en-us/office/create-a-pivottable-to-analyze-worksheet-data-a9a84538-bfe9-40a9-a8e9-f99134456576 +[ref-cube-cloud-for-excel]: /product/apis-integrations/microsoft-excel +[ref-cube-cloud-for-excel-pivot]: /product/apis-integrations/microsoft-excel#create-reports-via-pivot-table +[ref-cube-cloud-for-excel-installation]: /product/apis-integrations/microsoft-excel#installation +[ref-cube-cloud-for-excel-authentication]: /product/apis-integrations/microsoft-excel#authentication +[ref-integrations-tools]: /product/workspace/integrations#connect-specific-tools +[ref-integrations-apis]: /product/workspace/integrations#view-api-credentials \ No newline at end of file diff --git a/docs/pages/product/configuration/visualization-tools/powerbi.mdx b/docs/pages/product/configuration/visualization-tools/powerbi.mdx index 939af2f8b9eb6..d368f06440ac0 100644 --- a/docs/pages/product/configuration/visualization-tools/powerbi.mdx +++ b/docs/pages/product/configuration/visualization-tools/powerbi.mdx @@ -1,33 +1,34 @@ ---- -redirect_from: - - /config/downstream/powerbi ---- +# Microsoft Power BI -# Power BI +[Microsoft Power BI][link-powerbi] is a popular business intelligence tool. -[Power BI](https://www.microsoft.com/en-gb/power-platform/products/power-bi/) -is an interactive data visualization software product developed by Microsoft. - -See how you can use [ksqlDB][ref-ksqldb] and Cube Cloud to power real-time -analytics in Power BI: +Cube Cloud works with both [Power BI Desktop and Power BI service][link-powerbi-desktop-vs-service]. +If you're using Power BI service, you need to set up an [on-premises data gateway][link-powerbi-gateway]. ## Connect from Cube Cloud -It is recommended to use [Semantic Layer Sync][ref-sls] to connect Cube Cloud to -Power BI. It automatically synchronizes the [data model][ref-data-model] with Power BI. +Cube Cloud provides the [DAX API][ref-dax-api] to connect to Power BI. + +Navigate to the [Integrations][ref-integrations-tools] page, click Connect to Cube, +and choose Microsoft Power BI to get detailed instructions. + + + +Previously, [Semantic Layer Sync][ref-sls] was recommended as a way to connect +to Power BI. Since the introduction of the DAX API, it's now the preferred +method. -Navigate to the [Integrations](/product/workspace/integrations#connect-specific-tools) -page, click Connect to Cube, and choose Microsoft Power BI to get -detailed instructions. + ## Connect from Cube Core You can connect a Cube deployment to Power BI using the [SQL API][ref-sql-api]. +However, it would not provide the same experience as the DAX API. In Cube Core, the SQL API is disabled by default. Enable it and [configure the credentials](/product/apis-integrations/sql-api#configuration) to @@ -35,26 +36,32 @@ connect to Power BI. ## Connecting from Power BI -Power BI connects to Cube as to a Postgres database using the -[DirectQuery][link-powerbi-directquery] connectivity mode. +### DAX API + +In Power BI, choose the SQL Server Analysis Services database option +when connecting to a data source. The, enter the credentials. + +To find your DAX API credentials, go to the [Integrations][ref-integrations-apis] page, +click API credentials, and choose the DAX API tab. -It's not recommended to import data into Power BI when connecting to Cube; -choose DirectQuery instead. Use [pre-aggregations][ref-pre-aggs] to improve -querying performance and manage data freshness upstream of Power BI. +It's not recommended to import data into Power BI when connecting to Cube. +Use a _live connection_ whenever possible. [Read more][ref-powerbi-connection-methods] +about pros and cons of these methods. -### Querying data +### SQL API -Your cubes will be exposed as tables, where both your measures and dimensions -are columns. +Power BI can also connect to Cube as to a Postgres database. -[ref-getting-started]: /product/getting-started/cloud +[link-powerbi]: https://www.microsoft.com/en-gb/power-platform/products/power-bi/ +[link-powerbi-desktop-vs-service]: https://learn.microsoft.com/en-us/power-bi/fundamentals/service-service-vs-desktop +[link-powerbi-gateway]: https://learn.microsoft.com/en-us/power-bi/connect-data/service-gateway-onprem +[ref-dax-api]: /product/apis-integrations/dax-api +[ref-integrations-tools]: /product/workspace/integrations#connect-specific-tools +[ref-integrations-apis]: /product/workspace/integrations#view-api-credentials [ref-sql-api]: /product/apis-integrations/sql-api -[ref-pre-aggs]: /product/caching/using-pre-aggregations [ref-sls]: /product/apis-integrations/semantic-layer-sync -[ref-data-model]: /product/data-modeling/overview -[ref-ksqldb]: /product/configuration/data-sources/ksqldb -[link-powerbi-directquery]: https://learn.microsoft.com/en-us/power-bi/connect-data/desktop-use-directquery \ No newline at end of file +[ref-powerbi-connection-methods]: /product/apis-integrations/dax-api#connection-methods \ No newline at end of file diff --git a/docs/pages/product/configuration/visualization-tools/superset.mdx b/docs/pages/product/configuration/visualization-tools/superset.mdx index 5d61ef36cfa62..8815134c3e9e7 100644 --- a/docs/pages/product/configuration/visualization-tools/superset.mdx +++ b/docs/pages/product/configuration/visualization-tools/superset.mdx @@ -1,10 +1,4 @@ ---- -redirect_from: - - /recipes/using-apache-superset-with-cube-sql - - /config/downstream/superset ---- - -# Superset / Preset +# Apache Superset / Preset [Apache Superset][superset] is a popular open-source data exploration and visualization platform. [Preset][preset] is a fully-managed service for diff --git a/docs/pages/product/deployment/cloud/pricing.mdx b/docs/pages/product/deployment/cloud/pricing.mdx index 9a1a3733673f1..c217ef36d86d4 100644 --- a/docs/pages/product/deployment/cloud/pricing.mdx +++ b/docs/pages/product/deployment/cloud/pricing.mdx @@ -124,7 +124,7 @@ requests through [APIs & integrations][ref-apis] under the following tiers: | Tier | CCUs per hour | CPU and memory | Dependent features | | ---- | :--- | --- | --- | | S | 4 — _for Production Cluster_
1 — _for Development Instance_
1 — _for Cube API Instance_ | 100% | — | -| M | 8 — _for Production Cluster_
2 — _for Development Instance_
2 — _for Cube API Instance_ | 200% | [MDX API][ref-mdx-api] | +| M | 8 — _for Production Cluster_
2 — _for Development Instance_
2 — _for Cube API Instance_ | 200% | [DAX API][ref-dax-api], [MDX API][ref-mdx-api] | You can upgrade to a chosen tier in the Settings of your deployment. @@ -336,6 +336,7 @@ product tier level. Payments are non-refundable. [ref-development-instance]: /product/deployment/cloud/deployment-types#development-instance [ref-apis]: /product/apis-integrations [ref-mdx-api]: /product/apis-integrations/mdx-api +[ref-dax-api]: /product/apis-integrations/dax-api [ref-cube-store-architecture]: /product/caching/running-in-production#architecture [ref-data-at-rest-encryption]: /product/caching/running-in-production#data-at-rest-encryption [ref-customer-managed-keys]: /product/workspace/encryption-keys diff --git a/docs/pages/product/deployment/cloud/providers/azure.mdx b/docs/pages/product/deployment/cloud/providers/azure.mdx index eb31313feb3d3..088861e381be4 100644 --- a/docs/pages/product/deployment/cloud/providers/azure.mdx +++ b/docs/pages/product/deployment/cloud/providers/azure.mdx @@ -23,7 +23,9 @@ Cube Cloud integrates with the following [data sources](/product/configuration/d Cube Cloud integrates with the following [data visualization tools](/product/configuration/visualization-tools): -- [Microsoft Power BI](https://www.microsoft.com/en-us/power-platform/products/power-bi/) via [Semantic Layer Sync](/product/apis-integrations/semantic-layer-sync) +- [Microsoft Power BI][ref-powerbi] via the [DAX API][ref-dax-api] +- [Microsoft Excel][ref-excel] via the [MDX API][ref-mdx-api] and +[Cube Cloud for Excel][ref-cube-cloud-for-excel] ## Storage @@ -47,4 +49,11 @@ Cube Cloud integrates with the following [data visualization tools](/product/con ## Networking -[Azure Virtual Network](https://azure.microsoft.com/en-us/products/virtual-network) can be used for [secure networking](/product/deployment/cloud/vpc/azure). \ No newline at end of file +[Azure Virtual Network](https://azure.microsoft.com/en-us/products/virtual-network) can be used for [secure networking](/product/deployment/cloud/vpc/azure). + + +[ref-powerbi]: /product/configuration/visualization-tools/powerbi +[ref-dax-api]: /product/apis-integrations/dax-api +[ref-excel]: /product/configuration/visualization-tools/excel +[ref-mdx-api]: /product/apis-integrations/mdx-api +[ref-cube-cloud-for-excel]: /product/apis-integrations/microsoft-excel \ No newline at end of file diff --git a/docs/redirects.json b/docs/redirects.json index 0f246bcf95c2d..dd98aef061a37 100644 --- a/docs/redirects.json +++ b/docs/redirects.json @@ -1,4 +1,9 @@ [ + { + "source": "/product/apis-integrations/semantic-layer-sync/power-bi", + "destination": "/product/configuration/visualization-tools/powerbi", + "permanent": true + }, { "source": "/reference/frontend/cubejs-client-core", "destination": "/product/apis-integrations/javascript-sdk/reference/cubejs-client-core", diff --git a/packages/cubejs-backend-native/Cargo.lock b/packages/cubejs-backend-native/Cargo.lock index 5cd459688e993..188338412593e 100644 --- a/packages/cubejs-backend-native/Cargo.lock +++ b/packages/cubejs-backend-native/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "Inflector" @@ -95,11 +95,20 @@ dependencies = [ "libc", ] +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + [[package]] name = "anyhow" -version = "1.0.75" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" [[package]] name = "arc-swap" @@ -144,6 +153,24 @@ dependencies = [ "serde_json", ] +[[package]] +name = "ascii" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" + +[[package]] +name = "ast_node" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91fb5864e2f5bf9fd9797b94b2dfd1554d4c3092b535008b27d7e15c86675a2f" +dependencies = [ + "proc-macro2", + "quote", + "swc_macros_common", + "syn 2.0.90", +] + [[package]] name = "async-channel" version = "2.1.1" @@ -298,6 +325,24 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64-simd" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "781dd20c3aff0bd194fe7d2a977dd92f21c173891f3a03b677359e5fa457e5d5" +dependencies = [ + "simd-abstraction", +] + +[[package]] +name = "better_scoped_tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50fd297a11c709be8348aec039c8b91de16075d2b2bdaee1bd562c0875993664" +dependencies = [ + "scoped-tls", +] + [[package]] name = "bigdecimal" version = "0.4.2" @@ -328,9 +373,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" [[package]] name = "bitvec" @@ -432,9 +477,12 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +dependencies = [ + "allocator-api2", +] [[package]] name = "bytecheck" @@ -442,8 +490,20 @@ version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6372023ac861f6e6dc89c8344a8f398fb42aaba2b5dbc649ca0c0e9dbcb627" dependencies = [ - "bytecheck_derive", - "ptr_meta", + "bytecheck_derive 0.6.11", + "ptr_meta 0.1.4", + "simdutf8", +] + +[[package]] +name = "bytecheck" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50690fb3370fb9fe3550372746084c46f2ac8c9685c583d2be10eefd89d3d1a3" +dependencies = [ + "bytecheck_derive 0.8.1", + "ptr_meta 0.3.0", + "rancor", "simdutf8", ] @@ -458,6 +518,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "bytecheck_derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efb7846e0cb180355c2dec69e721edafa36919850f1a9f52ffba4ebc0393cb71" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "byteorder" version = "1.5.0" @@ -470,14 +541,70 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +[[package]] +name = "camino" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +dependencies = [ + "camino", + "cargo-platform", + "semver 1.0.23", + "serde", + "serde_json", + "thiserror 1.0.69", +] + +[[package]] +name = "cargo_metadata" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8769706aad5d996120af43197bf46ef6ad0fda35216b4505f926a365a232d924" +dependencies = [ + "camino", + "cargo-platform", + "semver 1.0.23", + "serde", + "serde_json", + "thiserror 2.0.11", +] + +[[package]] +name = "castaway" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5" +dependencies = [ + "rustversion", +] + [[package]] name = "cc" -version = "1.1.5" +version = "1.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "324c74f2155653c90b04f25b2a47a8a631360cb908f92a772695f430c7e31052" +checksum = "0c3d1b2e905a3a7b00a6141adb0e4c0bb941d11caf55349d863942a1cc44e3c9" dependencies = [ "jobserver", "libc", + "shlex", ] [[package]] @@ -494,9 +621,9 @@ checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" [[package]] name = "chrono" -version = "0.4.31" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" dependencies = [ "android-tzdata", "iana-time-zone", @@ -504,7 +631,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.48.5", + "windows-targets 0.52.0", ] [[package]] @@ -585,6 +712,19 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "compact_str" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "ryu", + "static_assertions", +] + [[package]] name = "concurrent-queue" version = "2.4.0" @@ -648,7 +788,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.8.0", "crossterm_winapi", "libc", "parking_lot", @@ -737,12 +877,14 @@ dependencies = [ "cubeorchestrator", "cubesql", "cubesqlplanner", + "cubetranspilers", "findshlibs", "futures", "http-body-util", "libc", "log", "log-reroute", + "lru", "minijinja", "neon", "once_cell", @@ -817,9 +959,9 @@ dependencies = [ "futures", "futures-core", "futures-util", - "hashbrown 0.14.3", + "hashbrown 0.14.5", "indexmap 1.9.3", - "itertools 0.10.5", + "itertools 0.14.0", "log", "lru", "minijinja", @@ -865,6 +1007,55 @@ dependencies = [ "tokio", ] +[[package]] +name = "cubetranspilers" +version = "0.1.0" +dependencies = [ + "anyhow", + "indexmap 2.7.1", + "regex", + "serde", + "serde_json", + "swc_core", + "swc_ecma_codegen", + "swc_ecma_parser", +] + +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.90", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.90", +] + [[package]] name = "data-encoding" version = "2.6.0" @@ -963,6 +1154,59 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "debugid" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" +dependencies = [ + "serde", + "uuid 1.6.1", +] + +[[package]] +name = "derive_builder" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" +dependencies = [ + "derive_builder_core", + "syn 2.0.90", +] + +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + +[[package]] +name = "difference" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" + [[package]] name = "digest" version = "0.10.7" @@ -974,6 +1218,17 @@ dependencies = [ "subtle", ] +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "egg" version = "0.9.5" @@ -992,15 +1247,15 @@ dependencies = [ "smallvec", "symbol_table", "symbolic_expressions", - "thiserror 1.0.50", + "thiserror 1.0.69", "vectorize", ] [[package]] name = "either" -version = "1.9.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "env_logger" @@ -1086,7 +1341,7 @@ checksum = "86b428b715fdbdd1c364b84573b5fdc0f84f8e423661b9f398735278bc7f2b6a" dependencies = [ "bitflags 1.3.2", "smallvec", - "thiserror 1.0.50", + "thiserror 1.0.69", ] [[package]] @@ -1096,7 +1351,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dac53e22462d78c16d64a1cd22371b54cc3fe94aa15e7886a2fa6e5d1ab8640" dependencies = [ "bitflags 1.3.2", - "rustc_version", + "rustc_version 0.4.1", ] [[package]] @@ -1115,6 +1370,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -1124,6 +1385,17 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "from_variant" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d7ccf961415e7aa17ef93dcb6c2441faaa8e768abe09e659b908089546f74c5" +dependencies = [ + "proc-macro2", + "swc_macros_common", + "syn 2.0.90", +] + [[package]] name = "funty" version = "2.0.0" @@ -1255,6 +1527,12 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +[[package]] +name = "glob" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + [[package]] name = "globset" version = "0.4.13" @@ -1297,14 +1575,25 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash 0.8.11", "allocator-api2", ] +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + [[package]] name = "heck" version = "0.3.3" @@ -1320,6 +1609,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -1350,6 +1645,20 @@ dependencies = [ "digest", ] +[[package]] +name = "hstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a26def229ea95a8709dad32868d975d0dd40235bd2ce82920e4a8fe692b5e0" +dependencies = [ + "hashbrown 0.14.5", + "new_debug_unreachable", + "once_cell", + "phf", + "rustc-hash 1.1.0", + "triomphe", +] + [[package]] name = "http" version = "1.1.0" @@ -1478,64 +1787,205 @@ dependencies = [ ] [[package]] -name = "idna" -version = "0.5.0" +name = "icu_collections" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "displaydoc", + "yoke", + "zerofrom", + "zerovec", ] [[package]] -name = "ignore" -version = "0.4.20" +name = "icu_locid" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbe7873dab538a9a44ad79ede1faf5f30d49f9a5c883ddbab48bce81b64b7492" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" dependencies = [ - "globset", - "lazy_static", - "log", - "memchr", - "regex", - "same-file", - "thread_local", - "walkdir", - "winapi-util", + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", ] [[package]] -name = "indexmap" -version = "1.9.3" +name = "icu_locid_transform" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" dependencies = [ - "autocfg", - "hashbrown 0.12.3", - "serde", + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", ] [[package]] -name = "indexmap" -version = "2.4.0" +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" dependencies = [ - "equivalent", - "hashbrown 0.14.3", + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", ] [[package]] -name = "indoc" -version = "2.0.4" +name = "icu_normalizer_data" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" [[package]] -name = "instant" -version = "0.1.12" +name = "icu_properties" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "if_chain" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" + +[[package]] +name = "ignore" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbe7873dab538a9a44ad79ede1faf5f30d49f9a5c883ddbab48bce81b64b7492" +dependencies = [ + "globset", + "lazy_static", + "log", + "memchr", + "regex", + "same-file", + "thread_local", + "walkdir", + "winapi-util", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" +dependencies = [ + "equivalent", + "hashbrown 0.15.2", +] + +[[package]] +name = "indoc" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8" + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if", ] @@ -1552,6 +2002,18 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +[[package]] +name = "is-macro" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57a3e447e24c22647738e4607f1df1e0ec6f72e16182c4cd199f647cdfb0e4" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "is-terminal" version = "0.4.9" @@ -1581,6 +2043,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.10" @@ -1677,9 +2148,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.151" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libloading" @@ -1703,6 +2174,12 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + [[package]] name = "lock_api" version = "0.4.11" @@ -1732,11 +2209,11 @@ dependencies = [ [[package]] name = "lru" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2994eeba8ed550fd9b47a0b38f0242bc3344e496483c6180b69139cc2fa5d1d7" +checksum = "227748d55f2f0ab4735d87fd623798cb6b664512fe979705f829c9f81c934465" dependencies = [ - "hashbrown 0.14.3", + "hashbrown 0.15.2", ] [[package]] @@ -1759,6 +2236,15 @@ dependencies = [ "libc", ] +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "matchit" version = "0.7.3" @@ -1796,6 +2282,31 @@ dependencies = [ "autocfg", ] +[[package]] +name = "miette" +version = "7.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a955165f87b37fd1862df2a59547ac542c77ef6d17c666f619d1ad22dd89484" +dependencies = [ + "cfg-if", + "miette-derive", + "owo-colors", + "textwrap", + "thiserror 1.0.69", + "unicode-width", +] + +[[package]] +name = "miette-derive" +version = "7.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf45bf44ab49be92fd1227a3be6fc6f617f1a337c06af54981048574d8783147" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "mime" version = "0.3.17" @@ -1864,6 +2375,26 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "munge" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64142d38c84badf60abf06ff9bd80ad2174306a5b11bd4706535090a30a419df" +dependencies = [ + "munge_macro", +] + +[[package]] +name = "munge_macro" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb5c1d8184f13f7d0ccbeeca0def2f9a181bce2624302793005f5ca8aa62e5e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "nativebridge" version = "0.1.0" @@ -1886,7 +2417,7 @@ dependencies = [ "libloading", "neon-macros", "once_cell", - "semver", + "semver 1.0.23", "send_wrapper", "smallvec", "tokio", @@ -1903,6 +2434,22 @@ dependencies = [ "syn-mid", ] +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num" version = "0.4.1" @@ -1926,6 +2473,7 @@ dependencies = [ "autocfg", "num-integer", "num-traits", + "serde", ] [[package]] @@ -2022,6 +2570,24 @@ dependencies = [ "num-traits", ] +[[package]] +name = "outref" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f222829ae9293e33a9f5e9f440c6760a3d450a64affe1846486b140db81c1f4" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "owo-colors" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb37767f6569cd834a413442455e0f066d0d522de8630436e2a1761d9726ba56" + [[package]] name = "parking" version = "2.2.0" @@ -2109,7 +2675,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae9cee2a55a544be8b89dc6848072af97a20f2422603c10865be2a42b580fff5" dependencies = [ "memchr", - "thiserror 1.0.50", + "thiserror 1.0.69", "ucd-trie", ] @@ -2166,6 +2732,7 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" dependencies = [ + "phf_macros", "phf_shared", ] @@ -2189,6 +2756,19 @@ dependencies = [ "rand", ] +[[package]] +name = "phf_macros" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "phf_shared" version = "0.11.2" @@ -2286,6 +2866,16 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "pretty_assertions" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" +dependencies = [ + "diff", + "yansi", +] + [[package]] name = "proc-macro-crate" version = "2.0.1" @@ -2328,13 +2918,31 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "psm" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200b9ff220857e53e184257720a14553b2f4aa02577d2ed9842d45d4b9654810" +dependencies = [ + "cc", +] + [[package]] name = "ptr_meta" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" dependencies = [ - "ptr_meta_derive", + "ptr_meta_derive 0.1.4", +] + +[[package]] +name = "ptr_meta" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9e76f66d3f9606f44e45598d155cb13ecf09f4a28199e48daf8c8fc937ea90" +dependencies = [ + "ptr_meta_derive 0.3.0", ] [[package]] @@ -2348,6 +2956,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "ptr_meta_derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca414edb151b4c8d125c12566ab0d74dc9cdba36fb80eb7b848c15f495fd32d1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "pyo3" version = "0.20.0" @@ -2444,9 +3063,9 @@ dependencies = [ "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash", + "rustc-hash 1.1.0", "rustls", - "thiserror 1.0.50", + "thiserror 1.0.69", "tokio", "tracing", ] @@ -2460,10 +3079,10 @@ dependencies = [ "bytes", "rand", "ring", - "rustc-hash", + "rustc-hash 1.1.0", "rustls", "slab", - "thiserror 1.0.50", + "thiserror 1.0.69", "tinyvec", "tracing", ] @@ -2496,6 +3115,15 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +[[package]] +name = "rancor" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf5f7161924b9d1cea0e4cabc97c372cea92b5f927fc13c6bca67157a0ad947" +dependencies = [ + "ptr_meta 0.3.0", +] + [[package]] name = "rand" version = "0.8.5" @@ -2537,25 +3165,34 @@ dependencies = [ [[package]] name = "regex" -version = "1.10.2" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax 0.8.2", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", ] [[package]] name = "regex-automata" -version = "0.4.3" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.2", + "regex-syntax 0.8.5", ] [[package]] @@ -2566,9 +3203,15 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "relative-path" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" [[package]] name = "rend" @@ -2576,7 +3219,16 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2571463863a6bd50c32f94402933f03457a3fbaf697a707c5be741e459f08fd" dependencies = [ - "bytecheck", + "bytecheck 0.6.11", +] + +[[package]] +name = "rend" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a35e8a6bf28cd121053a66aa2e6a2e3eaffad4a60012179f0e864aa5ffeff215" +dependencies = [ + "bytecheck 0.8.1", ] [[package]] @@ -2633,7 +3285,7 @@ dependencies = [ "http", "reqwest", "serde", - "thiserror 1.0.50", + "thiserror 1.0.69", "tower-service", ] @@ -2658,16 +3310,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0200c8230b013893c0b2d6213d6ec64ed2b9be2e0e016682b7224ff82cff5c58" dependencies = [ "bitvec", - "bytecheck", + "bytecheck 0.6.11", "hashbrown 0.12.3", - "ptr_meta", - "rend", - "rkyv_derive", + "ptr_meta 0.1.4", + "rend 0.4.1", + "rkyv_derive 0.7.42", "seahash", "tinyvec", "uuid 1.6.1", ] +[[package]] +name = "rkyv" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e147371c75553e1e2fcdb483944a8540b8438c31426279553b9a8182a9b7b65" +dependencies = [ + "bytecheck 0.8.1", + "bytes", + "hashbrown 0.15.2", + "indexmap 2.7.1", + "munge", + "ptr_meta 0.3.0", + "rancor", + "rend 0.5.2", + "rkyv_derive 0.8.10", + "tinyvec", + "uuid 1.6.1", +] + [[package]] name = "rkyv_derive" version = "0.7.42" @@ -2680,18 +3351,29 @@ dependencies = [ ] [[package]] -name = "rust_decimal" -version = "1.33.1" +name = "rkyv_derive" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06676aec5ccb8fc1da723cc8c0f9a46549f21ebb8753d3915c6c41db1e7f1dc4" +checksum = "246b40ac189af6c675d124b802e8ef6d5246c53e17367ce9501f8f66a81abb7a" dependencies = [ - "arrayvec", - "borsh", - "bytes", - "num-traits", + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "rust_decimal" +version = "1.33.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06676aec5ccb8fc1da723cc8c0f9a46549f21ebb8753d3915c6c41db1e7f1dc4" +dependencies = [ + "arrayvec", + "borsh", + "bytes", + "num-traits", "postgres", "rand", - "rkyv", + "rkyv 0.7.42", "serde", "serde_json", ] @@ -2708,13 +3390,28 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver 0.9.0", +] + [[package]] name = "rustc_version" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver", + "semver 1.0.23", ] [[package]] @@ -2723,7 +3420,7 @@ version = "0.38.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.8.0", "errno", "libc", "linux-raw-sys", @@ -2773,9 +3470,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" [[package]] name = "ryu" @@ -2783,6 +3480,12 @@ version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +[[package]] +name = "ryu-js" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad97d4ce1560a5e27cec89519dc8300d1aa6035b099821261c651486a19e44d5" + [[package]] name = "same-file" version = "1.0.6" @@ -2798,6 +3501,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ece8e78b2f38ec51c51f5d475df0a7187ba5111b2a28bdc761ee05b075d40a71" +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + [[package]] name = "scopeguard" version = "1.2.0" @@ -2816,11 +3525,29 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e388332cd64eb80cd595a00941baf513caffae8dce9cfd0467fc9c66397dade6" +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + [[package]] name = "semver" version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" +dependencies = [ + "serde", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "send_wrapper" @@ -2830,18 +3557,18 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.215" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.215" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", @@ -2850,11 +3577,11 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.133" +version = "1.0.138" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377" +checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" dependencies = [ - "indexmap 2.4.0", + "indexmap 2.7.1", "itoa", "memchr", "ryu", @@ -2911,6 +3638,21 @@ dependencies = [ "digest", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -2920,6 +3662,15 @@ dependencies = [ "libc", ] +[[package]] +name = "simd-abstraction" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cadb29c57caadc51ff8346233b5cec1d240b68ce55cf1afc764818791876987" +dependencies = [ + "outref", +] + [[package]] name = "simdutf8" version = "0.1.4" @@ -2960,6 +3711,17 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +[[package]] +name = "smartstring" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29" +dependencies = [ + "autocfg", + "static_assertions", + "version_check", +] + [[package]] name = "snap" version = "1.1.1" @@ -2976,6 +3738,25 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "sourcemap" +version = "9.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27c4ea7042fd1a155ad95335b5d505ab00d5124ea0332a06c8390d200bb1a76a" +dependencies = [ + "base64-simd", + "bitvec", + "data-encoding", + "debugid", + "if_chain", + "rustc-hash 1.1.0", + "rustc_version 0.2.3", + "serde", + "serde_json", + "unicode-id-start", + "url", +] + [[package]] name = "spin" version = "0.9.8" @@ -2990,12 +3771,43 @@ dependencies = [ "log", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "stacker" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d08feb8f695b465baed819b03c128dc23f57a694510ab1f06c77f763975685e" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "windows-sys 0.52.0", +] + [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "string_enum" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9fe66b8ee349846ce2f9557a26b8f1e74843c4a13fb381f9a3d73617a5f956a" +dependencies = [ + "proc-macro2", + "quote", + "swc_macros_common", + "syn 2.0.90", +] + [[package]] name = "stringprep" version = "0.1.4" @@ -3007,6 +3819,12 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "strum" version = "0.23.0" @@ -3051,6 +3869,367 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +[[package]] +name = "swc_allocator" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a1f988452cab8c4e25776e5a855ba088cdb38fbe9714f9b9d2a6ff345824858" +dependencies = [ + "bumpalo", + "hashbrown 0.14.5", + "ptr_meta 0.3.0", + "rustc-hash 2.1.1", + "triomphe", +] + +[[package]] +name = "swc_atoms" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c24077f986f0bc1c07823f850f688dd9be91b186efdb03fe1d52f7c2f2a4a346" +dependencies = [ + "bytecheck 0.8.1", + "hstr", + "once_cell", + "rancor", + "rkyv 0.8.10", + "rustc-hash 2.1.1", + "serde", +] + +[[package]] +name = "swc_common" +version = "6.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7327d132e85f8a50e0a9e2458a7b44726b2db2f7f9c8b8a556f07f359c42a461" +dependencies = [ + "anyhow", + "ast_node", + "better_scoped_tls", + "bytecheck 0.8.1", + "cfg-if", + "either", + "from_variant", + "new_debug_unreachable", + "num-bigint", + "once_cell", + "parking_lot", + "rancor", + "rkyv 0.8.10", + "rustc-hash 2.1.1", + "serde", + "siphasher", + "sourcemap", + "swc_allocator", + "swc_atoms", + "swc_eq_ignore_macros", + "swc_visit", + "termcolor", + "tracing", + "unicode-width", + "url", +] + +[[package]] +name = "swc_core" +version = "13.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94c8a27be20f13eb5b27e623b0d9ef486d861066b5f725e23ed38ca0e560dcca" +dependencies = [ + "once_cell", + "swc_allocator", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_transforms_base", + "swc_ecma_transforms_testing", + "swc_ecma_visit", + "swc_plugin", + "swc_plugin_macro", + "swc_plugin_proxy", + "vergen", +] + +[[package]] +name = "swc_ecma_ast" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbd513dab5fb1181e66ac34c4c959e9e8824d8d2c8bd50f698f5f2943794c0cc" +dependencies = [ + "bitflags 2.8.0", + "bytecheck 0.8.1", + "is-macro", + "num-bigint", + "phf", + "rancor", + "rkyv 0.8.10", + "scoped-tls", + "string_enum", + "swc_atoms", + "swc_common", + "swc_visit", + "unicode-id-start", +] + +[[package]] +name = "swc_ecma_codegen" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa6bdce26d910981128bc709a997292a5d1c98d54a9832154a1083d1adb6fdd7" +dependencies = [ + "ascii", + "compact_str", + "memchr", + "num-bigint", + "once_cell", + "regex", + "rustc-hash 2.1.1", + "serde", + "sourcemap", + "swc_allocator", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_codegen_macros", + "tracing", +] + +[[package]] +name = "swc_ecma_codegen_macros" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f9a42f479a6475647e248fa9750982c87cd985e19d1016a1fc18a70682305d1" +dependencies = [ + "proc-macro2", + "quote", + "swc_macros_common", + "syn 2.0.90", +] + +[[package]] +name = "swc_ecma_parser" +version = "8.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a359eaebed82e5e13e1004d1e4003931b66d8b8edab8884f3d02ed827df7530" +dependencies = [ + "either", + "new_debug_unreachable", + "num-bigint", + "num-traits", + "phf", + "rustc-hash 2.1.1", + "serde", + "smallvec", + "smartstring", + "stacker", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "tracing", + "typed-arena", +] + +[[package]] +name = "swc_ecma_testing" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46f1b2d6510edc0f54f0856c2e776b5673c3df8088dd0bda8d97ba197d054133" +dependencies = [ + "anyhow", + "hex", + "sha2", + "testing", + "tracing", +] + +[[package]] +name = "swc_ecma_transforms_base" +version = "9.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b696e003dd095ae8b8dba00f601040f756273c9af0fd67cb1c57115785cb5ec" +dependencies = [ + "better_scoped_tls", + "bitflags 2.8.0", + "indexmap 2.7.1", + "once_cell", + "phf", + "rustc-hash 2.1.1", + "serde", + "smallvec", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_parser", + "swc_ecma_utils", + "swc_ecma_visit", + "swc_parallel", + "tracing", +] + +[[package]] +name = "swc_ecma_transforms_testing" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f1ad440cfdcafabcc4ab8bb01df2d1a488f65cdad8dc85de821f66ebcedca55" +dependencies = [ + "ansi_term", + "anyhow", + "base64 0.21.5", + "hex", + "serde", + "serde_json", + "sha2", + "sourcemap", + "swc_allocator", + "swc_common", + "swc_ecma_ast", + "swc_ecma_codegen", + "swc_ecma_parser", + "swc_ecma_testing", + "swc_ecma_transforms_base", + "swc_ecma_utils", + "swc_ecma_visit", + "tempfile", + "testing", +] + +[[package]] +name = "swc_ecma_utils" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c217edaa22c98537e09ed3189e723feed3d889eeb7e02a0b3d48cbb91ba7e4" +dependencies = [ + "indexmap 2.7.1", + "num_cpus", + "once_cell", + "rustc-hash 2.1.1", + "ryu-js", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_visit", + "swc_parallel", + "tracing", + "unicode-id", +] + +[[package]] +name = "swc_ecma_visit" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a32fb2902c01f9b4615605a4a3e67e0c928bd3b9f2182e764f1c9fe4130965cf" +dependencies = [ + "new_debug_unreachable", + "num-bigint", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_visit", + "tracing", +] + +[[package]] +name = "swc_eq_ignore_macros" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e96e15288bf385ab85eb83cff7f9e2d834348da58d0a31b33bdb572e66ee413e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "swc_error_reporters" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed21ea887faeb0dab190838d2331ed187f2a74d185c9fe7044d5092900a83d29" +dependencies = [ + "anyhow", + "miette", + "once_cell", + "parking_lot", + "swc_common", +] + +[[package]] +name = "swc_macros_common" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a509f56fca05b39ba6c15f3e58636c3924c78347d63853632ed2ffcb6f5a0ac7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "swc_parallel" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5f75f1094d69174ef628e3665fff0f81d58e9f568802e3c90d332c72b0b6026" +dependencies = [ + "once_cell", +] + +[[package]] +name = "swc_plugin" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b45099a38ed45528bef939d0eac1a0c1347749d0c67d3dd744d545316c5fd05" +dependencies = [ + "once_cell", +] + +[[package]] +name = "swc_plugin_macro" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0917ccfdcd3fa6cf41bdacef2388702a3b274f9ea708d930e1e8db37c7c3e1c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "swc_plugin_proxy" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61e8ae3974157b2939ada468ffec7932358f2f567abb6c237204dd603e52ffff" +dependencies = [ + "better_scoped_tls", + "bytecheck 0.8.1", + "rancor", + "rkyv 0.8.10", + "rustc-hash 2.1.1", + "swc_common", + "swc_ecma_ast", + "swc_trace_macro", + "tracing", +] + +[[package]] +name = "swc_trace_macro" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c78717a841565df57f811376a3d19c9156091c55175e12d378f3a522de70cef" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + +[[package]] +name = "swc_visit" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9138b6a36bbe76dd6753c4c0794f7e26480ea757bee499738bedbbb3ae3ec5f3" +dependencies = [ + "either", + "new_debug_unreachable", +] + [[package]] name = "symbol_table" version = "0.2.0" @@ -3125,6 +4304,17 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "tap" version = "1.0.1" @@ -3166,13 +4356,70 @@ dependencies = [ "unic-segment", ] +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "testing" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60326bf11ba23afed0b731866c6e8b709d516554dc813bb3a91f8a273f22f333" +dependencies = [ + "ansi_term", + "cargo_metadata 0.18.1", + "difference", + "once_cell", + "pretty_assertions", + "regex", + "rustc-hash 2.1.1", + "serde", + "serde_json", + "swc_common", + "swc_error_reporters", + "testing_macros", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "testing_macros" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2d27bf245b90a80d5aa231133418ae7db98f032855ce5292e12071ab29c4b26" +dependencies = [ + "anyhow", + "glob", + "once_cell", + "proc-macro2", + "quote", + "regex", + "relative-path", + "syn 2.0.90", +] + +[[package]] +name = "textwrap" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" +dependencies = [ + "unicode-linebreak", + "unicode-width", +] + [[package]] name = "thiserror" -version = "1.0.50" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl 1.0.50", + "thiserror-impl 1.0.69", ] [[package]] @@ -3186,9 +4433,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "1.0.50" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", @@ -3238,6 +4485,16 @@ dependencies = [ "threadpool", ] +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -3371,7 +4628,7 @@ version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" dependencies = [ - "indexmap 2.4.0", + "indexmap 2.7.1", "toml_datetime", "winnow", ] @@ -3434,6 +4691,46 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "triomphe" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef8f7726da4807b58ea5c96fdc122f80702030edc33b35aff9190a51148ccc85" +dependencies = [ + "serde", + "stable_deref_trait", ] [[package]] @@ -3456,11 +4753,17 @@ dependencies = [ "log", "rand", "sha1", - "thiserror 1.0.50", + "thiserror 1.0.69", "url", "utf-8", ] +[[package]] +name = "typed-arena" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" + [[package]] name = "typenum" version = "1.17.0" @@ -3547,12 +4850,30 @@ version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" +[[package]] +name = "unicode-id" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10103c57044730945224467c09f71a4db0071c123a0648cc3e818913bde6b561" + +[[package]] +name = "unicode-id-start" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f322b60f6b9736017344fa0635d64be2f458fbc04eef65f6be22976dd1ffd5b" + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + [[package]] name = "unicode-normalization" version = "0.1.22" @@ -3588,9 +4909,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.0" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna", @@ -3603,6 +4924,18 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "uuid" version = "0.8.2" @@ -3622,6 +4955,12 @@ dependencies = [ "serde", ] +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + [[package]] name = "vectorize" version = "0.2.0" @@ -3631,6 +4970,31 @@ dependencies = [ "serde", ] +[[package]] +name = "vergen" +version = "9.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0d2f179f8075b805a43a2a21728a46f0cc2921b3c58695b28fa8817e103cd9a" +dependencies = [ + "anyhow", + "cargo_metadata 0.19.1", + "derive_builder", + "regex", + "rustversion", + "vergen-lib", +] + +[[package]] +name = "vergen-lib" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b07e6010c0f3e59fcb164e0163834597da68d1f864e2b8ca49f74de01e9c166" +dependencies = [ + "anyhow", + "derive_builder", + "rustversion", +] + [[package]] name = "version_check" version = "0.9.4" @@ -3948,6 +5312,18 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "wyz" version = "0.5.1" @@ -3957,6 +5333,36 @@ dependencies = [ "tap", ] +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", + "synstructure", +] + [[package]] name = "zerocopy" version = "0.7.32" @@ -3977,12 +5383,55 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", + "synstructure", +] + [[package]] name = "zeroize" version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.90", +] + [[package]] name = "zstd" version = "0.11.2+zstd.1.5.2" diff --git a/packages/cubejs-backend-native/Cargo.toml b/packages/cubejs-backend-native/Cargo.toml index 37ddb4094c1c1..a03ed00820821 100644 --- a/packages/cubejs-backend-native/Cargo.toml +++ b/packages/cubejs-backend-native/Cargo.toml @@ -18,6 +18,7 @@ crate-type = ["cdylib", "lib"] [dependencies] cubesqlplanner = { path = "../../rust/cubesqlplanner/cubesqlplanner" } cubeorchestrator = { path = "../../rust/cubeorchestrator" } +cubetranspilers = { path = "../../rust/cubetranspilers" } cubenativeutils = { path = "../../rust/cubenativeutils" } cubesql = { path = "../../rust/cubesql/cubesql" } anyhow = "1.0" @@ -36,14 +37,18 @@ minijinja = { version = "1", features = ["json", "loader"] } once_cell = "1.10" # python pyo3 = { version = "0.20.0", features = [], optional = true } -pyo3-asyncio = { version = "0.20.0", features = ["tokio-runtime", "attributes"], optional = true } -serde = { version = "1.0.209", features = ["derive"] } +pyo3-asyncio = { version = "0.20.0", features = [ + "tokio-runtime", + "attributes", +], optional = true } +serde = { version = "1.0.217", features = ["derive"] } serde_json = "1.0.127" simple_logger = "1.7.0" tokio = { version = "1", features = ["full", "rt"] } uuid = { version = "1", features = ["v4"] } bytes = "1.5.0" regex = "1.10.2" +lru = "0.13.0" [dependencies.neon] version = "=1" diff --git a/packages/cubejs-backend-native/js/index.ts b/packages/cubejs-backend-native/js/index.ts index f6c54477f463f..c745c27a35849 100644 --- a/packages/cubejs-backend-native/js/index.ts +++ b/packages/cubejs-backend-native/js/index.ts @@ -101,6 +101,23 @@ export type SQLInterfaceOptions = { gatewayPort?: number, }; +export interface TransformConfig { + fileName: string; + transpilers: string[]; + compilerId: string; + metaData?: { + cubeNames: string[]; + cubeSymbols: Record>; + contextSymbols: Record; + } +} + +export interface TransformResponse { + code: string; + errors: string[]; + warnings: string[]; +} + export type DBResponsePrimitive = null | boolean | @@ -405,6 +422,12 @@ export const getFinalQueryResultMulti = (transformDataArr: Object[], rows: any[] return native.getFinalQueryResultMulti(transformDataArr, rows, responseData); }; +export const transpileJs = async (content: String, metadata: TransformConfig): Promise => { + const native = loadNative(); + + return native.transpileJs(content, metadata); +}; + export interface PyConfiguration { repositoryFactory?: (ctx: unknown) => Promise, logger?: (msg: string, params: Record) => void, diff --git a/packages/cubejs-backend-native/src/lib.rs b/packages/cubejs-backend-native/src/lib.rs index 60104692c697f..516db7b1c5af8 100644 --- a/packages/cubejs-backend-native/src/lib.rs +++ b/packages/cubejs-backend-native/src/lib.rs @@ -17,6 +17,7 @@ pub mod orchestrator; pub mod python; pub mod stream; pub mod template; +pub mod transpilers; pub mod transport; pub mod utils; diff --git a/packages/cubejs-backend-native/src/node_export.rs b/packages/cubejs-backend-native/src/node_export.rs index 802076279c47d..b34d00c646ef1 100644 --- a/packages/cubejs-backend-native/src/node_export.rs +++ b/packages/cubejs-backend-native/src/node_export.rs @@ -510,6 +510,9 @@ pub fn register_module_exports( //========= sql orchestrator exports ================= crate::orchestrator::register_module(&mut cx)?; + //========= transpilers exports ================= + crate::transpilers::register_module(&mut cx)?; + crate::template::template_register_module(&mut cx)?; #[cfg(feature = "python")] diff --git a/packages/cubejs-backend-native/src/transpilers.rs b/packages/cubejs-backend-native/src/transpilers.rs new file mode 100644 index 0000000000000..d9706fd2d7fbb --- /dev/null +++ b/packages/cubejs-backend-native/src/transpilers.rs @@ -0,0 +1,119 @@ +use crate::node_obj_deserializer::JsValueDeserializer; +use crate::node_obj_serializer::NodeObjSerializer; +use anyhow::anyhow; +use cubetranspilers::{run_transpilers, TransformConfig, Transpilers}; +use lru::LruCache; +use neon::context::{Context, FunctionContext, ModuleContext}; +use neon::prelude::{JsPromise, JsResult, JsValue, NeonResult}; +use neon::types::JsString; +use serde::Deserialize; +use std::collections::{HashMap, HashSet}; +use std::env; +use std::num::NonZeroUsize; +use std::sync::{LazyLock, Mutex}; + +#[derive(Deserialize, Default, Clone, Debug)] +#[serde(rename_all = "camelCase")] +pub struct TransformMetaData { + pub cube_names: HashSet, + pub cube_symbols: HashMap>, + pub context_symbols: HashMap, +} + +#[derive(Deserialize, Clone, Debug)] +#[serde(rename_all = "camelCase")] +pub struct TransformRequestConfig { + pub file_name: String, + pub transpilers: Vec, + pub compiler_id: String, + pub meta_data: Option, +} + +/// It should be equal or more then number of internal libuv threads used by Neon +/// By 01.2025 it defaults to 4. But maybe changed via `UV_THREADPOOL_SIZE` env var. +/// `CUBEJS_TRANSPILER_METADATA_CACHE_SIZE` env var is provided for fine tuning. +/// @see https://docs.libuv.org/en/v1.x/threadpool.html +/// @see https://nodejs.org/api/cli.html#cli_uv_threadpool_size_size +static DEFAULT_CACHE_SIZE: usize = 16; + +static METADATA_CACHE: LazyLock>> = LazyLock::new(|| { + let cache_size = env::var("CUBEJS_TRANSPILER_METADATA_CACHE_SIZE") + .ok() + .and_then(|s| s.parse::().ok()) + .and_then(NonZeroUsize::new) + .unwrap_or(NonZeroUsize::new(DEFAULT_CACHE_SIZE).unwrap()); + Mutex::new(LruCache::new(cache_size)) +}); + +pub fn register_module(cx: &mut ModuleContext) -> NeonResult<()> { + cx.export_function("transpileJs", transpile_js)?; + + Ok(()) +} + +pub fn transpile_js(mut cx: FunctionContext) -> JsResult { + let content = cx.argument::(0)?.value(&mut cx); + let transform_data_js_object = cx.argument::(1)?; + let deserializer = JsValueDeserializer::new(&mut cx, transform_data_js_object); + let transform_request_config = TransformRequestConfig::deserialize(deserializer); + + let promise = cx + .task(move || { + let transform_config: TransformConfig = match transform_request_config { + Ok(data) => match data.meta_data { + Some(meta_data) => { + let mut config_lock = METADATA_CACHE.lock().unwrap(); + let cache = TransformMetaData { + cube_names: meta_data.cube_names, + cube_symbols: meta_data.cube_symbols, + context_symbols: meta_data.context_symbols, + }; + let cfg = TransformConfig { + file_name: data.file_name, + transpilers: data.transpilers, + cube_names: cache.cube_names.clone(), + cube_symbols: cache.cube_symbols.clone(), + context_symbols: cache.context_symbols.clone(), + }; + config_lock.put(data.compiler_id.clone(), cache); + cfg + } + None => { + let mut config_lock = METADATA_CACHE.lock().unwrap(); + + match config_lock.get(&data.compiler_id) { + Some(cached) => TransformConfig { + file_name: data.file_name, + transpilers: data.transpilers, + cube_names: cached.cube_names.clone(), + cube_symbols: cached.cube_symbols.clone(), + context_symbols: cached.context_symbols.clone(), + }, + None => TransformConfig { + file_name: data.file_name, + transpilers: data.transpilers, + cube_names: HashSet::new(), + cube_symbols: HashMap::new(), + context_symbols: HashMap::new(), + }, + } + } + }, + Err(err) => return Err(anyhow!("Failed to deserialize input data: {}", err)), + }; + + run_transpilers(content, transform_config) + }) + .promise(move |mut cx, res| match res { + Ok(result) => { + let obj = match NodeObjSerializer::serialize(&result, &mut cx) { + Ok(data) => data, + Err(err) => return cx.throw_error(err.to_string()), + }; + Ok(obj) + } + Err(err) => cx.throw_error(err.to_string()), + }); + + Ok(promise) +} diff --git a/packages/cubejs-backend-shared/src/env.ts b/packages/cubejs-backend-shared/src/env.ts index 93a1c7e5a49ef..85a177ca54fee 100644 --- a/packages/cubejs-backend-shared/src/env.ts +++ b/packages/cubejs-backend-shared/src/env.ts @@ -229,6 +229,10 @@ const variables: Record any> = { transpilationWorkerThreadsCount: () => get('CUBEJS_TRANSPILATION_WORKER_THREADS_COUNT') .default('0') .asInt(), + // This one takes precedence over CUBEJS_TRANSPILATION_WORKER_THREADS + transpilationNative: () => get('CUBEJS_TRANSPILATION_NATIVE') + .default('false') + .asBoolStrict(), /** **************************************************************** * Common db options * diff --git a/packages/cubejs-jdbc-driver/package.json b/packages/cubejs-jdbc-driver/package.json index 33319f5e63f69..d44800d09c518 100644 --- a/packages/cubejs-jdbc-driver/package.json +++ b/packages/cubejs-jdbc-driver/package.json @@ -44,7 +44,7 @@ }, "devDependencies": { "@cubejs-backend/linter": "1.2.5", - "@cubejs-backend/shared": "1.2.4", + "@cubejs-backend/shared": "1.2.5", "@types/generic-pool": "^3.1.9", "@types/node": "^18", "@types/sqlstring": "^2.3.0", diff --git a/packages/cubejs-schema-compiler/src/compiler/DataSchemaCompiler.js b/packages/cubejs-schema-compiler/src/compiler/DataSchemaCompiler.js index cd087329b9dfd..cfd770fbc9816 100644 --- a/packages/cubejs-schema-compiler/src/compiler/DataSchemaCompiler.js +++ b/packages/cubejs-schema-compiler/src/compiler/DataSchemaCompiler.js @@ -9,8 +9,10 @@ import R from 'ramda'; import workerpool from 'workerpool'; import { getEnv, isNativeSupported } from '@cubejs-backend/shared'; +import { transpileJs } from '@cubejs-backend/native'; import { UserError } from './UserError'; import { ErrorReporter } from './ErrorReporter'; +import { CONTEXT_SYMBOLS } from './CubeSymbols'; const NATIVE_IS_SUPPORTED = isNativeSupported(); @@ -44,6 +46,7 @@ export class DataSchemaCompiler { this.yamlCompiler.dataSchemaCompiler = this; this.pythonContext = null; this.workerPool = null; + this.compilerId = options.compilerId; } compileObjects(compileServices, objects, errorsReport) { @@ -93,7 +96,11 @@ export class DataSchemaCompiler { const errorsReport = new ErrorReporter(null, [], this.errorReport); this.errorsReport = errorsReport; - if (getEnv('transpilationWorkerThreads')) { + const transpilationWorkerThreads = getEnv('transpilationWorkerThreads'); + const transpilationNative = getEnv('transpilationNative'); + const { compilerId } = this; + + if (!transpilationNative && transpilationWorkerThreads) { const wc = getEnv('transpilationWorkerThreadsCount'); this.workerPool = workerpool.pool( path.join(__dirname, 'transpilers/transpiler_worker'), @@ -103,9 +110,11 @@ export class DataSchemaCompiler { const transpile = async () => { let cubeNames; - let cubeSymbolsNames; + let cubeSymbols; + let transpilerNames; + let results; - if (getEnv('transpilationWorkerThreads')) { + if (transpilationNative || transpilationWorkerThreads) { cubeNames = Object.keys(this.cubeDictionary.byId); // We need only cubes and all its member names for transpiling. // Cubes doesn't change during transpiling, but are changed during compilation phase, @@ -113,7 +122,8 @@ export class DataSchemaCompiler { // Communication between main and worker threads uses // The structured clone algorithm (@see https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm) // which doesn't allow passing any function objects, so we need to sanitize the symbols. - cubeSymbolsNames = Object.fromEntries( + // Communication with native backend also involves deserialization. + cubeSymbols = Object.fromEntries( Object.entries(this.cubeSymbols.symbols) .map( ([key, value]) => [key, Object.fromEntries( @@ -121,8 +131,27 @@ export class DataSchemaCompiler { )], ), ); + + // Transpilers are the same for all files within phase. + transpilerNames = this.transpilers.map(t => t.constructor.name); + } + + if (transpilationNative) { + // Warming up swc compiler cache + const dummyFile = { + fileName: 'dummy.js', + content: ';', + }; + + await this.transpileJsFile(dummyFile, errorsReport, { cubeNames, cubeSymbols, transpilerNames, contextSymbols: CONTEXT_SYMBOLS, compilerId }); + + results = await Promise.all(toCompile.map(f => this.transpileFile(f, errorsReport, { transpilerNames, compilerId }))); + } else if (transpilationWorkerThreads) { + results = await Promise.all(toCompile.map(f => this.transpileFile(f, errorsReport, { cubeNames, cubeSymbols, transpilerNames }))); + } else { + results = await Promise.all(toCompile.map(f => this.transpileFile(f, errorsReport, {}))); } - const results = await Promise.all(toCompile.map(f => this.transpileFile(f, errorsReport, { cubeNames, cubeSymbolsNames }))); + return results.filter(f => !!f); }; @@ -138,9 +167,23 @@ export class DataSchemaCompiler { contextCompilers: this.contextCompilers, })) .then(() => { - if (this.workerPool) { + if (transpilationNative) { + // Clean up cache + const dummyFile = { + fileName: 'terminate.js', + content: ';', + }; + + return this.transpileJsFile( + dummyFile, + errorsReport, + { cubeNames: [], cubeSymbols: {}, transpilerNames: [], contextSymbols: {}, compilerId: this.compilerId } + ); + } else if (transpilationWorkerThreads && this.workerPool) { this.workerPool.terminate(); } + + return Promise.resolve(); }); } @@ -182,15 +225,36 @@ export class DataSchemaCompiler { } } - async transpileJsFile(file, errorsReport, { cubeNames, cubeSymbolsNames }) { + async transpileJsFile(file, errorsReport, { cubeNames, cubeSymbols, contextSymbols, transpilerNames, compilerId }) { try { - if (getEnv('transpilationWorkerThreads')) { + if (getEnv('transpilationNative')) { + const reqData = { + fileName: file.fileName, + transpilers: transpilerNames, + compilerId, + ...(cubeNames && { + metaData: { + cubeNames, + cubeSymbols, + contextSymbols, + }, + }), + }; + + errorsReport.inFile(file); + const res = await transpileJs(file.content, reqData); + errorsReport.addErrors(res.errors); + errorsReport.addWarnings(res.warnings); + errorsReport.exitFile(); + + return Object.assign({}, file, { content: res.code }); + } else if (getEnv('transpilationWorkerThreads')) { const data = { fileName: file.fileName, content: file.content, - transpilers: this.transpilers.map(t => t.constructor.name), + transpilers: transpilerNames, cubeNames, - cubeSymbolsNames, + cubeSymbols, }; const res = await this.workerPool.exec('transpile', [data]); @@ -208,11 +272,11 @@ export class DataSchemaCompiler { }, ); + errorsReport.inFile(file); this.transpilers.forEach((t) => { - errorsReport.inFile(file); babelTraverse(ast, t.traverseObject(errorsReport)); - errorsReport.exitFile(); }); + errorsReport.exitFile(); const content = babelGenerator(ast, {}, file.content).code; return Object.assign({}, file, { content }); diff --git a/packages/cubejs-schema-compiler/src/compiler/ErrorReporter.ts b/packages/cubejs-schema-compiler/src/compiler/ErrorReporter.ts index bf48792f4a3ab..a4827647587a5 100644 --- a/packages/cubejs-schema-compiler/src/compiler/ErrorReporter.ts +++ b/packages/cubejs-schema-compiler/src/compiler/ErrorReporter.ts @@ -3,6 +3,12 @@ import { codeFrameColumns, SourceLocation } from '@babel/code-frame'; import { UserError } from './UserError'; import { CompileError } from './CompileError'; +export type ErrorLikeObject = { + message: string; +}; + +export type PossibleError = Error | UserError | string | ErrorLikeObject; + export interface CompilerErrorInterface { message: string; plainMessage?: string @@ -108,7 +114,7 @@ export class ErrorReporter { } public error(e: any, fileName?: any, lineNumber?: any, position?: any) { - const message = `${this.context.length ? `${this.context.join(' -> ')}: ` : ''}${e instanceof UserError ? e.message : (e.stack || e)}`; + const message = `${this.context.length ? `${this.context.join(' -> ')}: ` : ''}${e.message ? e.message : (e.stack || e)}`; if (this.rootReporter().errors.find(m => (m.message || m) === message)) { return; } @@ -141,8 +147,8 @@ export class ErrorReporter { return this.rootReporter().errors; } - public addErrors(errors: CompilerErrorInterface[]) { - this.rootReporter().errors.push(...errors); + public addErrors(errors: PossibleError[]) { + errors.forEach((e: any) => { this.error(e); }); } public getWarnings() { @@ -150,7 +156,7 @@ export class ErrorReporter { } public addWarnings(warnings: SyntaxErrorInterface[]) { - this.rootReporter().warnings.push(...warnings); + warnings.forEach(w => { this.warning(w); }); } protected rootReporter(): ErrorReporter { diff --git a/packages/cubejs-schema-compiler/src/compiler/PrepareCompiler.ts b/packages/cubejs-schema-compiler/src/compiler/PrepareCompiler.ts index 70f9f3c454704..57f615c2b7840 100644 --- a/packages/cubejs-schema-compiler/src/compiler/PrepareCompiler.ts +++ b/packages/cubejs-schema-compiler/src/compiler/PrepareCompiler.ts @@ -59,6 +59,8 @@ export const prepareCompiler = (repo: SchemaFileRepository, options: PrepareComp transpilers.push(new CubeCheckDuplicatePropTranspiler()); } + const compilerId = uuidv4(); + const compiler = new DataSchemaCompiler(repo, Object.assign({}, { cubeNameCompilers: [cubeDictionary], preTranspileCubeCompilers: [cubeSymbols, cubeValidator], @@ -80,6 +82,7 @@ export const prepareCompiler = (repo: SchemaFileRepository, options: PrepareComp standalone: options.standalone, nativeInstance, yamlCompiler, + compilerId, }, options)); return { @@ -90,7 +93,7 @@ export const prepareCompiler = (repo: SchemaFileRepository, options: PrepareComp joinGraph, compilerCache, headCommitId: options.headCommitId, - compilerId: uuidv4(), + compilerId, }; }; diff --git a/packages/cubejs-schema-compiler/src/compiler/transpilers/transpiler_worker.ts b/packages/cubejs-schema-compiler/src/compiler/transpilers/transpiler_worker.ts index 0d5d87ffc9f46..93c1c142744f0 100644 --- a/packages/cubejs-schema-compiler/src/compiler/transpilers/transpiler_worker.ts +++ b/packages/cubejs-schema-compiler/src/compiler/transpilers/transpiler_worker.ts @@ -16,7 +16,7 @@ type TransferContent = { content: string; transpilers: string[]; cubeNames: string[]; - cubeSymbolsNames: Record>; + cubeSymbols: Record>; }; const cubeDictionary = new LightweightNodeCubeDictionary(); @@ -32,7 +32,7 @@ const transpilers = { const transpile = (data: TransferContent) => { cubeDictionary.setCubeNames(data.cubeNames); - cubeSymbols.setSymbols(data.cubeSymbolsNames); + cubeSymbols.setSymbols(data.cubeSymbols); const ast = parse( data.content, @@ -43,15 +43,15 @@ const transpile = (data: TransferContent) => { }, ); + errorsReport.inFile(data); data.transpilers.forEach(transpilerName => { if (transpilers[transpilerName]) { - errorsReport.inFile(data); babelTraverse(ast, transpilers[transpilerName].traverseObject(errorsReport)); - errorsReport.exitFile(); } else { throw new Error(`Transpiler ${transpilerName} not supported`); } }); + errorsReport.exitFile(); const content = babelGenerator(ast, {}, data.content).code; diff --git a/packages/cubejs-schema-compiler/test/unit/transpilers.test.ts b/packages/cubejs-schema-compiler/test/unit/transpilers.test.ts index 56a53ee3d0a37..30e48a8a2eeb0 100644 --- a/packages/cubejs-schema-compiler/test/unit/transpilers.test.ts +++ b/packages/cubejs-schema-compiler/test/unit/transpilers.test.ts @@ -27,7 +27,7 @@ describe('Transpilers', () => { throw new Error('Compile should thrown an error'); } catch (e: any) { - expect(e.message).toMatch(/Duplicate property parsing test1 in main.js/); + expect(e.message).toMatch(/Duplicate property parsing test1/); } }); diff --git a/packages/cubejs-server-core/src/core/CompilerApi.js b/packages/cubejs-server-core/src/core/CompilerApi.js index 45ae80dc68375..1e7fd4e4bcb53 100644 --- a/packages/cubejs-server-core/src/core/CompilerApi.js +++ b/packages/cubejs-server-core/src/core/CompilerApi.js @@ -1,7 +1,7 @@ import crypto from 'crypto'; import R from 'ramda'; import { createQuery, compile, queryClass, PreAggregations, QueryFactory } from '@cubejs-backend/schema-compiler'; -import { v4 as uuidv4 } from 'uuid'; +import { v4 as uuidv4, parse as uuidParse, stringify as uuidStringify } from 'uuid'; import { NativeInstance } from '@cubejs-backend/native'; export class CompilerApi { @@ -428,7 +428,7 @@ export class CompilerApi { const { cubeEvaluator } = compilers; if (!cubeEvaluator.isRbacEnabled()) { - return cubes; + return { cubes, visibilityMaskHash: null }; } for (const cube of cubes) { @@ -481,23 +481,38 @@ export class CompilerApi { }); }; - return cubes - .map((cube) => ({ - config: { - ...cube.config, - measures: cube.config.measures?.map(visibilityPatcherForCube(cube)), - dimensions: cube.config.dimensions?.map(visibilityPatcherForCube(cube)), - segments: cube.config.segments?.map(visibilityPatcherForCube(cube)), - hierarchies: cube.config.hierarchies?.map(visibilityPatcherForCube(cube)), - }, - })); + const visibiliyMask = JSON.stringify(isMemberVisibleInContext, Object.keys(isMemberVisibleInContext).sort()); + // This hash will be returned along the modified meta config and can be used + // to distinguish between different "schema versions" after DAP visibility is applied + const visibilityMaskHash = crypto.createHash('sha256').update(visibiliyMask).digest('hex'); + + return { + cubes: cubes + .map((cube) => ({ + config: { + ...cube.config, + measures: cube.config.measures?.map(visibilityPatcherForCube(cube)), + dimensions: cube.config.dimensions?.map(visibilityPatcherForCube(cube)), + segments: cube.config.segments?.map(visibilityPatcherForCube(cube)), + hierarchies: cube.config.hierarchies?.map(visibilityPatcherForCube(cube)), + }, + })), + visibilityMaskHash + }; + } + + mixInVisibilityMaskHash(compilerId, visibilityMaskHash) { + const uuidBytes = uuidParse(compilerId); + const hashBytes = Buffer.from(visibilityMaskHash, 'hex'); + return uuidv4({ random: crypto.createHash('sha256').update(uuidBytes).update(hashBytes).digest() + .subarray(0, 16) }); } async metaConfig(requestContext, options = {}) { const { includeCompilerId, ...restOptions } = options; const compilers = await this.getCompilers(restOptions); const { cubes } = compilers.metaTransformer; - const patchedCubes = await this.patchVisibilityByAccessPolicy( + const { visibilityMaskHash, cubes: patchedCubes } = await this.patchVisibilityByAccessPolicy( compilers, requestContext, cubes @@ -505,7 +520,11 @@ export class CompilerApi { if (includeCompilerId) { return { cubes: patchedCubes, - compilerId: compilers.compilerId, + // This compilerId is primarily used by the cubejs-backend-native or caching purposes. + // By default it doesn't account for member visibility changes introduced above by DAP. + // Here we're modifying the originila compilerId in a way that it's distinct for + // distinct schema versions while still being a valid UUID. + compilerId: visibilityMaskHash ? this.mixInVisibilityMaskHash(compilers.compilerId, visibilityMaskHash) : compilers.compilerId, }; } return patchedCubes; @@ -513,7 +532,7 @@ export class CompilerApi { async metaConfigExtended(requestContext, options) { const compilers = await this.getCompilers(options); - const patchedCubes = await this.patchVisibilityByAccessPolicy( + const { cubes: patchedCubes } = await this.patchVisibilityByAccessPolicy( compilers, requestContext, compilers.metaTransformer?.cubes diff --git a/packages/cubejs-testing/birdbox-fixtures/rbac/cube.js b/packages/cubejs-testing/birdbox-fixtures/rbac/cube.js index e7c82f455e36f..6f50f842c97de 100644 --- a/packages/cubejs-testing/birdbox-fixtures/rbac/cube.js +++ b/packages/cubejs-testing/birdbox-fixtures/rbac/cube.js @@ -1,5 +1,6 @@ module.exports = { contextToRoles: async (context) => context.securityContext.auth?.roles || [], + canSwitchSqlUser: async () => true, checkSqlAuth: async (req, user, password) => { if (user === 'admin') { if (password && password !== 'admin_password') { @@ -64,6 +65,27 @@ module.exports = { }, }; } + if (user === 'restricted') { + if (password && password !== 'restricted_password') { + throw new Error(`Password doesn't match for ${user}`); + } + return { + password, + superuser: false, + securityContext: { + auth: { + username: 'default', + userAttributes: { + region: 'CA', + city: 'San Francisco', + canHaveAdmin: true, + minDefaultId: 20000, + }, + roles: ['restricted'], + }, + }, + }; + } throw new Error(`User "${user}" doesn't exist`); } }; diff --git a/packages/cubejs-testing/birdbox-fixtures/rbac/model/cubes/line_items.js b/packages/cubejs-testing/birdbox-fixtures/rbac/model/cubes/line_items.js index 7013052b0169e..8d35089fdbc43 100644 --- a/packages/cubejs-testing/birdbox-fixtures/rbac/model/cubes/line_items.js +++ b/packages/cubejs-testing/birdbox-fixtures/rbac/model/cubes/line_items.js @@ -60,6 +60,12 @@ cube('line_items', { excludes: ['count', 'price', 'price_dim'], }, }, + { + role: 'restricted', + memberLevel: { + excludes: ['count', 'price', 'price_dim'], + }, + }, { role: 'admin', conditions: [ diff --git a/packages/cubejs-testing/test/__snapshots__/smoke-rbac.test.ts.snap b/packages/cubejs-testing/test/__snapshots__/smoke-rbac.test.ts.snap index b4a7ce5537530..645cd46bdd9ae 100644 --- a/packages/cubejs-testing/test/__snapshots__/smoke-rbac.test.ts.snap +++ b/packages/cubejs-testing/test/__snapshots__/smoke-rbac.test.ts.snap @@ -770,3 +770,153 @@ Array [ `; exports[`Cube RBAC Engine RBAC via SQL API manager SELECT * from line_items: line_items_manager 1`] = `Array []`; + +exports[`Cube RBAC Engine RBAC via SQL changing users Switching user should allow more members to be visible: line_items 1`] = ` +Array [ + Object { + "__cubeJoinField": null, + "__user": null, + "count": "1", + "created_at": 2018-10-23T07:54:39.000Z, + "price": 267, + "quantity": 2, + }, + Object { + "__cubeJoinField": null, + "__user": null, + "count": "1", + "created_at": 2018-01-01T13:50:20.000Z, + "price": 263, + "quantity": 7, + }, + Object { + "__cubeJoinField": null, + "__user": null, + "count": "1", + "created_at": 2017-05-13T21:23:08.000Z, + "price": 180, + "quantity": 8, + }, + Object { + "__cubeJoinField": null, + "__user": null, + "count": "1", + "created_at": 2018-04-10T22:51:15.000Z, + "price": 169, + "quantity": 6, + }, + Object { + "__cubeJoinField": null, + "__user": null, + "count": "1", + "created_at": 2017-07-16T15:00:34.000Z, + "price": 156, + "quantity": 1, + }, + Object { + "__cubeJoinField": null, + "__user": null, + "count": "1", + "created_at": 2019-05-23T04:25:27.000Z, + "price": 36, + "quantity": 5, + }, + Object { + "__cubeJoinField": null, + "__user": null, + "count": "1", + "created_at": 2018-09-29T20:29:30.000Z, + "price": 245, + "quantity": 4, + }, + Object { + "__cubeJoinField": null, + "__user": null, + "count": "1", + "created_at": 2019-04-17T03:32:54.000Z, + "price": 232, + "quantity": 8, + }, + Object { + "__cubeJoinField": null, + "__user": null, + "count": "1", + "created_at": 2019-11-15T18:22:17.000Z, + "price": 63, + "quantity": 8, + }, + Object { + "__cubeJoinField": null, + "__user": null, + "count": "1", + "created_at": 2019-12-16T08:09:36.000Z, + "price": 68, + "quantity": 6, + }, +] +`; + +exports[`Cube RBAC Engine RBAC via SQL changing users Switching user should allow more members to be visible: line_items_default 1`] = ` +Array [ + Object { + "__cubeJoinField": null, + "__user": null, + "created_at": 2018-10-23T07:54:39.000Z, + "quantity": 2, + }, + Object { + "__cubeJoinField": null, + "__user": null, + "created_at": 2018-01-01T13:50:20.000Z, + "quantity": 7, + }, + Object { + "__cubeJoinField": null, + "__user": null, + "created_at": 2017-05-13T21:23:08.000Z, + "quantity": 8, + }, + Object { + "__cubeJoinField": null, + "__user": null, + "created_at": 2018-04-10T22:51:15.000Z, + "quantity": 6, + }, + Object { + "__cubeJoinField": null, + "__user": null, + "created_at": 2017-07-16T15:00:34.000Z, + "quantity": 1, + }, + Object { + "__cubeJoinField": null, + "__user": null, + "created_at": 2019-05-23T04:25:27.000Z, + "quantity": 5, + }, + Object { + "__cubeJoinField": null, + "__user": null, + "created_at": 2018-09-29T20:29:30.000Z, + "quantity": 4, + }, + Object { + "__cubeJoinField": null, + "__user": null, + "created_at": 2019-04-17T03:32:54.000Z, + "quantity": 8, + }, + Object { + "__cubeJoinField": null, + "__user": null, + "created_at": 2019-11-15T18:22:17.000Z, + "quantity": 8, + }, + Object { + "__cubeJoinField": null, + "__user": null, + "created_at": 2019-12-16T08:09:36.000Z, + "quantity": 6, + }, +] +`; diff --git a/packages/cubejs-testing/test/driver-postgres.test.ts b/packages/cubejs-testing/test/driver-postgres.test.ts index 01704791f1dbf..38f85585d494b 100644 --- a/packages/cubejs-testing/test/driver-postgres.test.ts +++ b/packages/cubejs-testing/test/driver-postgres.test.ts @@ -1,4 +1,4 @@ -import { mainTestSet, preAggsTestSet, productionTestSet } from './driverTests/testSets'; +import { mainTestSet, multiQueryTestSet, preAggsTestSet, productionTestSet } from './driverTests/testSets'; import { executeTestSuite } from './driver-test-suite'; executeTestSuite({ @@ -6,6 +6,11 @@ executeTestSuite({ tests: mainTestSet, }); +executeTestSuite({ + type: 'postgres', + tests: multiQueryTestSet, +}); + executeTestSuite({ type: 'postgres', tests: mainTestSet, diff --git a/packages/cubejs-testing/test/driver-test-suite.ts b/packages/cubejs-testing/test/driver-test-suite.ts index 765414670fd5d..3a923b893b04e 100644 --- a/packages/cubejs-testing/test/driver-test-suite.ts +++ b/packages/cubejs-testing/test/driver-test-suite.ts @@ -68,7 +68,7 @@ export function executeTestSuite({ type, tests, config = {} }: TestSuite) { for (const t of tests) { const jsonConfig = JSON.stringify(overridedConfig); const testNameWithHash = `${t.name}_${jsonConfig}`; - + if (t.type === 'basic') { // eslint-disable-next-line no-loop-func const cbFn = async () => { @@ -83,6 +83,26 @@ export function executeTestSuite({ type, tests, config = {} }: TestSuite) { } }; + if (t.skip) { + test.skip(testNameWithHash, cbFn); + } else { + test(testNameWithHash, cbFn); + } + } else if (t.type === 'multi') { + // eslint-disable-next-line no-loop-func + const cbFn = async () => { + const response = await client.load(t.query); + + const resultSets = response.decompose(); + expect(resultSets).toMatchSnapshot('query'); + + if (t.expectArray) { + for (const expectFn of t.expectArray) { + expectFn(response); + } + } + }; + if (t.skip) { test.skip(testNameWithHash, cbFn); } else { diff --git a/packages/cubejs-testing/test/driverTests/driverTest.ts b/packages/cubejs-testing/test/driverTests/driverTest.ts index 9272164c90d0f..b4cecec6728b9 100644 --- a/packages/cubejs-testing/test/driverTests/driverTest.ts +++ b/packages/cubejs-testing/test/driverTests/driverTest.ts @@ -7,7 +7,7 @@ export type TestType = 'basic' | 'withError' | 'testFn'; type DriverTestArg = { name: string; - query: Query; + query: Query | Query[]; expectArray?: ((response: ResultSet>) => any)[]; schemas: Schemas; skip?: boolean; @@ -23,7 +23,7 @@ type DriverTestWithErrorArg = { export type DriverTestBasic = { name: string, - query: Query, + query: Query | Query[], expectArray?: ((response: ResultSet>) => any)[] schemas: Schemas, skip?: boolean; @@ -32,13 +32,22 @@ export type DriverTestBasic = { export type DriverTestWithError = { name: string; - query: Query; + query: Query | Query[]; expectArray?: ((e: Error) => any)[]; schemas: Schemas; skip?: boolean; type: 'withError'; }; +export type DriverTestMulti = { + name: string, + query: Query | Query[], + expectArray?: ((response: ResultSet>) => any)[] + schemas: Schemas, + skip?: boolean; + type: 'multi'; +}; + type DriverTestFnArg = { name: string; schemas: Schemas, @@ -50,7 +59,7 @@ export type DriverTestFn = DriverTestFnArg & { type: 'testFn'; }; -export type DriverTest = DriverTestBasic | DriverTestWithError | DriverTestFn; +export type DriverTest = DriverTestBasic | DriverTestWithError | DriverTestFn | DriverTestMulti; export function driverTest( { name, query, expectArray = [], skip, schemas }: DriverTestArg @@ -70,6 +79,12 @@ export function driverTestFn( return { name, testFn, schemas, skip, type: 'testFn' }; } +export function driverTestMulti( + { name, query, expectArray = [], skip, schemas }: DriverTestArg +): DriverTestMulti { + return { name, query, expectArray, schemas, skip, type: 'multi' }; +} + export function testSet(tests: DriverTest[]) { const uniqTests = uniqBy((t) => t.name, tests); return uniqTests; diff --git a/packages/cubejs-testing/test/driverTests/testSets.ts b/packages/cubejs-testing/test/driverTests/testSets.ts index 9b73e0a12b555..2e533ac031a6e 100644 --- a/packages/cubejs-testing/test/driverTests/testSets.ts +++ b/packages/cubejs-testing/test/driverTests/testSets.ts @@ -62,6 +62,7 @@ import { hiddenCube, viewMetaExposed, preAggsCustomersRunningTotal, + queryingECommerceCompareDateRangesByCustomerOverProductNameByMonth, } from './tests'; import { testSet } from './driverTest'; @@ -156,3 +157,7 @@ export const productionTestSet = testSet([ hiddenMember, hiddenCube, ]); + +export const multiQueryTestSet = testSet([ + queryingECommerceCompareDateRangesByCustomerOverProductNameByMonth, +]); diff --git a/packages/cubejs-testing/test/driverTests/tests.ts b/packages/cubejs-testing/test/driverTests/tests.ts index 7cb3d0e52bb9e..6e8adfddbd0ee 100644 --- a/packages/cubejs-testing/test/driverTests/tests.ts +++ b/packages/cubejs-testing/test/driverTests/tests.ts @@ -1,6 +1,6 @@ // eslint-disable-next-line import/no-extraneous-dependencies import { expect } from '@jest/globals'; -import { driverTest, driverTestFn, driverTestWithError } from './driverTest'; +import { driverTest, driverTestFn, driverTestMulti, driverTestWithError } from './driverTest'; const commonSchemas = [ 'CAST.js', @@ -189,7 +189,7 @@ export const filteringCustomersCubeThird = driverTest( }, schemas: commonSchemas, } - + ); export const filteringCustomersEndsWithFilterFirst = driverTest({ @@ -1296,6 +1296,31 @@ export const preAggsCustomersRunningTotal = driverTest({ schemas: commonSchemas }); +export const queryingECommerceCompareDateRangesByCustomerOverProductNameByMonth = driverTestMulti({ + name: 'querying ECommerce: compare DateRanges by customer over productName by month', + query: [{ + timeDimensions: [{ + dimension: 'ECommerce.orderDate', + granularity: 'month', + compareDateRange: [ + ['2023-01-01', '2024-01-01'], + ['2024-01-01', '2025-01-01'] + ] + }], + dimensions: [ + 'ECommerce.productName' + ], + measures: [ + 'ECommerce.count' + ], + order: { + 'ECommerce.orderDate': 'desc', + 'ECommerce.productName': 'asc', + }, + }], + schemas: commonSchemas +}); + export const queryingEcommerceTotalQuantifyAvgDiscountTotal = driverTestWithError({ name: 'querying ECommerce: total quantity, avg discount, total ' + 'sales, total profit by product + order + total -- noisy ' + diff --git a/packages/cubejs-testing/test/smoke-rbac.test.ts b/packages/cubejs-testing/test/smoke-rbac.test.ts index f318711932a8c..2d825ebf77609 100644 --- a/packages/cubejs-testing/test/smoke-rbac.test.ts +++ b/packages/cubejs-testing/test/smoke-rbac.test.ts @@ -198,6 +198,28 @@ describe('Cube RBAC Engine', () => { }); }); + describe('RBAC via SQL changing users', () => { + let connection: PgClient; + + beforeAll(async () => { + connection = await createPostgresClient('restricted', 'restricted_password'); + }); + + afterAll(async () => { + await connection.end(); + }, JEST_AFTER_ALL_DEFAULT_TIMEOUT); + + test('Switching user should allow more members to be visible', async () => { + const resDefault = await connection.query('SELECT * FROM line_items limit 10'); + expect(resDefault.rows).toMatchSnapshot('line_items_default'); + + await connection.query('SET USER=admin'); + + const resAdmin = await connection.query('SELECT * FROM line_items limit 10'); + expect(resAdmin.rows).toMatchSnapshot('line_items'); + }); + }); + describe('RBAC via REST API', () => { let client: CubeApi; let defaultClient: CubeApi; diff --git a/rust/cubenativeutils/Cargo.lock b/rust/cubenativeutils/Cargo.lock index a428a689435cd..9a8d0c550d2eb 100644 --- a/rust/cubenativeutils/Cargo.lock +++ b/rust/cubenativeutils/Cargo.lock @@ -426,9 +426,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" dependencies = [ "android-tzdata", "iana-time-zone", @@ -690,7 +690,7 @@ dependencies = [ "futures-util", "hashbrown 0.14.5", "indexmap 1.9.3", - "itertools", + "itertools 0.14.0", "log", "lru", "minijinja", @@ -729,7 +729,7 @@ dependencies = [ "datafusion-physical-expr", "futures", "hashbrown 0.12.3", - "itertools", + "itertools 0.10.5", "lazy_static", "log", "num_cpus", @@ -930,6 +930,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -1120,6 +1126,17 @@ dependencies = [ "allocator-api2", ] +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + [[package]] name = "heck" version = "0.3.3" @@ -1373,6 +1390,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -1513,11 +1539,11 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lru" -version = "0.12.3" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" +checksum = "227748d55f2f0ab4735d87fd623798cb6b664512fe979705f829c9f81c934465" dependencies = [ - "hashbrown 0.14.5", + "hashbrown 0.15.2", ] [[package]] diff --git a/rust/cubeorchestrator/Cargo.lock b/rust/cubeorchestrator/Cargo.lock index f4a1156cfff88..35a40dd365fed 100644 --- a/rust/cubeorchestrator/Cargo.lock +++ b/rust/cubeorchestrator/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -341,18 +341,18 @@ checksum = "cd0b0ec5f1c1ca621c432a25813d8d60c88abe6d3e08a3eb9cf37d97a0fe3d73" [[package]] name = "serde" -version = "1.0.215" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.215" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", diff --git a/rust/cubeorchestrator/Cargo.toml b/rust/cubeorchestrator/Cargo.toml index 0605d4608ae08..140932619d253 100644 --- a/rust/cubeorchestrator/Cargo.toml +++ b/rust/cubeorchestrator/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] chrono = { version = "0.4.31", features = ["serde"] } cubeshared = { path = "../cubeshared" } -serde = { version = "1.0.215", features = ["derive"] } +serde = { version = "1.0.217", features = ["derive"] } serde_json = "1.0.133" anyhow = "1.0" itertools = "0.13.0" diff --git a/rust/cubeorchestrator/src/transport.rs b/rust/cubeorchestrator/src/transport.rs index 5bcd358a28a40..b45ea96c4dec5 100644 --- a/rust/cubeorchestrator/src/transport.rs +++ b/rust/cubeorchestrator/src/transport.rs @@ -82,6 +82,13 @@ pub struct GroupingSet { pub sub_id: Option, } +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(untagged)] +pub enum CompareDateRangeType { + Single(Vec), + Multi(Vec>), +} + // We can do nothing with JS functions here, // but to keep DTOs in sync with reality, let's keep it. pub type JsFunction = String; @@ -120,7 +127,7 @@ pub struct ParsedMemberExpression { pub struct QueryTimeDimension { pub dimension: String, pub date_range: Option>, - pub compare_date_range: Option>, + pub compare_date_range: Option, pub granularity: Option, } @@ -161,6 +168,8 @@ pub struct ConfigItem { pub drill_members_grouped: Option, #[serde(skip_serializing_if = "Option::is_none")] pub granularities: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub granularity: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/rust/cubesql/Cargo.lock b/rust/cubesql/Cargo.lock index ed5cf710e5d61..1fd85f30c9bc4 100644 --- a/rust/cubesql/Cargo.lock +++ b/rust/cubesql/Cargo.lock @@ -398,16 +398,16 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.31" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "wasm-bindgen", - "windows-targets 0.48.1", + "windows-targets 0.52.6", ] [[package]] @@ -579,7 +579,7 @@ dependencies = [ "ciborium", "clap", "criterion-plot", - "itertools", + "itertools 0.10.3", "lazy_static", "num-traits", "oorandom", @@ -600,7 +600,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" dependencies = [ "cast", - "itertools", + "itertools 0.10.3", ] [[package]] @@ -766,7 +766,7 @@ dependencies = [ "hashbrown 0.14.3", "indexmap 1.9.3", "insta", - "itertools", + "itertools 0.14.0", "log", "lru", "minijinja", @@ -853,7 +853,7 @@ dependencies = [ "datafusion-physical-expr", "futures", "hashbrown 0.12.1", - "itertools", + "itertools 0.10.3", "lazy_static", "log", "num_cpus", @@ -1058,6 +1058,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -1261,6 +1267,17 @@ dependencies = [ "allocator-api2", ] +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + [[package]] name = "heck" version = "0.3.3" @@ -1522,6 +1539,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "0.4.8" @@ -1667,11 +1693,11 @@ checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "lru" -version = "0.12.1" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2994eeba8ed550fd9b47a0b38f0242bc3344e496483c6180b69139cc2fa5d1d7" +checksum = "227748d55f2f0ab4735d87fd623798cb6b664512fe979705f829c9f81c934465" dependencies = [ - "hashbrown 0.14.3", + "hashbrown 0.15.2", ] [[package]] diff --git a/rust/cubesql/cubesql/Cargo.toml b/rust/cubesql/cubesql/Cargo.toml index 09fd88de56436..e8da544e6370a 100644 --- a/rust/cubesql/cubesql/Cargo.toml +++ b/rust/cubesql/cubesql/Cargo.toml @@ -10,7 +10,10 @@ homepage = "https://cube.dev" [dependencies] arc-swap = "1" -datafusion = { git = 'https://github.com/cube-js/arrow-datafusion.git', rev = "dcf3e4aa26fd112043ef26fa4a78db5dbd443c86", default-features = false, features = ["regex_expressions", "unicode_expressions"] } +datafusion = { git = 'https://github.com/cube-js/arrow-datafusion.git', rev = "dcf3e4aa26fd112043ef26fa4a78db5dbd443c86", default-features = false, features = [ + "regex_expressions", + "unicode_expressions", +] } thiserror = "2" cubeclient = { path = "../cubeclient" } pg-srv = { path = "../pg-srv" } @@ -18,14 +21,14 @@ sqlparser = { git = 'https://github.com/cube-js/sqlparser-rs.git', rev = "6a54d2 base64 = "0.13.0" tokio = { version = "^1.35", features = ["full", "rt", "tracing"] } serde = { version = "^1.0", features = ["derive"] } -itertools = "0.10.2" +itertools = "0.14.0" serde_json = "^1.0" bytes = "1.2" futures = "0.3.23" rand = "0.8.3" hashbrown = "0.14.3" log = "0.4.21" -rust_decimal = { version = "1.25", features = ["c-repr", "db-postgres"]} +rust_decimal = { version = "1.25", features = ["c-repr", "db-postgres"] } postgres-types = "0.2.3" # Locked, because starting from 1.15 this crate switch from chrono to time # which panic with Could not determine the UTC offset on this system. @@ -36,12 +39,14 @@ async-trait = "0.1.36" regex = "1.5" uuid = { version = "1", features = ["serde", "v4"] } bincode = "1.3.1" -chrono = "0.4.31" +chrono = "0.4.39" chrono-tz = "0.6" -tokio-util = { version = "0.7", features=["compat"] } +tokio-util = { version = "0.7", features = ["compat"] } comfy-table = "7.1.0" bitflags = "1.3.2" -egg = { rev = "952f8c2a1033e5da097d23c523b0d8e392eb532b", git = "https://github.com/cube-js/egg.git", features = ["serde-1"] } +egg = { rev = "952f8c2a1033e5da097d23c523b0d8e392eb532b", git = "https://github.com/cube-js/egg.git", features = [ + "serde-1", +] } paste = "1.0.6" tracing = { version = "0.1.40", features = ["async-await"] } async-stream = "0.3.3" @@ -50,7 +55,7 @@ futures-util = "0.3.23" sha1_smol = "1.0.0" tera = { version = "1", default-features = false } minijinja = { version = "1", features = ["json", "loader"] } -lru = "0.12.1" +lru = "0.13.0" sha2 = "0.10.8" bigdecimal = "0.4.2" indexmap = "1.9.3" @@ -60,7 +65,10 @@ indexmap = "1.9.3" pretty_assertions = "1.0.0" insta = "1.12" portpicker = "0.1.1" -tokio-postgres = { version = "0.7.7", features = ["with-chrono-0_4", "runtime"] } +tokio-postgres = { version = "0.7.7", features = [ + "with-chrono-0_4", + "runtime", +] } rust_decimal = { version = "1.23", features = ["db-tokio-postgres"] } pg_interval = "0.4.1" criterion = { version = "0.4.0", features = ["html_reports"] } diff --git a/rust/cubesql/cubesql/src/compile/engine/df/scan.rs b/rust/cubesql/cubesql/src/compile/engine/df/scan.rs index afd410898b25c..bd88f6716f77a 100644 --- a/rust/cubesql/cubesql/src/compile/engine/df/scan.rs +++ b/rust/cubesql/cubesql/src/compile/engine/df/scan.rs @@ -1129,7 +1129,7 @@ pub fn transform_response( // TODO switch parsing to microseconds if timestamp.and_utc().timestamp_millis() > (((1i64) << 62) / 1_000_000) { builder.append_null()?; - } else if let Some(nanos) = timestamp.timestamp_nanos_opt() { + } else if let Some(nanos) = timestamp.and_utc().timestamp_nanos_opt() { builder.append_value(nanos)?; } else { log::error!( diff --git a/rust/cubesql/cubesql/src/compile/engine/udf/common.rs b/rust/cubesql/cubesql/src/compile/engine/udf/common.rs index 20f57f81379bb..d957ec36235d2 100644 --- a/rust/cubesql/cubesql/src/compile/engine/udf/common.rs +++ b/rust/cubesql/cubesql/src/compile/engine/udf/common.rs @@ -1669,7 +1669,8 @@ pub fn create_to_char_udf() -> ScalarUDF { let secs = duration.num_seconds(); let nanosecs = duration.num_nanoseconds().unwrap_or(0) - secs * 1_000_000_000; - let timestamp = NaiveDateTime::from_timestamp_opt(secs, nanosecs as u32) + let timestamp = ::chrono::DateTime::from_timestamp(secs, nanosecs as u32) + .map(|dt| dt.naive_utc()) .unwrap_or_else(|| panic!("Invalid secs {} nanosecs {}", secs, nanosecs)); // chrono's strftime is missing quarter format, as such a workaround is required @@ -2316,7 +2317,8 @@ macro_rules! generate_series_udtf { macro_rules! generate_series_helper_date32 { ($CURRENT:ident, $STEP:ident, $PRIMITIVE_TYPE: ident) => { - let current_dt = NaiveDateTime::from_timestamp_opt(($CURRENT as i64) * 86400, 0) + let current_dt = ::chrono::DateTime::from_timestamp(($CURRENT as i64) * 86400, 0) + .map(|dt| dt.naive_utc()) .ok_or_else(|| { DataFusionError::Execution(format!( "Cannot convert date to NaiveDateTime: {}", @@ -2324,16 +2326,17 @@ macro_rules! generate_series_helper_date32 { )) })?; let res = date_addsub_month_day_nano(current_dt, $STEP, true)?; - $CURRENT = (res.timestamp() / 86400) as $PRIMITIVE_TYPE; + $CURRENT = (res.and_utc().timestamp() / 86400) as $PRIMITIVE_TYPE; }; } macro_rules! generate_series_helper_timestamp { ($CURRENT:ident, $STEP:ident, $PRIMITIVE_TYPE: ident) => { - let current_dt = NaiveDateTime::from_timestamp_opt( + let current_dt = ::chrono::DateTime::from_timestamp( ($CURRENT as i64) / 1_000_000_000, ($CURRENT % 1_000_000_000) as u32, ) + .map(|dt| dt.naive_utc()) .ok_or_else(|| { DataFusionError::Execution(format!( "Cannot convert timestamp to NaiveDateTime: {}", diff --git a/rust/cubesql/cubesql/src/compile/rewrite/converter.rs b/rust/cubesql/cubesql/src/compile/rewrite/converter.rs index dc65029915143..d717f23f249f4 100644 --- a/rust/cubesql/cubesql/src/compile/rewrite/converter.rs +++ b/rust/cubesql/cubesql/src/compile/rewrite/converter.rs @@ -1551,14 +1551,13 @@ impl LanguageToLogicalPlanConverter { match_data_node!(node_by_id, measure_params[0], MeasureName); let expr = self.to_expr(measure_params[1])?; query_measures.push(measure.to_string()); - let data_type = self - .cube_context - .meta - .find_df_data_type(measure.to_string()) - .ok_or(CubeError::internal(format!( - "Can't find measure '{}'", - measure - )))?; + let data_type = + self.cube_context.meta.find_df_data_type(&measure).ok_or( + CubeError::internal(format!( + "Can't find measure '{}'", + measure + )), + )?; fields.push(( DFField::new( expr_relation(&expr), @@ -1608,14 +1607,13 @@ impl LanguageToLogicalPlanConverter { LogicalPlanLanguage::Dimension(params) => { let dimension = match_data_node!(node_by_id, params[0], DimensionName); let expr = self.to_expr(params[1])?; - let data_type = self - .cube_context - .meta - .find_df_data_type(dimension.to_string()) - .ok_or(CubeError::internal(format!( - "Can't find dimension '{}'", - dimension - )))?; + let data_type = + self.cube_context.meta.find_df_data_type(&dimension).ok_or( + CubeError::internal(format!( + "Can't find dimension '{}'", + dimension + )), + )?; query_dimensions.push(dimension.to_string()); fields.push(( DFField::new( @@ -1705,7 +1703,7 @@ impl LanguageToLogicalPlanConverter { if self .cube_context .meta - .is_synthetic_field(column.member_name().to_string()) + .is_synthetic_field(column.member_name()) { fields.push(( DFField::new( diff --git a/rust/cubesql/cubesql/src/compile/rewrite/cost.rs b/rust/cubesql/cubesql/src/compile/rewrite/cost.rs index b012588c370c0..5972d72619c1c 100644 --- a/rust/cubesql/cubesql/src/compile/rewrite/cost.rs +++ b/rust/cubesql/cubesql/src/compile/rewrite/cost.rs @@ -143,7 +143,7 @@ impl BestCubePlan { let time_dimensions_used_as_dimensions = match enode { LogicalPlanLanguage::DimensionName(DimensionName(name)) => { - if let Some(dimension) = self.meta_context.find_dimension_with_name(name.clone()) { + if let Some(dimension) = self.meta_context.find_dimension_with_name(name) { if dimension.is_time() { 1 } else { diff --git a/rust/cubesql/cubesql/src/compile/rewrite/rules/filters.rs b/rust/cubesql/cubesql/src/compile/rewrite/rules/filters.rs index 39774ce7458ca..fc8f6daec4f5a 100644 --- a/rust/cubesql/cubesql/src/compile/rewrite/rules/filters.rs +++ b/rust/cubesql/cubesql/src/compile/rewrite/rules/filters.rs @@ -34,7 +34,7 @@ use chrono::{ Numeric::{Day, Hour, Minute, Month, Second, Year}, Pad::Zero, }, - Datelike, Days, Duration, Months, NaiveDate, NaiveDateTime, Timelike, Weekday, + DateTime, Datelike, Days, Duration, Months, NaiveDate, NaiveDateTime, Timelike, Weekday, }; use cubeclient::models::V1CubeMeta; use datafusion::{ @@ -3730,15 +3730,15 @@ impl FilterRules { } } - fn filter_member_name( + fn filter_member_name<'meta>( egraph: &mut CubeEGraph, subst: &Subst, - meta_context: &Arc, + meta_context: &'meta MetaContext, alias_to_cube_var: Var, column_var: Var, members_var: Var, aliases: &Vec<(String, String)>, - ) -> Option<(String, V1CubeMeta)> { + ) -> Option<(String, &'meta V1CubeMeta)> { Self::filter_member_name_with_granularity( egraph, subst, @@ -3751,15 +3751,15 @@ impl FilterRules { .map(|(name, _, meta)| (name, meta)) } - fn filter_member_name_with_granularity( + fn filter_member_name_with_granularity<'meta>( egraph: &mut CubeEGraph, subst: &Subst, - meta_context: &Arc, + meta_context: &'meta MetaContext, alias_to_cube_var: Var, column_var: Var, members_var: Var, aliases: &Vec<(String, String)>, - ) -> Option<(String, Option, V1CubeMeta)> { + ) -> Option<(String, Option, &'meta V1CubeMeta)> { let alias_to_cubes: Vec<_> = var_iter!(egraph[subst[alias_to_cube_var]], FilterReplacerAliasToCube) .cloned() @@ -4836,7 +4836,7 @@ impl FilterRules { }; let ts_seconds = *ts / 1_000_000_000; let ts_nanos = (*ts % 1_000_000_000) as u32; - let dt = NaiveDateTime::from_timestamp_opt(ts_seconds, ts_nanos).map(|dt| Some(dt)); + let dt = DateTime::from_timestamp(ts_seconds, ts_nanos).map(|dt| Some(dt.naive_utc())); return dt; }; diff --git a/rust/cubesql/cubesql/src/compile/rewrite/rules/members.rs b/rust/cubesql/cubesql/src/compile/rewrite/rules/members.rs index a9452ce69be37..99b325901cede 100644 --- a/rust/cubesql/cubesql/src/compile/rewrite/rules/members.rs +++ b/rust/cubesql/cubesql/src/compile/rewrite/rules/members.rs @@ -2166,7 +2166,7 @@ impl MemberRules { .cloned() { if let Some(measure) = - meta_context.find_measure_with_name(measure_name.to_string()) + meta_context.find_measure_with_name(&measure_name) { let measure_cube_name = measure_name.split(".").next().unwrap(); if let Some(((_, cube_alias), _)) = alias_to_cube @@ -2205,7 +2205,7 @@ impl MemberRules { } if let Some(dimension) = - meta_context.find_dimension_with_name(measure_name.to_string()) + meta_context.find_dimension_with_name(&measure_name) { let alias_to_cube = alias_to_cube.clone(); subst.insert( @@ -2309,7 +2309,7 @@ impl MemberRules { call_agg_type, alias, measure_out_var, - cube_alias, + cube_alias.to_string(), subst[aggr_expr_var], alias_to_cube, disable_strict_agg_type_match, diff --git a/rust/cubesql/cubesql/src/compile/rewrite/rules/old_split.rs b/rust/cubesql/cubesql/src/compile/rewrite/rules/old_split.rs index b79d5325f8467..7cb019f19f793 100644 --- a/rust/cubesql/cubesql/src/compile/rewrite/rules/old_split.rs +++ b/rust/cubesql/cubesql/src/compile/rewrite/rules/old_split.rs @@ -4922,7 +4922,7 @@ impl OldSplitRules { // TODO unwrap let name = expr.name(&DFSchema::empty()).unwrap(); let column1 = Column { - relation: Some(alias), + relation: Some(alias.to_string()), name: name.to_string(), }; let alias = egraph.add(LogicalPlanLanguage::ColumnExprColumn( diff --git a/rust/cubesql/cubesql/src/compile/rewrite/rules/utils.rs b/rust/cubesql/cubesql/src/compile/rewrite/rules/utils.rs index a941bc7cdc6ca..66eed50dc226d 100644 --- a/rust/cubesql/cubesql/src/compile/rewrite/rules/utils.rs +++ b/rust/cubesql/cubesql/src/compile/rewrite/rules/utils.rs @@ -4,7 +4,7 @@ use std::{ sync::Arc, }; -use chrono::{Datelike, NaiveDateTime, Timelike}; +use chrono::{DateTime, Datelike, Timelike, Utc}; use datafusion::{ arrow::datatypes::{ArrowPrimitiveType, IntervalDayTimeType, IntervalMonthDayNanoType}, error::DataFusionError, @@ -457,9 +457,9 @@ pub fn is_literal_date_trunced(ns: i64, granularity: &str) -> Option { return Some(false); } let seconds = ns / ns_in_seconds; - let dt = NaiveDateTime::from_timestamp_opt(seconds, 0)?; + let dt = DateTime::from_timestamp(seconds, 0)?; - let is_minute_trunced = |dt: NaiveDateTime| dt.second() == 0; + let is_minute_trunced = |dt: DateTime| dt.second() == 0; let is_hour_trunced = |dt| is_minute_trunced(dt) && dt.minute() == 0; let is_day_trunced = |dt| is_hour_trunced(dt) && dt.hour() == 0; let is_week_trunced = |dt| is_day_trunced(dt) && dt.weekday().num_days_from_monday() == 0; diff --git a/rust/cubesql/cubesql/src/compile/rewrite/rules/wrapper/aggregate.rs b/rust/cubesql/cubesql/src/compile/rewrite/rules/wrapper/aggregate.rs index d1cff7bfc5844..63155c173f3db 100644 --- a/rust/cubesql/cubesql/src/compile/rewrite/rules/wrapper/aggregate.rs +++ b/rust/cubesql/cubesql/src/compile/rewrite/rules/wrapper/aggregate.rs @@ -878,9 +878,7 @@ impl WrapperRules { &column.name, ) { - if let Some(measure) = - meta.find_measure_with_name(member.to_string()) - { + if let Some(measure) = meta.find_measure_with_name(member) { if call_agg_type.is_none() || measure.is_same_agg_type( call_agg_type.as_ref().unwrap(), diff --git a/rust/cubesql/cubesql/src/compile/rewrite/rules/wrapper/column.rs b/rust/cubesql/cubesql/src/compile/rewrite/rules/wrapper/column.rs index a3ce49c8b9c28..1b19e8da99e0c 100644 --- a/rust/cubesql/cubesql/src/compile/rewrite/rules/wrapper/column.rs +++ b/rust/cubesql/cubesql/src/compile/rewrite/rules/wrapper/column.rs @@ -214,7 +214,7 @@ impl WrapperRules { .data .find_member_by_alias(&column.name) { - if let Some(measure) = meta.find_measure_with_name(member.to_string()) { + if let Some(measure) = meta.find_measure_with_name(member) { if measure.agg_type != Some("number".to_string()) { return true; } diff --git a/rust/cubesql/cubesql/src/transport/ctx.rs b/rust/cubesql/cubesql/src/transport/ctx.rs index 321a7c349a9a1..997928730cde6 100644 --- a/rust/cubesql/cubesql/src/transport/ctx.rs +++ b/rust/cubesql/cubesql/src/transport/ctx.rs @@ -79,63 +79,53 @@ impl MetaContext { &self, alias_to_cube: &Vec<(String, String)>, ) -> Option> { - let data_sources = alias_to_cube + let data_source = alias_to_cube .iter() .map(|(_, c)| self.cube_to_data_source.get(c)) - .unique() - .collect::>>()?; - if data_sources.len() != 1 { - return None; - } - self.data_source_to_sql_generator - .get(data_sources[0].as_str()) - .cloned() - } + .all_equal_value(); - pub fn find_cube_with_name(&self, name: &str) -> Option { - for cube in self.cubes.iter() { - if cube.name.eq(&name) { - return Some(cube.clone()); - } - } + // Don't care for non-equal data sources, nor for missing cube_to_data_source keys + let data_source = data_source.ok()??; - None + self.data_source_to_sql_generator.get(data_source).cloned() } - pub fn find_cube_by_column( - &self, - alias_to_cube: &Vec<(String, String)>, + pub fn find_cube_with_name(&self, name: &str) -> Option<&CubeMeta> { + self.cubes.iter().find(|&cube| cube.name == name) + } + + pub fn find_cube_by_column<'meta, 'alias>( + &'meta self, + alias_to_cube: &'alias Vec<(String, String)>, column: &Column, - ) -> Option<(String, CubeMeta)> { + ) -> Option<(&'alias str, &'meta CubeMeta)> { (if let Some(rel) = column.relation.as_ref() { alias_to_cube.iter().find(|(a, _)| a == rel) } else { alias_to_cube.iter().find(|(_, c)| { if let Some(cube) = self.find_cube_with_name(c) { + // TODO replace cube.contains_member(&cube.member_name(...)) with searching by prepared column names cube.contains_member(&cube.member_name(&column.name)) } else { false } }) }) - .and_then(|(a, c)| { - self.find_cube_with_name(c) - .map(|cube| (a.to_string(), cube)) - }) + .and_then(|(a, c)| self.find_cube_with_name(c).map(|cube| (a.as_str(), cube))) } - pub fn find_cube_by_column_for_replacer( + pub fn find_cube_by_column_for_replacer<'alias>( &self, - alias_to_cube: &Vec<((String, String), String)>, + alias_to_cube: &'alias Vec<((String, String), String)>, column: &Column, - ) -> Vec<((String, String), CubeMeta)> { + ) -> Vec<((&'alias str, &'alias str), &CubeMeta)> { if let Some(rel) = column.relation.as_ref() { alias_to_cube .iter() .filter_map(|((old, new), c)| { if old == rel { self.find_cube_with_name(c) - .map(|cube| ((old.to_string(), new.to_string()), cube)) + .map(|cube| ((old.as_str(), new.as_str()), cube)) } else { None } @@ -146,8 +136,9 @@ impl MetaContext { .iter() .filter_map(|((old, new), c)| { if let Some(cube) = self.find_cube_with_name(c) { + // TODO replace cube.contains_member(&cube.member_name(...)) with searching by prepared column names if cube.contains_member(&cube.member_name(&column.name)) { - return Some(((old.to_string(), new.to_string()), cube)); + return Some(((old.as_str(), new.as_str()), cube)); } } @@ -157,33 +148,33 @@ impl MetaContext { } } - pub fn find_measure_with_name(&self, name: String) -> Option { - let cube_and_member_name = name.split(".").collect::>(); - if let Some(cube) = self.find_cube_with_name(cube_and_member_name[0]) { - cube.lookup_measure(cube_and_member_name[1]).cloned() - } else { - None - } + pub fn find_measure_with_name(&self, name: &str) -> Option<&CubeMetaMeasure> { + let mut cube_and_member_name = name.split("."); + let cube_name = cube_and_member_name.next()?; + let member_name = cube_and_member_name.next()?; + let cube = self.find_cube_with_name(cube_name)?; + cube.lookup_measure(member_name) } - pub fn find_dimension_with_name(&self, name: String) -> Option { - let cube_and_member_name = name.split(".").collect::>(); - if let Some(cube) = self.find_cube_with_name(cube_and_member_name[0]) { - cube.lookup_dimension(cube_and_member_name[1]).cloned() - } else { - None - } + pub fn find_dimension_with_name(&self, name: &str) -> Option<&CubeMetaDimension> { + let mut cube_and_member_name = name.split("."); + let cube_name = cube_and_member_name.next()?; + let member_name = cube_and_member_name.next()?; + let cube = self.find_cube_with_name(cube_name)?; + cube.lookup_dimension(member_name) } - pub fn is_synthetic_field(&self, name: String) -> bool { - let cube_and_member_name = name.split(".").collect::>(); - if cube_and_member_name.len() == 1 - && MetaContext::is_synthetic_field_name(cube_and_member_name[0]) - { - return true; - } - if let Some(_) = self.find_cube_with_name(cube_and_member_name[0]) { - MetaContext::is_synthetic_field_name(cube_and_member_name[1]) + pub fn is_synthetic_field(&self, name: &str) -> bool { + let mut cube_and_member_name = name.split("."); + let Some(cube_name) = cube_and_member_name.next() else { + return false; + }; + let Some(member_name) = cube_and_member_name.next() else { + return MetaContext::is_synthetic_field_name(cube_name); + }; + + if self.find_cube_with_name(cube_name).is_some() { + MetaContext::is_synthetic_field_name(member_name) } else { false } @@ -193,22 +184,24 @@ impl MetaContext { field_name == "__user" || field_name == "__cubeJoinField" } - pub fn find_df_data_type(&self, member_name: String) -> Option { - self.find_cube_with_name(member_name.split(".").next()?)? - .df_data_type(member_name.as_str()) + pub fn find_df_data_type(&self, member_name: &str) -> Option { + let (cube_name, _) = member_name.split_once(".")?; + + self.find_cube_with_name(cube_name)? + .df_data_type(member_name) } - pub fn find_cube_table_with_oid(&self, oid: u32) -> Option { - self.tables.iter().find(|table| table.oid == oid).cloned() + pub fn find_cube_table_with_oid(&self, oid: u32) -> Option<&CubeMetaTable> { + self.tables.iter().find(|table| table.oid == oid) } - pub fn find_cube_table_with_name(&self, name: String) -> Option { - self.tables.iter().find(|table| table.name == name).cloned() + pub fn find_cube_table_with_name(&self, name: &str) -> Option<&CubeMetaTable> { + self.tables.iter().find(|table| table.name == name) } - pub fn cube_has_join(&self, cube_name: &str, join_name: String) -> bool { + pub fn cube_has_join(&self, cube_name: &str, join_name: &str) -> bool { if let Some(cube) = self.find_cube_with_name(cube_name) { - if let Some(joins) = cube.joins { + if let Some(joins) = &cube.joins { return joins.iter().any(|j| j.name == join_name); } } @@ -262,7 +255,7 @@ mod tests { _ => panic!("wrong oid!"), } - match test_context.find_cube_table_with_name("test2".to_string()) { + match test_context.find_cube_table_with_name("test2") { Some(table) => assert_eq!(18005, table.oid), _ => panic!("wrong name!"), } diff --git a/rust/cubesqlplanner/Cargo.lock b/rust/cubesqlplanner/Cargo.lock index e983f7b7d4135..e6ceccebb3a1a 100644 --- a/rust/cubesqlplanner/Cargo.lock +++ b/rust/cubesqlplanner/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "Inflector" @@ -436,9 +436,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" dependencies = [ "android-tzdata", "iana-time-zone", @@ -722,7 +722,7 @@ dependencies = [ "futures-util", "hashbrown 0.14.5", "indexmap 1.9.3", - "itertools", + "itertools 0.14.0", "log", "lru", "minijinja", @@ -757,7 +757,7 @@ dependencies = [ "cubeclient", "cubenativeutils", "datafusion", - "itertools", + "itertools 0.10.5", "lazy_static", "minijinja", "nativebridge", @@ -783,7 +783,7 @@ dependencies = [ "datafusion-physical-expr", "futures", "hashbrown 0.12.3", - "itertools", + "itertools 0.10.5", "lazy_static", "log", "num_cpus", @@ -984,6 +984,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -1174,6 +1180,17 @@ dependencies = [ "allocator-api2", ] +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash", +] + [[package]] name = "heck" version = "0.3.3" @@ -1427,6 +1444,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -1567,11 +1593,11 @@ checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "lru" -version = "0.12.3" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" +checksum = "227748d55f2f0ab4735d87fd623798cb6b664512fe979705f829c9f81c934465" dependencies = [ - "hashbrown 0.14.5", + "hashbrown 0.15.2", ] [[package]] @@ -1691,7 +1717,7 @@ dependencies = [ "Inflector", "async-trait", "byteorder", - "itertools", + "itertools 0.10.5", "proc-macro2", "quote", "syn 1.0.109", diff --git a/rust/cubestore/package.json b/rust/cubestore/package.json index 1096bf92660ad..311443507d022 100644 --- a/rust/cubestore/package.json +++ b/rust/cubestore/package.json @@ -27,7 +27,7 @@ "author": "Cube Dev, Inc.", "license": "Apache-2.0", "devDependencies": { - "@cubejs-backend/linter": "1.2.4", + "@cubejs-backend/linter": "1.2.5", "@types/jest": "^27", "@types/node": "^12", "jest": "^27", @@ -37,7 +37,7 @@ "access": "public" }, "dependencies": { - "@cubejs-backend/shared": "1.2.4", + "@cubejs-backend/shared": "1.2.5", "@octokit/core": "^3.2.5", "source-map-support": "^0.5.19" }, diff --git a/rust/cubetranspilers/.gitignore b/rust/cubetranspilers/.gitignore new file mode 100644 index 0000000000000..2a0a960cb1ec5 --- /dev/null +++ b/rust/cubetranspilers/.gitignore @@ -0,0 +1,3 @@ +/target +/.idea +.vscode diff --git a/rust/cubetranspilers/Cargo.lock b/rust/cubetranspilers/Cargo.lock new file mode 100644 index 0000000000000..5b40feaf455e7 --- /dev/null +++ b/rust/cubetranspilers/Cargo.lock @@ -0,0 +1,2411 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + +[[package]] +name = "anyhow" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" + +[[package]] +name = "ascii" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" + +[[package]] +name = "ast_node" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91fb5864e2f5bf9fd9797b94b2dfd1554d4c3092b535008b27d7e15c86675a2f" +dependencies = [ + "proc-macro2", + "quote", + "swc_macros_common", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64-simd" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "781dd20c3aff0bd194fe7d2a977dd92f21c173891f3a03b677359e5fa457e5d5" +dependencies = [ + "simd-abstraction", +] + +[[package]] +name = "better_scoped_tls" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50fd297a11c709be8348aec039c8b91de16075d2b2bdaee1bd562c0875993664" +dependencies = [ + "scoped-tls", +] + +[[package]] +name = "bitflags" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" +dependencies = [ + "allocator-api2", +] + +[[package]] +name = "bytecheck" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50690fb3370fb9fe3550372746084c46f2ac8c9685c583d2be10eefd89d3d1a3" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "rancor", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efb7846e0cb180355c2dec69e721edafa36919850f1a9f52ffba4ebc0393cb71" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "bytes" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9" + +[[package]] +name = "camino" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo-platform" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +dependencies = [ + "camino", + "cargo-platform", + "semver 1.0.25", + "serde", + "serde_json", + "thiserror 1.0.69", +] + +[[package]] +name = "cargo_metadata" +version = "0.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8769706aad5d996120af43197bf46ef6ad0fda35216b4505f926a365a232d924" +dependencies = [ + "camino", + "cargo-platform", + "semver 1.0.25", + "serde", + "serde_json", + "thiserror 2.0.11", +] + +[[package]] +name = "castaway" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5" +dependencies = [ + "rustversion", +] + +[[package]] +name = "cc" +version = "1.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c3d1b2e905a3a7b00a6141adb0e4c0bb941d11caf55349d863942a1cc44e3c9" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "compact_str" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f" +dependencies = [ + "castaway", + "cfg-if", + "itoa", + "ryu", + "static_assertions", +] + +[[package]] +name = "console" +version = "0.15.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea3c6ecd8059b57859df5c69830340ed3c41d30e3da0c1cbed90a96ac853041b" +dependencies = [ + "encode_unicode", + "libc", + "once_cell", + "windows-sys", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "cubetranspilers" +version = "0.1.0" +dependencies = [ + "anyhow", + "indexmap", + "insta", + "regex", + "serde", + "serde_json", + "swc_core", + "swc_ecma_codegen", + "swc_ecma_parser", +] + +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "data-encoding" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "575f75dfd25738df5b91b8e43e14d44bda14637a58fae779fd2b064f8bf3e010" + +[[package]] +name = "debugid" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" +dependencies = [ + "serde", + "uuid", +] + +[[package]] +name = "derive_builder" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" +dependencies = [ + "derive_builder_core", + "syn", +] + +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + +[[package]] +name = "difference" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "encode_unicode" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "from_variant" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d7ccf961415e7aa17ef93dcb6c2441faaa8e768abe09e659b908089546f74c5" +dependencies = [ + "proc-macro2", + "swc_macros_common", + "syn", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi", + "windows-targets", +] + +[[package]] +name = "glob" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a26def229ea95a8709dad32868d975d0dd40235bd2ce82920e4a8fe692b5e0" +dependencies = [ + "hashbrown 0.14.5", + "new_debug_unreachable", + "once_cell", + "phf", + "rustc-hash 1.1.0", + "triomphe", +] + +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "if_chain" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" + +[[package]] +name = "indexmap" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" +dependencies = [ + "equivalent", + "hashbrown 0.15.2", +] + +[[package]] +name = "insta" +version = "1.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71c1b125e30d93896b365e156c33dadfffab45ee8400afcbba4752f59de08a86" +dependencies = [ + "console", + "linked-hash-map", + "once_cell", + "pin-project", + "similar", +] + +[[package]] +name = "is-macro" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57a3e447e24c22647738e4607f1df1e0ec6f72e16182c4cd199f647cdfb0e4" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.169" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" + +[[package]] +name = "linked-hash-map" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" + +[[package]] +name = "matchers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +dependencies = [ + "regex-automata 0.1.10", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "miette" +version = "7.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a955165f87b37fd1862df2a59547ac542c77ef6d17c666f619d1ad22dd89484" +dependencies = [ + "cfg-if", + "miette-derive", + "owo-colors", + "textwrap", + "thiserror 1.0.69", + "unicode-width", +] + +[[package]] +name = "miette-derive" +version = "7.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf45bf44ab49be92fd1227a3be6fc6f617f1a337c06af54981048574d8783147" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "munge" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64142d38c84badf60abf06ff9bd80ad2174306a5b11bd4706535090a30a419df" +dependencies = [ + "munge_macro", +] + +[[package]] +name = "munge_macro" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bb5c1d8184f13f7d0ccbeeca0def2f9a181bce2624302793005f5ca8aa62e5e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", + "serde", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" + +[[package]] +name = "outref" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f222829ae9293e33a9f5e9f440c6760a3d450a64affe1846486b140db81c1f4" + +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + +[[package]] +name = "owo-colors" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb37767f6569cd834a413442455e0f066d0d522de8630436e2a1761d9726ba56" + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher 1.0.1", +] + +[[package]] +name = "pin-project" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfe2e71e1471fe07709406bf725f710b02927c9c54b2b5b2ec0e8087d97c327d" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e859e6e5bd50440ab63c47e3ebabc90f26251f7c73c3d3e837b74a1cc3fa67" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pretty_assertions" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" +dependencies = [ + "diff", + "yansi", +] + +[[package]] +name = "proc-macro2" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "psm" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200b9ff220857e53e184257720a14553b2f4aa02577d2ed9842d45d4b9654810" +dependencies = [ + "cc", +] + +[[package]] +name = "ptr_meta" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9e76f66d3f9606f44e45598d155cb13ecf09f4a28199e48daf8c8fc937ea90" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca414edb151b4c8d125c12566ab0d74dc9cdba36fb80eb7b848c15f495fd32d1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "quote" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rancor" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf5f7161924b9d1cea0e4cabc97c372cea92b5f927fc13c6bca67157a0ad947" +dependencies = [ + "ptr_meta", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + +[[package]] +name = "redox_syscall" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata 0.4.9", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax 0.8.5", +] + +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "relative-path" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" + +[[package]] +name = "rend" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a35e8a6bf28cd121053a66aa2e6a2e3eaffad4a60012179f0e864aa5ffeff215" +dependencies = [ + "bytecheck", +] + +[[package]] +name = "rkyv" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e147371c75553e1e2fcdb483944a8540b8438c31426279553b9a8182a9b7b65" +dependencies = [ + "bytecheck", + "bytes", + "hashbrown 0.15.2", + "indexmap", + "munge", + "ptr_meta", + "rancor", + "rend", + "rkyv_derive", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "246b40ac189af6c675d124b802e8ef6d5246c53e17367ce9501f8f66a81abb7a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver 0.9.0", +] + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "rustversion" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" + +[[package]] +name = "ryu" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd" + +[[package]] +name = "ryu-js" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad97d4ce1560a5e27cec89519dc8300d1aa6035b099821261c651486a19e44d5" + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f79dfe2d285b0488816f30e700a7438c5a73d816b5b7d3ac72fbc48b0d185e03" +dependencies = [ + "serde", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "serde" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.138" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "simd-abstraction" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cadb29c57caadc51ff8346233b5cec1d240b68ce55cf1afc764818791876987" +dependencies = [ + "outref", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + +[[package]] +name = "similar" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" + +[[package]] +name = "siphasher" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" + +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "smartstring" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29" +dependencies = [ + "autocfg", + "static_assertions", + "version_check", +] + +[[package]] +name = "sourcemap" +version = "9.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27c4ea7042fd1a155ad95335b5d505ab00d5124ea0332a06c8390d200bb1a76a" +dependencies = [ + "base64-simd", + "bitvec", + "data-encoding", + "debugid", + "if_chain", + "rustc-hash 1.1.0", + "rustc_version", + "serde", + "serde_json", + "unicode-id-start", + "url", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "stacker" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d08feb8f695b465baed819b03c128dc23f57a694510ab1f06c77f763975685e" +dependencies = [ + "cc", + "cfg-if", + "libc", + "psm", + "windows-sys", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "string_enum" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9fe66b8ee349846ce2f9557a26b8f1e74843c4a13fb381f9a3d73617a5f956a" +dependencies = [ + "proc-macro2", + "quote", + "swc_macros_common", + "syn", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "swc_allocator" +version = "3.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a1f988452cab8c4e25776e5a855ba088cdb38fbe9714f9b9d2a6ff345824858" +dependencies = [ + "bumpalo", + "hashbrown 0.14.5", + "ptr_meta", + "rustc-hash 2.1.1", + "triomphe", +] + +[[package]] +name = "swc_atoms" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c24077f986f0bc1c07823f850f688dd9be91b186efdb03fe1d52f7c2f2a4a346" +dependencies = [ + "bytecheck", + "hstr", + "once_cell", + "rancor", + "rkyv", + "rustc-hash 2.1.1", + "serde", +] + +[[package]] +name = "swc_common" +version = "6.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7327d132e85f8a50e0a9e2458a7b44726b2db2f7f9c8b8a556f07f359c42a461" +dependencies = [ + "anyhow", + "ast_node", + "better_scoped_tls", + "bytecheck", + "cfg-if", + "either", + "from_variant", + "new_debug_unreachable", + "num-bigint", + "once_cell", + "parking_lot", + "rancor", + "rkyv", + "rustc-hash 2.1.1", + "serde", + "siphasher 0.3.11", + "sourcemap", + "swc_allocator", + "swc_atoms", + "swc_eq_ignore_macros", + "swc_visit", + "termcolor", + "tracing", + "unicode-width", + "url", +] + +[[package]] +name = "swc_core" +version = "13.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94c8a27be20f13eb5b27e623b0d9ef486d861066b5f725e23ed38ca0e560dcca" +dependencies = [ + "once_cell", + "swc_allocator", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_transforms_base", + "swc_ecma_transforms_testing", + "swc_ecma_visit", + "swc_plugin", + "swc_plugin_macro", + "swc_plugin_proxy", + "vergen", +] + +[[package]] +name = "swc_ecma_ast" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbd513dab5fb1181e66ac34c4c959e9e8824d8d2c8bd50f698f5f2943794c0cc" +dependencies = [ + "bitflags", + "bytecheck", + "is-macro", + "num-bigint", + "phf", + "rancor", + "rkyv", + "scoped-tls", + "string_enum", + "swc_atoms", + "swc_common", + "swc_visit", + "unicode-id-start", +] + +[[package]] +name = "swc_ecma_codegen" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa6bdce26d910981128bc709a997292a5d1c98d54a9832154a1083d1adb6fdd7" +dependencies = [ + "ascii", + "compact_str", + "memchr", + "num-bigint", + "once_cell", + "regex", + "rustc-hash 2.1.1", + "serde", + "sourcemap", + "swc_allocator", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_codegen_macros", + "tracing", +] + +[[package]] +name = "swc_ecma_codegen_macros" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f9a42f479a6475647e248fa9750982c87cd985e19d1016a1fc18a70682305d1" +dependencies = [ + "proc-macro2", + "quote", + "swc_macros_common", + "syn", +] + +[[package]] +name = "swc_ecma_parser" +version = "8.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a359eaebed82e5e13e1004d1e4003931b66d8b8edab8884f3d02ed827df7530" +dependencies = [ + "either", + "new_debug_unreachable", + "num-bigint", + "num-traits", + "phf", + "rustc-hash 2.1.1", + "serde", + "smallvec", + "smartstring", + "stacker", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "tracing", + "typed-arena", +] + +[[package]] +name = "swc_ecma_testing" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46f1b2d6510edc0f54f0856c2e776b5673c3df8088dd0bda8d97ba197d054133" +dependencies = [ + "anyhow", + "hex", + "sha2", + "testing", + "tracing", +] + +[[package]] +name = "swc_ecma_transforms_base" +version = "9.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b696e003dd095ae8b8dba00f601040f756273c9af0fd67cb1c57115785cb5ec" +dependencies = [ + "better_scoped_tls", + "bitflags", + "indexmap", + "once_cell", + "phf", + "rustc-hash 2.1.1", + "serde", + "smallvec", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_parser", + "swc_ecma_utils", + "swc_ecma_visit", + "swc_parallel", + "tracing", +] + +[[package]] +name = "swc_ecma_transforms_testing" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f1ad440cfdcafabcc4ab8bb01df2d1a488f65cdad8dc85de821f66ebcedca55" +dependencies = [ + "ansi_term", + "anyhow", + "base64", + "hex", + "serde", + "serde_json", + "sha2", + "sourcemap", + "swc_allocator", + "swc_common", + "swc_ecma_ast", + "swc_ecma_codegen", + "swc_ecma_parser", + "swc_ecma_testing", + "swc_ecma_transforms_base", + "swc_ecma_utils", + "swc_ecma_visit", + "tempfile", + "testing", +] + +[[package]] +name = "swc_ecma_utils" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c217edaa22c98537e09ed3189e723feed3d889eeb7e02a0b3d48cbb91ba7e4" +dependencies = [ + "indexmap", + "num_cpus", + "once_cell", + "rustc-hash 2.1.1", + "ryu-js", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_ecma_visit", + "swc_parallel", + "tracing", + "unicode-id", +] + +[[package]] +name = "swc_ecma_visit" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a32fb2902c01f9b4615605a4a3e67e0c928bd3b9f2182e764f1c9fe4130965cf" +dependencies = [ + "new_debug_unreachable", + "num-bigint", + "swc_atoms", + "swc_common", + "swc_ecma_ast", + "swc_visit", + "tracing", +] + +[[package]] +name = "swc_eq_ignore_macros" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e96e15288bf385ab85eb83cff7f9e2d834348da58d0a31b33bdb572e66ee413e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "swc_error_reporters" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed21ea887faeb0dab190838d2331ed187f2a74d185c9fe7044d5092900a83d29" +dependencies = [ + "anyhow", + "miette", + "once_cell", + "parking_lot", + "swc_common", +] + +[[package]] +name = "swc_macros_common" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a509f56fca05b39ba6c15f3e58636c3924c78347d63853632ed2ffcb6f5a0ac7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "swc_parallel" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5f75f1094d69174ef628e3665fff0f81d58e9f568802e3c90d332c72b0b6026" +dependencies = [ + "once_cell", +] + +[[package]] +name = "swc_plugin" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b45099a38ed45528bef939d0eac1a0c1347749d0c67d3dd744d545316c5fd05" +dependencies = [ + "once_cell", +] + +[[package]] +name = "swc_plugin_macro" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0917ccfdcd3fa6cf41bdacef2388702a3b274f9ea708d930e1e8db37c7c3e1c6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "swc_plugin_proxy" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61e8ae3974157b2939ada468ffec7932358f2f567abb6c237204dd603e52ffff" +dependencies = [ + "better_scoped_tls", + "bytecheck", + "rancor", + "rkyv", + "rustc-hash 2.1.1", + "swc_common", + "swc_ecma_ast", + "swc_trace_macro", + "tracing", +] + +[[package]] +name = "swc_trace_macro" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c78717a841565df57f811376a3d19c9156091c55175e12d378f3a522de70cef" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "swc_visit" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9138b6a36bbe76dd6753c4c0794f7e26480ea757bee499738bedbbb3ae3ec5f3" +dependencies = [ + "either", + "new_debug_unreachable", +] + +[[package]] +name = "syn" +version = "2.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tempfile" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91" +dependencies = [ + "cfg-if", + "fastrand", + "getrandom", + "once_cell", + "rustix", + "windows-sys", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "testing" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60326bf11ba23afed0b731866c6e8b709d516554dc813bb3a91f8a273f22f333" +dependencies = [ + "ansi_term", + "cargo_metadata 0.18.1", + "difference", + "once_cell", + "pretty_assertions", + "regex", + "rustc-hash 2.1.1", + "serde", + "serde_json", + "swc_common", + "swc_error_reporters", + "testing_macros", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "testing_macros" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2d27bf245b90a80d5aa231133418ae7db98f032855ce5292e12071ab29c4b26" +dependencies = [ + "anyhow", + "glob", + "once_cell", + "proc-macro2", + "quote", + "regex", + "relative-path", + "syn", +] + +[[package]] +name = "textwrap" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" +dependencies = [ + "unicode-linebreak", + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +dependencies = [ + "thiserror-impl 2.0.11", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "triomphe" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef8f7726da4807b58ea5c96fdc122f80702030edc33b35aff9190a51148ccc85" +dependencies = [ + "serde", + "stable_deref_trait", +] + +[[package]] +name = "typed-arena" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-id" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10103c57044730945224467c09f71a4db0071c123a0648cc3e818913bde6b561" + +[[package]] +name = "unicode-id-start" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f322b60f6b9736017344fa0635d64be2f458fbc04eef65f6be22976dd1ffd5b" + +[[package]] +name = "unicode-ident" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" + +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "uuid" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced87ca4be083373936a67f8de945faa23b6b42384bd5b64434850802c6dccd0" + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "vergen" +version = "9.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0d2f179f8075b805a43a2a21728a46f0cc2921b3c58695b28fa8817e103cd9a" +dependencies = [ + "anyhow", + "cargo_metadata 0.19.1", + "derive_builder", + "regex", + "rustversion", + "vergen-lib", +] + +[[package]] +name = "vergen-lib" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b07e6010c0f3e59fcb164e0163834597da68d1f864e2b8ca49f74de01e9c166" +dependencies = [ + "anyhow", + "derive_builder", + "rustversion", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags", +] + +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/rust/cubetranspilers/Cargo.toml b/rust/cubetranspilers/Cargo.toml new file mode 100644 index 0000000000000..f153ebbea2ead --- /dev/null +++ b/rust/cubetranspilers/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "cubetranspilers" +version = "0.1.0" +edition = "2021" + +[dependencies] +indexmap = "2.7.1" +regex = "1.11.1" +serde = { version = "1.0.217", features = ["derive"] } +serde_json = "1.0.138" +swc_core = { version = "13.3.*", features = ["ecma_plugin_transform"] } +swc_ecma_parser = "8.0.1" +swc_ecma_codegen = "6.1.0" +anyhow = "1.0.95" + +[dev-dependencies] +insta = "1" diff --git a/rust/cubetranspilers/rust-toolchain.toml b/rust/cubetranspilers/rust-toolchain.toml new file mode 100644 index 0000000000000..d9f5179de3d93 --- /dev/null +++ b/rust/cubetranspilers/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "1.84.1" +components = ["rustfmt", "clippy"] +profile = "minimal" diff --git a/rust/cubetranspilers/rustfmt.toml b/rust/cubetranspilers/rustfmt.toml new file mode 100644 index 0000000000000..0912ea72b3bf8 --- /dev/null +++ b/rust/cubetranspilers/rustfmt.toml @@ -0,0 +1 @@ +#imports_granularity = "Crate" diff --git a/rust/cubetranspilers/src/check_dup_prop_transpiler.rs b/rust/cubetranspilers/src/check_dup_prop_transpiler.rs new file mode 100644 index 0000000000000..a13b09275ed72 --- /dev/null +++ b/rust/cubetranspilers/src/check_dup_prop_transpiler.rs @@ -0,0 +1,107 @@ +use std::collections::HashSet; + +use swc_core::common::errors::Handler; +use swc_core::common::{BytePos, Span, DUMMY_SP}; +use swc_core::ecma::visit::VisitMutWith; +use swc_core::{ + ecma::{ast::*, visit::VisitMut}, + plugin::proxies::PluginSourceMapProxy, +}; + +pub struct CheckDupPropTransformVisitor<'a> { + pub(crate) source_map: Option, + handler: &'a Handler, +} + +impl<'a> CheckDupPropTransformVisitor<'a> { + pub fn new(source_map: Option, handler: &'a Handler) -> Self { + CheckDupPropTransformVisitor { + source_map, + handler, + } + } + + fn emit_error(&mut self, span: Span, message: &str) { + self.handler + .struct_span_err(span, &self.format_msg(span, message)) + .emit(); + } + + fn format_msg(&self, span: Span, message: &str) -> String { + if let Some(ref sm) = self.source_map { + if let Some(source_file) = sm.source_file.get() { + let loc = source_file.lookup_line(span.lo()).unwrap_or(0); + let column = span.lo() - source_file.line_begin_pos(BytePos(loc as u32)); + format!( + "{}. Found in {}:{}:{}", + message, + source_file.name, + loc + 1, + column.0, + ) + } else { + message.to_string() + } + } else { + message.to_string() + } + } + + fn compile_key(&self, key: &PropName) -> Option { + match key { + PropName::Ident(ident) => Some(ident.sym.to_string()), + PropName::Str(s) => Some(s.value.to_string()), + _ => None, + } + } + + fn check_object_expr(&mut self, obj: &ObjectLit) { + let mut unique = HashSet::new(); + + for prop_or_spread in obj.props.iter() { + if let PropOrSpread::Prop(prop_box) = prop_or_spread { + if let Prop::KeyValue(kv) = &**prop_box { + if let Expr::Object(ref inner_obj) = *kv.value { + self.check_object_expr(inner_obj); + } + if let Some(key_name) = self.compile_key(&kv.key) { + if unique.contains(&key_name) { + let span = match &kv.key { + PropName::Ident(ident) => ident.span, + PropName::Str(s) => s.span, + _ => DUMMY_SP, + }; + self.emit_error( + span, + &format!("Duplicate property parsing {}", key_name), + ); + } else { + unique.insert(key_name); + } + } + } + } + } + } +} + +impl VisitMut for CheckDupPropTransformVisitor<'_> { + // Implement necessary visit_mut_* methods for actual custom transform. + // A comprehensive list of possible visitor methods can be found here: + // https://rustdoc.swc.rs/swc_ecma_visit/trait.VisitMut.html + + fn visit_mut_call_expr(&mut self, call_expr: &mut CallExpr) { + if let Callee::Expr(callee_expr) = &call_expr.callee { + if let Expr::Ident(ident) = &**callee_expr { + if ident.sym == *"cube" || ident.sym == *"view" { + for arg in call_expr.args.iter() { + if let Expr::Object(ref obj_lit) = *arg.expr { + self.check_object_expr(obj_lit); + } + } + } + } + } + call_expr.visit_mut_children_with(self) + } +} diff --git a/rust/cubetranspilers/src/cube_prop_ctx_transpiler.rs b/rust/cubetranspilers/src/cube_prop_ctx_transpiler.rs new file mode 100644 index 0000000000000..b6e18b712f680 --- /dev/null +++ b/rust/cubetranspilers/src/cube_prop_ctx_transpiler.rs @@ -0,0 +1,397 @@ +use indexmap::IndexSet; +use regex::Regex; +use std::collections::{HashMap, HashSet}; +use std::sync::LazyLock; +use swc_core::atoms::Atom; +use swc_core::common::errors::Handler; +use swc_core::common::{BytePos, Span, SyntaxContext, DUMMY_SP}; +use swc_core::ecma::visit::{Visit, VisitMutWith, VisitWith}; + +use swc_core::{ + ecma::{ast::*, visit::VisitMut}, + plugin::proxies::PluginSourceMapProxy, +}; + +static CURRENT_CUBE_CONSTANTS: [&str; 2] = ["CUBE", "TABLE"]; + +static TRANSPILLED_FIELDS_PATTERNS: LazyLock> = LazyLock::new(|| { + let patterns = [ + r"sql$", + r"(sqlTable|sql_table)$", + r"^measures\.[_a-zA-Z][_a-zA-Z0-9]*\.(drillMemberReferences|drillMembers|drill_members)$", + r"^measures\.[_a-zA-Z][_a-zA-Z0-9]*\.(orderBy|order_by)\.[0-9]+\.sql$", + r"^measures\.[_a-zA-Z][_a-zA-Z0-9]*\.(timeShift|time_shift)\.[0-9]+\.(timeDimension|time_dimension)$", + r"^measures\.[_a-zA-Z][_a-zA-Z0-9]*\.(reduceBy|reduce_by|groupBy|group_by|addGroupBy|add_group_by)$", + r"^dimensions\.[_a-zA-Z][_a-zA-Z0-9]*\.(reduceBy|reduce_by|groupBy|group_by|addGroupBy|add_group_by)$", + r"^(preAggregations|pre_aggregations)\.[_a-zA-Z][_a-zA-Z0-9]*\.indexes\.[_a-zA-Z][_a-zA-Z0-9]*\.columns$", + r"^(preAggregations|pre_aggregations)\.[_a-zA-Z][_a-zA-Z0-9]*\.(timeDimensionReference|timeDimension|time_dimension|segments|dimensions|measures|rollups|segmentReferences|dimensionReferences|measureReferences|rollupReferences)$", + r"^(preAggregations|pre_aggregations)\.[_a-zA-Z][_a-zA-Z0-9]*\.(timeDimensions|time_dimensions)\.\d+\.dimension$", + r"^(preAggregations|pre_aggregations)\.[_a-zA-Z][_a-zA-Z0-9]*\.(outputColumnTypes|output_column_types)\.\d+\.member$", + r"^contextMembers$", + r"^includes$", + r"^excludes$", + r"^hierarchies\.[_a-zA-Z][_a-zA-Z0-9]*\.levels$", + r"^cubes\.[0-9]+\.(joinPath|join_path)$", + r"^(accessPolicy|access_policy)\.[0-9]+\.(rowLevel|row_level)\.filters\.[0-9]+.*\.member$", + r"^(accessPolicy|access_policy)\.[0-9]+\.(rowLevel|row_level)\.filters\.[0-9]+.*\.values$", + r"^(accessPolicy|access_policy)\.[0-9]+\.conditions.[0-9]+\.if$", + ]; + patterns + .iter() + .map(|pat| Regex::new(pat).expect("Invalid regex pattern")) + .collect() +}); + +static TRANSPILLED_FIELDS: LazyLock> = LazyLock::new(|| { + let re_extract = Regex::new(r".*?([_a-zA-Z|][_a-zA-Z0-9|]*)([^_a-zA-Z0-9|]*)$").unwrap(); + let mut set = HashSet::new(); + + for regex in TRANSPILLED_FIELDS_PATTERNS.iter() { + let pat_str = regex.as_str(); + if let Some(caps) = re_extract.captures(pat_str) { + if let Some(m) = caps.get(1) { + let fields_str = m.as_str(); + for field in fields_str.split('|') { + set.insert(field.to_string()); + } + } + } + } + set +}); + +pub struct CubePropTransformVisitor<'a> { + pub(crate) cube_names: HashSet, + pub(crate) cube_symbols: HashMap>, + pub(crate) context_symbols: HashMap, + pub(crate) source_map: Option, + handler: &'a Handler, +} + +impl<'a> CubePropTransformVisitor<'a> { + pub fn new( + cube_names: HashSet, + cube_symbols: HashMap>, + context_symbols: HashMap, + source_map: Option, + handler: &'a Handler, + ) -> Self { + CubePropTransformVisitor { + source_map, + cube_names, + cube_symbols, + context_symbols, + handler, + } + } + + fn emit_error(&self, span: Span, message: &str) { + self.handler + .struct_span_err(span, &self.format_msg(span, message)) + .emit(); + } + + fn format_msg(&self, span: Span, message: &str) -> String { + if let Some(ref sm) = self.source_map { + if let Some(source_file) = sm.source_file.get() { + let loc = source_file.lookup_line(span.lo()).unwrap_or(0); + let column = span.lo() - source_file.line_begin_pos(BytePos(loc as u32)); + format!( + "{}. Found in {}:{}:{}", + message, + source_file.name, + loc + 1, + column.0, + ) + } else { + message.to_string() + } + } else { + message.to_string() + } + } + + fn resolve_cube(&self, name: &str) -> bool { + self.cube_names.contains(name) + } + + fn is_current_cube(&self, name: &str) -> bool { + CURRENT_CUBE_CONSTANTS.contains(&name) + } + + fn resolve_symbol(&self, cube_name: &str, name: &str, span: Span) -> bool { + if name == "USER_CONTEXT" { + self.emit_error( + span, + "Support for USER_CONTEXT was removed, please migrate to SECURITY_CONTEXT", + ); + return true; + } + + if self.context_symbols.contains_key(name) { + return true; + } + + let key = if self.is_current_cube(name) { + cube_name + } else { + name + }; + + match self.cube_symbols.get(key) { + Some(_cube) => true, + None => match self.cube_symbols.get(cube_name) { + Some(cube) => cube.get(name).copied().unwrap_or(false), + None => false, + }, + } + } + + fn sql_and_references_field_visitor<'b>( + &'b mut self, + cube_name: Option, + ) -> SqlAndReferencesFieldVisitor<'b, 'a> + where + 'a: 'b, + { + SqlAndReferencesFieldVisitor { + cube_name, + parent: self, + path_stack: Vec::new(), + } + } + + fn known_identifiers_inject_visitor<'b>( + &'b mut self, + field: String, + ) -> KnownIdentifiersInjectVisitor<'b, 'a> + where + 'a: 'b, + { + KnownIdentifiersInjectVisitor { + field, + parent: self, + } + } + + /// Converts the property value (Prop::KeyValue) to an arrow function whose parameters + /// are unique identifiers collected from the source expression. + fn transform_object_property(&mut self, prop: &mut Prop, resolve: &dyn Fn(&str) -> bool) { + if let Prop::KeyValue(ref mut kv) = prop { + if let Some(new_expr) = self.replace_value_with_arrow_function(resolve, &kv.value) { + kv.value = Box::new(new_expr); + } + } + } + + /// Collects identifiers from the expression and returns ArrowExpr, + /// where the parameters are the collected identifiers and the body is the original expression. + fn replace_value_with_arrow_function( + &mut self, + resolve: &dyn Fn(&str) -> bool, + expr: &Expr, + ) -> Option { + let mut collector = CollectIdentifiersVisitor { + identifiers: IndexSet::new(), + resolve, + }; + expr.visit_with(&mut collector); + let params: Vec = collector + .identifiers + .into_iter() + .map(|(sym, ctxt)| { + Pat::Ident(BindingIdent { + id: Ident::new(sym, DUMMY_SP, ctxt), + type_ann: None, + }) + }) + .collect(); + let body_expr = match expr { + Expr::Arrow(arrow_expr) => arrow_expr.body.clone(), + _ => Box::new(BlockStmtOrExpr::Expr(Box::new((*expr).clone()))), + }; + let arrow = ArrowExpr { + span: DUMMY_SP, + params, + body: body_expr, + is_async: false, + is_generator: false, + type_params: None, + return_type: None, + ctxt: SyntaxContext::empty(), + }; + Some(Expr::Arrow(arrow)) + } +} + +impl VisitMut for CubePropTransformVisitor<'_> { + // Implement necessary visit_mut_* methods for actual custom transform. + // A comprehensive list of possible visitor methods can be found here: + // https://rustdoc.swc.rs/swc_ecma_visit/trait.VisitMut.html + + fn visit_mut_call_expr(&mut self, call_expr: &mut CallExpr) { + if let Callee::Expr(callee) = &call_expr.callee { + if let Expr::Ident(ident) = &**callee { + let callee_name = ident.sym.to_string(); + let args_len = call_expr.args.len(); + if args_len > 0 { + if callee_name == "cube" || callee_name == "view" { + if args_len != 2 { + self.emit_error( + call_expr.span, + &format!( + "Incorrect number of arguments to {}() function", + callee_name + ), + ); + return; + } + let cube_name_opt: Option = { + let first_arg = &call_expr.args[0].expr; + match &**first_arg { + Expr::Lit(Lit::Str(s)) => Some(s.value.to_string()), + Expr::Tpl(tpl) => { + if !tpl.quasis.is_empty() { + tpl.quasis[0].cooked.as_ref().map(|c| c.clone().to_string()) + } else { + None + } + } + _ => None, + } + }; + if let Some(last_arg) = call_expr.args.last_mut() { + { + let mut sql_visitor = + self.sql_and_references_field_visitor(cube_name_opt.clone()); + last_arg.visit_mut_with(&mut sql_visitor); + } + { + let mut known_visitor = + self.known_identifiers_inject_visitor("extends".to_string()); + last_arg.visit_mut_with(&mut known_visitor); + } + } + } else if callee_name == "context" { + if let Some(last_arg) = call_expr.args.last_mut() { + let mut sql_visitor = { + let self_ref = &mut *self; + self_ref.sql_and_references_field_visitor(None) + }; + + last_arg.expr.visit_mut_with(&mut sql_visitor); + } + } + } + } + } + call_expr.visit_mut_children_with(self) + } +} + +pub struct SqlAndReferencesFieldVisitor<'b, 'a: 'b> { + pub cube_name: Option, + pub parent: &'b mut CubePropTransformVisitor<'a>, + pub path_stack: Vec, +} + +impl SqlAndReferencesFieldVisitor<'_, '_> { + fn current_path(&self) -> String { + self.path_stack.join(".") + } +} + +impl VisitMut for SqlAndReferencesFieldVisitor<'_, '_> { + fn visit_mut_prop(&mut self, prop: &mut Prop) { + let mut added = false; + if let Prop::KeyValue(ref kv) = prop { + if let PropName::Ident(ref ident) = kv.key { + let key_name = ident.sym.to_string(); + self.path_stack.push(key_name.clone()); + added = true; + if TRANSPILLED_FIELDS.contains(&key_name) { + let full_path = self.current_path(); + for pattern in TRANSPILLED_FIELDS_PATTERNS.iter() { + if pattern.is_match(&full_path) { + let parent_ptr = self.parent as *mut CubePropTransformVisitor; + let resolve = |n: &str| unsafe { + (*parent_ptr).resolve_symbol( + self.cube_name.as_deref().unwrap_or(""), + n, + DUMMY_SP, + ) || (*parent_ptr).is_current_cube(n) + }; + self.parent.transform_object_property(prop, &resolve); + self.path_stack.pop(); + return; + } + } + } + } + } + prop.visit_mut_children_with(self); + + if !self.path_stack.is_empty() && added { + self.path_stack.pop(); + } + } + + fn visit_mut_array_lit(&mut self, arr: &mut ArrayLit) { + for (idx, el) in arr.elems.iter_mut().enumerate() { + if let Some(el) = el { + self.path_stack.push(idx.to_string()); + el.visit_mut_children_with(self); + self.path_stack.pop(); + } + } + } +} + +pub struct KnownIdentifiersInjectVisitor<'b, 'a: 'b> { + pub field: String, + pub parent: &'b mut CubePropTransformVisitor<'a>, +} + +impl VisitMut for KnownIdentifiersInjectVisitor<'_, '_> { + fn visit_mut_prop(&mut self, prop: &mut Prop) { + let ident_name = match &prop { + Prop::Shorthand(ident) => ident.sym.clone().to_string(), + Prop::KeyValue(key_value_prop) => match &key_value_prop.key { + PropName::Ident(ident_name) => ident_name.sym.clone().to_string(), + PropName::Str(str) => str.value.clone().to_string(), + _ => "".to_string(), + }, + _ => "".to_string(), + }; + + if ident_name.contains(&self.field) { + let parent_ptr = self.parent as *mut CubePropTransformVisitor; + let resolve = move |n: &str| unsafe { (*parent_ptr).resolve_cube(n) }; + self.parent.transform_object_property(prop, &resolve); + } + + prop.visit_mut_children_with(self) + } +} + +pub struct CollectIdentifiersVisitor<'a> { + pub identifiers: IndexSet<(Atom, SyntaxContext)>, + pub resolve: &'a dyn Fn(&str) -> bool, +} + +impl Visit for CollectIdentifiersVisitor<'_> { + fn visit_ident(&mut self, ident: &Ident) { + if (self.resolve)(&ident.sym) { + self.identifiers.insert((ident.sym.clone(), ident.ctxt)); + } + } + + fn visit_member_expr(&mut self, member: &MemberExpr) { + member.obj.visit_with(self); + match &member.prop { + MemberProp::Ident(_ident_name) => member.prop.visit_with(self), + MemberProp::PrivateName(_private_name) => {} + MemberProp::Computed(_computed_prop_name) => member.prop.visit_with(self), + }; + } +} diff --git a/rust/cubetranspilers/src/error_reporter.rs b/rust/cubetranspilers/src/error_reporter.rs new file mode 100644 index 0000000000000..eef3581c9cfc0 --- /dev/null +++ b/rust/cubetranspilers/src/error_reporter.rs @@ -0,0 +1,45 @@ +use std::sync::{Arc, Mutex}; + +use swc_core::common::errors::{DiagnosticBuilder, Emitter}; + +pub struct ErrorReporter { + pub errors: Arc>>, + pub warnings: Arc>>, +} + +impl ErrorReporter { + pub fn new(errors: Arc>>, warnings: Arc>>) -> Self { + ErrorReporter { errors, warnings } + } +} + +impl Default for ErrorReporter { + fn default() -> Self { + ErrorReporter { + errors: Arc::new(Mutex::new(Vec::new())), + warnings: Arc::new(Mutex::new(Vec::new())), + } + } +} + +impl Emitter for ErrorReporter { + fn emit(&mut self, diagnostic: &DiagnosticBuilder) { + match diagnostic.diagnostic.level { + swc_core::common::errors::Level::Bug + | swc_core::common::errors::Level::Fatal + | swc_core::common::errors::Level::PhaseFatal + | swc_core::common::errors::Level::Error => { + let mut errors = self.errors.lock().unwrap(); + errors.push(diagnostic.message()); + } + swc_core::common::errors::Level::Warning + | swc_core::common::errors::Level::Note + | swc_core::common::errors::Level::Help + | swc_core::common::errors::Level::Cancelled + | swc_core::common::errors::Level::FailureNote => { + let mut warnings = self.warnings.lock().unwrap(); + warnings.push(diagnostic.message()); + } + } + } +} diff --git a/rust/cubetranspilers/src/import_export_transpiler.rs b/rust/cubetranspilers/src/import_export_transpiler.rs new file mode 100644 index 0000000000000..b63f4182fcf98 --- /dev/null +++ b/rust/cubetranspilers/src/import_export_transpiler.rs @@ -0,0 +1,356 @@ +use swc_core::common::errors::Handler; +use swc_core::common::BytePos; +use swc_core::common::{Span, SyntaxContext, DUMMY_SP}; +use swc_core::ecma::visit::{noop_visit_mut_type, VisitMutWith}; +use swc_core::{ + ecma::{ast::*, visit::VisitMut}, + plugin::proxies::PluginSourceMapProxy, +}; + +pub struct ImportExportTransformVisitor<'a> { + pub(crate) source_map: Option, + handler: &'a Handler, +} + +impl<'a> ImportExportTransformVisitor<'a> { + pub fn new(source_map: Option, handler: &'a Handler) -> Self { + ImportExportTransformVisitor { + source_map, + handler, + } + } + + fn emit_error(&self, span: Span, message: &str) { + self.handler + .struct_span_err(span, &self.format_msg(span, message)) + .emit(); + } + + fn format_msg(&self, span: Span, message: &str) -> String { + if let Some(ref sm) = self.source_map { + if let Some(source_file) = sm.source_file.get() { + let loc = source_file.lookup_line(span.lo()).unwrap_or(0); + let column = span.lo() - source_file.line_begin_pos(BytePos(loc as u32)); + format!( + "{}. Found in {}:{}:{}", + message, + source_file.name, + loc + 1, + column.0, + ) + } else { + message.to_string() + } + } else { + message.to_string() + } + } +} + +impl VisitMut for ImportExportTransformVisitor<'_> { + // Implement necessary visit_mut_* methods for actual custom transform. + // A comprehensive list of possible visitor methods can be found here: + // https://rustdoc.swc.rs/swc_ecma_visit/trait.VisitMut.html + + // We don't need to do anything besides imports here + noop_visit_mut_type!(); + + // Can't use visit_mut_module_item for cases when we need to replace + // the item with multiple statements + fn visit_mut_module(&mut self, module: &mut Module) { + let mut new_body = Vec::with_capacity(module.body.len()); + + for mut item in module.body.drain(..) { + self.visit_mut_module_item(&mut item); + + match &item { + ModuleItem::ModuleDecl(ModuleDecl::ExportDecl(export_decl)) => { + let decl = export_decl.decl.clone(); + let stmt_decl = ModuleItem::Stmt(Stmt::Decl(decl.clone())); + + let mut ids = vec![]; + + match decl { + Decl::Var(var_decl) => { + for var_declarator in var_decl.decls.iter() { + if let Pat::Ident(BindingIdent { id, .. }) = &var_declarator.name { + ids.push(id.clone()); + } + } + } + Decl::Fn(fn_decl) => { + ids.push(fn_decl.ident.clone()); + } + Decl::Class(class_decl) => { + ids.push(class_decl.ident.clone()); + } + _ => {} + } + + let props: Vec = ids + .iter() + .map(|ident| { + PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp { + key: PropName::Ident(IdentName::from(ident.sym.clone())), + value: Box::new(Expr::Ident(ident.clone())), + }))) + }) + .collect(); + + let add_export_call = Expr::Call(CallExpr { + span: DUMMY_SP, + callee: Callee::Expr(Box::new(Expr::Ident(Ident::new( + "addExport".into(), + DUMMY_SP, + SyntaxContext::empty(), + )))), + args: vec![ExprOrSpread { + spread: None, + expr: Box::new(Expr::Object(ObjectLit { + span: DUMMY_SP, + props, + })), + }], + type_args: None, + ctxt: SyntaxContext::empty(), + }); + + let stmt_add_export = ModuleItem::Stmt(Stmt::Expr(ExprStmt { + span: DUMMY_SP, + expr: Box::new(add_export_call), + })); + + new_body.extend(vec![stmt_decl, stmt_add_export]); + } + _ => new_body.push(item), + } + } + + module.body = new_body; + } + + fn visit_mut_module_item(&mut self, item: &mut ModuleItem) { + if let ModuleItem::ModuleDecl(decl) = item { + match decl { + ModuleDecl::Import(import_decl) => { + let require_call = Expr::Call(CallExpr { + span: DUMMY_SP, + callee: Callee::Expr(Box::new(Expr::Ident(Ident::new( + "require".into(), + DUMMY_SP, + SyntaxContext::empty(), + )))), + args: vec![ExprOrSpread { + spread: None, + expr: Box::new(Expr::Lit(Lit::Str(*import_decl.src.clone()))), + }], + type_args: None, + ctxt: SyntaxContext::empty(), + }); + + let mut var_decls = Vec::with_capacity(import_decl.specifiers.len()); + + for spec in &import_decl.specifiers { + match spec { + ImportSpecifier::Named(named) => { + let local_ident = named.local.clone(); + let init_expr = if let Some(imported) = &named.imported { + match imported { + ModuleExportName::Ident(id) => Expr::Member(MemberExpr { + span: DUMMY_SP, + obj: Box::new(require_call.clone()), + prop: MemberProp::Ident(IdentName { + span: DUMMY_SP, + sym: id.sym.clone(), + }), + }), + ModuleExportName::Str(s) => Expr::Member(MemberExpr { + span: DUMMY_SP, + obj: Box::new(require_call.clone()), + prop: MemberProp::Computed(ComputedPropName { + span: DUMMY_SP, + expr: Box::new(Expr::Lit(Lit::Str(s.clone()))), + }), + }), + } + } else { + Expr::Member(MemberExpr { + span: DUMMY_SP, + obj: Box::new(require_call.clone()), + prop: MemberProp::Ident(IdentName { + span: DUMMY_SP, + sym: local_ident.sym.clone(), + }), + }) + }; + + let var_decl = VarDeclarator { + span: DUMMY_SP, + name: Pat::Ident(BindingIdent { + id: local_ident, + type_ann: None, + }), + init: Some(Box::new(init_expr)), + definite: false, + }; + var_decls.push(var_decl); + } + ImportSpecifier::Default(default) => { + let local_ident = default.local.clone(); + let var_decl = VarDeclarator { + span: DUMMY_SP, + name: Pat::Ident(BindingIdent { + id: local_ident, + type_ann: None, + }), + init: Some(Box::new(require_call.clone())), + definite: false, + }; + var_decls.push(var_decl); + } + ImportSpecifier::Namespace(star_as) => { + self.emit_error(star_as.span, "Namespace import not supported"); + } + } + } + + *item = ModuleItem::Stmt(Stmt::Decl(Decl::Var(Box::new(VarDecl { + span: DUMMY_SP, + kind: VarDeclKind::Const, + declare: false, + decls: var_decls, + ctxt: SyntaxContext::empty(), + })))); + } + ModuleDecl::ExportNamed(export_named) => { + // For named exports we collect object properties for each specifier + let mut props = Vec::with_capacity(export_named.specifiers.len()); + for spec in &export_named.specifiers { + match spec { + ExportSpecifier::Named(named_spec) => { + // Cases like `export { foo as bar }` + let key = if let Some(exported) = &named_spec.exported { + match exported { + ModuleExportName::Ident(id) => PropName::Ident(IdentName { + span: DUMMY_SP, + sym: id.sym.clone(), + }), + ModuleExportName::Str(s) => PropName::Str(s.clone()), + } + } else { + match &named_spec.orig { + ModuleExportName::Ident(id) => PropName::Ident(IdentName { + span: DUMMY_SP, + sym: id.sym.clone(), + }), + ModuleExportName::Str(s) => PropName::Str(s.clone()), + } + }; + let value_expr = match &named_spec.orig { + ModuleExportName::Ident(id) => Expr::Ident(id.clone()), + ModuleExportName::Str(s) => Expr::Lit(Lit::Str(s.clone())), + }; + let prop = + PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp { + key, + value: Box::new(value_expr), + }))); + props.push(prop); + } + ExportSpecifier::Namespace(_export_namespace_specifier) => { + self.emit_error( + export_named.span, + "Unsupported export specifier: Named Namespace", + ); + } + ExportSpecifier::Default(_export_default_specifier) => { + self.emit_error( + export_named.span, + "Unsupported export specifier: Named Default", + ); + } + } + } + let obj_expr = Expr::Object(ObjectLit { + span: DUMMY_SP, + props, + }); + let add_export_call = Expr::Call(CallExpr { + span: DUMMY_SP, + callee: Callee::Expr(Box::new(Expr::Ident(Ident::new( + "addExport".into(), + DUMMY_SP, + SyntaxContext::empty(), + )))), + args: vec![ExprOrSpread { + spread: None, + expr: Box::new(obj_expr), + }], + type_args: None, + ctxt: SyntaxContext::empty(), + }); + + *item = ModuleItem::Stmt(Stmt::Expr(ExprStmt { + span: DUMMY_SP, + expr: Box::new(add_export_call), + })); + } + ModuleDecl::ExportDefaultDecl(export_default) => { + let decl_expr: Expr = match &export_default.decl { + DefaultDecl::Fn(expr) => Expr::Fn(FnExpr::from(expr.function.clone())), + DefaultDecl::Class(expr) => { + Expr::Class(ClassExpr::from(expr.class.clone())) + } + DefaultDecl::TsInterfaceDecl(tsdecl) => { + self.emit_error(tsdecl.span, "Unsupported default export declaration"); + // Return null as fallback + Expr::Lit(Lit::Null(Null { span: DUMMY_SP })) + } + }; + let set_export_call = Expr::Call(CallExpr { + span: DUMMY_SP, + callee: Callee::Expr(Box::new(Expr::Ident(Ident::new( + "setExport".into(), + DUMMY_SP, + SyntaxContext::empty(), + )))), + args: vec![ExprOrSpread { + spread: None, + expr: Box::new(decl_expr), + }], + type_args: None, + ctxt: SyntaxContext::empty(), + }); + *item = ModuleItem::Stmt(Stmt::Expr(ExprStmt { + span: DUMMY_SP, + expr: Box::new(set_export_call), + })); + } + ModuleDecl::ExportDefaultExpr(export_default) => { + let decl_expr: Expr = *export_default.expr.clone(); + let set_export_call = Expr::Call(CallExpr { + span: DUMMY_SP, + callee: Callee::Expr(Box::new(Expr::Ident(Ident::new( + "setExport".into(), + DUMMY_SP, + SyntaxContext::empty(), + )))), + args: vec![ExprOrSpread { + spread: None, + expr: Box::new(decl_expr), + }], + type_args: None, + ctxt: SyntaxContext::empty(), + }); + *item = ModuleItem::Stmt(Stmt::Expr(ExprStmt { + span: DUMMY_SP, + expr: Box::new(set_export_call), + })); + } + _ => {} + } + } + + item.visit_mut_children_with(self) + } +} diff --git a/rust/cubetranspilers/src/lib.rs b/rust/cubetranspilers/src/lib.rs new file mode 100644 index 0000000000000..e45f3b65f2ee6 --- /dev/null +++ b/rust/cubetranspilers/src/lib.rs @@ -0,0 +1,156 @@ +use crate::check_dup_prop_transpiler::CheckDupPropTransformVisitor; +use crate::cube_prop_ctx_transpiler::CubePropTransformVisitor; +use crate::import_export_transpiler::ImportExportTransformVisitor; +use crate::validation_transpiler::ValidationTransformVisitor; +use anyhow::{anyhow, Result}; +use error_reporter::ErrorReporter; +use serde::{Deserialize, Serialize}; +use std::collections::{HashMap, HashSet}; +use std::sync::{Arc, Mutex}; +use swc_core::common::errors::{Handler, HandlerFlags}; +use swc_core::common::input::StringInput; +use swc_core::common::sync::{Lrc, OnceCell}; +use swc_core::common::{FileName, SourceMap}; +use swc_core::ecma::ast::{EsVersion, Program}; +use swc_core::ecma::visit::VisitMutWith; +use swc_core::plugin::proxies::PluginSourceMapProxy; +use swc_ecma_codegen::Config; +use swc_ecma_codegen::{text_writer::JsWriter, Emitter as CodeEmitter}; +use swc_ecma_parser::lexer::Lexer; +use swc_ecma_parser::{Parser, Syntax}; + +pub mod check_dup_prop_transpiler; +pub mod cube_prop_ctx_transpiler; +pub mod error_reporter; +pub mod import_export_transpiler; +pub mod validation_transpiler; + +#[derive(Deserialize, Clone, Debug)] +pub enum Transpilers { + CubeCheckDuplicatePropTranspiler, + CubePropContextTranspiler, + ImportExportTranspiler, + ValidationTranspiler, +} + +#[derive(Deserialize, Clone, Debug, Default)] +#[serde(rename_all = "camelCase")] +pub struct TransformConfig { + pub file_name: String, + pub transpilers: Vec, + pub cube_names: HashSet, + pub cube_symbols: HashMap>, + pub context_symbols: HashMap, +} + +#[derive(Serialize, Clone, Debug)] +#[serde(rename_all = "camelCase")] +pub struct TransformResult { + pub code: String, + pub errors: Vec, + pub warnings: Vec, +} + +pub fn run_transpilers( + sources: String, + transform_config: TransformConfig, +) -> Result { + let sm: Lrc = Default::default(); + let sf = sm.new_source_file( + Arc::new(FileName::Custom(transform_config.file_name)), + sources, + ); + + let lexer = Lexer::new( + Syntax::Es(Default::default()), + EsVersion::Es2015, + StringInput::from(&*sf), + None, + ); + let mut parser = Parser::new_from(lexer); + + let mut program = match parser.parse_program() { + Ok(v) => v, + Err(_err) => return Err(anyhow!("Failed to parse the JS code")), + }; + + let sm_cell = OnceCell::new(); + sm_cell + .set(sf.clone()) + .map_err(|_err| anyhow!("Failed to init OnceCell with source file"))?; + + let plugin_source_map = PluginSourceMapProxy { + source_file: sm_cell, + }; + + let errors = Arc::new(Mutex::new(Vec::new())); + let warnings = Arc::new(Mutex::new(Vec::new())); + + let reporter = Box::new(ErrorReporter::new(errors.clone(), warnings.clone())); + let handler = Handler::with_emitter_and_flags( + reporter, + HandlerFlags { + can_emit_warnings: true, + ..Default::default() + }, + ); + + transform_config + .transpilers + .into_iter() + .for_each(|transpiler| match transpiler { + Transpilers::CubeCheckDuplicatePropTranspiler => { + let mut visitor = + CheckDupPropTransformVisitor::new(Some(plugin_source_map.clone()), &handler); + program.visit_mut_with(&mut visitor); + } + Transpilers::CubePropContextTranspiler => { + let mut visitor = CubePropTransformVisitor::new( + transform_config.cube_names.clone(), + transform_config.cube_symbols.clone(), + transform_config.context_symbols.clone(), + Some(plugin_source_map.clone()), + &handler, + ); + program.visit_mut_with(&mut visitor); + } + Transpilers::ImportExportTranspiler => { + let mut visitor = + ImportExportTransformVisitor::new(Some(plugin_source_map.clone()), &handler); + program.visit_mut_with(&mut visitor); + } + Transpilers::ValidationTranspiler => { + let mut visitor = + ValidationTransformVisitor::new(Some(plugin_source_map.clone()), &handler); + program.visit_mut_with(&mut visitor); + } + }); + + let output_code = generate_code(&program, &sm)?; + let errors = errors.lock().unwrap().clone(); + let warnings = warnings.lock().unwrap().clone(); + + Ok(TransformResult { + code: output_code, + errors, + warnings, + }) +} + +pub fn generate_code(program: &Program, sm: &Lrc) -> Result { + let mut buf = vec![]; + { + let mut emitter = CodeEmitter { + cfg: Config::default().with_target(EsVersion::Es2015), + comments: None, + wr: JsWriter::new(sm.clone(), "\n", &mut buf, None), + cm: sm.clone(), + }; + emitter + .emit_program(program) + .map_err(|err| anyhow!("Failed to generate code: {}", err))?; + } + + let code = String::from_utf8(buf).map_err(|err| anyhow!("Invalid UTF8: {}", err))?; + Ok(code) +} diff --git a/rust/cubetranspilers/src/validation_transpiler.rs b/rust/cubetranspilers/src/validation_transpiler.rs new file mode 100644 index 0000000000000..8670c792175c6 --- /dev/null +++ b/rust/cubetranspilers/src/validation_transpiler.rs @@ -0,0 +1,70 @@ +use swc_core::common::errors::Handler; +use swc_core::common::BytePos; +use swc_core::common::Span; +use swc_core::ecma::visit::noop_visit_mut_type; +use swc_core::plugin::proxies::PluginSourceMapProxy; +use swc_core::{ + atoms::JsWord, + ecma::{ast::*, visit::VisitMut}, +}; + +pub struct ValidationTransformVisitor<'a> { + pub(crate) source_map: Option, + handler: &'a Handler, +} + +impl<'a> ValidationTransformVisitor<'a> { + pub fn new(source_map: Option, handler: &'a Handler) -> Self { + ValidationTransformVisitor { + source_map, + handler, + } + } + + fn emit_warn(&self, span: Span, message: &str) { + self.handler + .struct_span_warn(span, &self.format_msg(span, message)) + .emit(); + } + + fn format_msg(&self, span: Span, message: &str) -> String { + if let Some(ref sm) = self.source_map { + if let Some(source_file) = sm.source_file.get() { + let loc = source_file.lookup_line(span.lo()).unwrap_or(0); + let column = span.lo() - source_file.line_begin_pos(BytePos(loc as u32)); + format!( + "{}. Found in {}:{}:{}", + message, + source_file.name, + loc + 1, + column.0, + ) + } else { + message.to_string() + } + } else { + message.to_string() + } + } +} + +impl VisitMut for ValidationTransformVisitor<'_> { + // Implement necessary visit_mut_* methods for actual custom transform. + // A comprehensive list of possible visitor methods can be found here: + // https://rustdoc.swc.rs/swc_ecma_visit/trait.VisitMut.html + + // We don't need to do anything besides checking identifiers here + noop_visit_mut_type!(); + + fn visit_mut_ident(&mut self, ident: &mut Ident) { + let uc_id: JsWord = "USER_CONTEXT".into(); + if ident.sym == uc_id { + self.emit_warn( + ident.span, + "Support for USER_CONTEXT was removed, please migrate to SECURITY_CONTEXT", + ); + // TODO: How to report the errors? + // @see https://rustdoc.swc.rs/swc_common/errors/struct.Handler.html + } + } +} diff --git a/rust/cubetranspilers/tests/check_dup_prop_test.rs b/rust/cubetranspilers/tests/check_dup_prop_test.rs new file mode 100644 index 0000000000000..6900153a898f8 --- /dev/null +++ b/rust/cubetranspilers/tests/check_dup_prop_test.rs @@ -0,0 +1,273 @@ +mod common; + +// Recommended strategy to test plugin's transform is verify +// the Visitor's behavior, instead of trying to run `process_transform` with mocks +// unless explicitly required to do so. + +use std::sync::{Arc, Mutex}; + +use common::TestEmitter; +use cubetranspilers::check_dup_prop_transpiler::*; +use swc_core::ecma::ast::{EsVersion, Program}; +use swc_core::{ + common::{ + errors::{Handler, HandlerFlags}, + sync::Lrc, + FileName, SourceMap, + }, + ecma::visit::VisitMutWith, +}; +use swc_ecma_parser::{lexer::Lexer, Parser, StringInput, Syntax}; + +#[test] +fn test_errors_for_duplicates_first_level() { + let cm: Lrc = Default::default(); + let diagnostics = Arc::new(Mutex::new(Vec::new())); + let emitter = Box::new(TestEmitter { + diagnostics: diagnostics.clone(), + }); + let handler = Handler::with_emitter_and_flags( + emitter, + HandlerFlags { + can_emit_warnings: true, + ..Default::default() + }, + ); + + let js_code = r#" + cube(`cube1`, { + sql: `SELECT * FROM table`, + + dimensions: { + id: { + sql: `id`, + type: `number`, + primary_key: true, + }, + created_at: { + sql: `created_at`, + type: `time`, + }, + dim1Number: { + sql: `dim1Number`, + type: `number`, + }, + dim2Number: { + sql: `dim2Number`, + type: `number`, + }, + }, + + dimensions: { + dim2Number: { + sql: `dim2Number`, + type: `number`, + }, + }, + + measures: { + count: { + type: `count`, + sql: `id`, + }, + measureDim1: { + sql: `dim1Number`, + type: + `max`, + }, + measureDim2: { + sql: `dim1Number`, + type: `min`, + }, + }, + }); + "#; + + let fm = cm.new_source_file( + Arc::new(FileName::Custom("input.js".into())), + js_code.into(), + ); + let lexer = Lexer::new( + Syntax::Es(Default::default()), + EsVersion::Es2020, + StringInput::from(&*fm), + None, + ); + let mut parser = Parser::new_from(lexer); + let mut program: Program = parser.parse_program().expect("Failed to parse the JS code"); + + let mut visitor = CheckDupPropTransformVisitor::new(None, &handler); + program.visit_mut_with(&mut visitor); + + let diags = diagnostics.lock().unwrap(); + let msgs: Vec<_> = diags + .iter() + .filter(|msg| msg.contains("Duplicate property")) + .collect(); + assert!(msgs.len() == 1, "Should emit errors",); +} + +#[test] +fn test_errors_for_duplicates_deep_level() { + let cm: Lrc = Default::default(); + let diagnostics = Arc::new(Mutex::new(Vec::new())); + let emitter = Box::new(TestEmitter { + diagnostics: diagnostics.clone(), + }); + let handler = Handler::with_emitter_and_flags( + emitter, + HandlerFlags { + can_emit_warnings: true, + ..Default::default() + }, + ); + + let js_code = r#" + cube(`cube1`, { + sql: `SELECT * FROM table`, + + dimensions: { + id: { + sql: `id`, + type: `number`, + primary_key: true, + }, + created_at: { + sql: `created_at`, + type: `time`, + }, + dim1Number: { + sql: `dim1Number`, + type: `number`, + }, + dim1Number: { + sql: `dim2Number`, + type: `number`, + }, + }, + + dimensions: { + dim2Number: { + sql: `dim2Number`, + type: `number`, + }, + }, + + measures: { + count: { + type: `count`, + sql: `id`, + }, + measureDim1: { + sql: `dim1Number`, + type: + `max`, + }, + measureDim1: { + sql: `dim1Number`, + type: `min`, + }, + }, + }); + "#; + + let fm = cm.new_source_file( + Arc::new(FileName::Custom("input.js".into())), + js_code.into(), + ); + let lexer = Lexer::new( + Syntax::Es(Default::default()), + EsVersion::Es2020, + StringInput::from(&*fm), + None, + ); + let mut parser = Parser::new_from(lexer); + let mut program: Program = parser.parse_program().expect("Failed to parse the JS code"); + + let mut visitor = CheckDupPropTransformVisitor::new(None, &handler); + program.visit_mut_with(&mut visitor); + + let diags = diagnostics.lock().unwrap(); + let msgs: Vec<_> = diags + .iter() + .filter(|msg| msg.contains("Duplicate property")) + .collect(); + assert!(msgs.len() == 3, "Should emit errors",); +} + +#[test] +fn test_no_errors() { + let cm: Lrc = Default::default(); + let diagnostics = Arc::new(Mutex::new(Vec::new())); + let emitter = Box::new(TestEmitter { + diagnostics: diagnostics.clone(), + }); + let handler = Handler::with_emitter_and_flags( + emitter, + HandlerFlags { + can_emit_warnings: true, + ..Default::default() + }, + ); + + let js_code = r#" + cube(`cube1`, { + sql: `SELECT * FROM table`, + + dimensions: { + id: { + sql: `id`, + type: `number`, + primary_key: true, + }, + created_at: { + sql: `created_at`, + type: `time`, + }, + dim1Number: { + sql: `dim1Number`, + type: `number`, + }, + dim2Number: { + sql: `dim2Number`, + type: `number`, + }, + }, + + measures: { + count: { + type: `count`, + sql: `id`, + }, + measureDim1: { + sql: `dim1Number`, + type: + `max`, + }, + measureDim2: { + sql: `dim1Number`, + type: `min`, + }, + }, + }); + "#; + + let fm = cm.new_source_file( + Arc::new(FileName::Custom("input.js".into())), + js_code.into(), + ); + let lexer = Lexer::new( + Syntax::Es(Default::default()), + EsVersion::Es2020, + StringInput::from(&*fm), + None, + ); + let mut parser = Parser::new_from(lexer); + let mut program: Program = parser.parse_program().expect("Failed to parse the JS code"); + + let mut visitor = CheckDupPropTransformVisitor::new(None, &handler); + program.visit_mut_with(&mut visitor); + + let diags = diagnostics.lock().unwrap(); + assert!(diags.is_empty(), "Should not emit errors",); +} diff --git a/rust/cubetranspilers/tests/common/mod.rs b/rust/cubetranspilers/tests/common/mod.rs new file mode 100644 index 0000000000000..42f2897aee0b2 --- /dev/null +++ b/rust/cubetranspilers/tests/common/mod.rs @@ -0,0 +1,34 @@ +use std::sync::{Arc, Arc as Lrc, Mutex}; +use swc_core::common::errors::{DiagnosticBuilder, Emitter}; +use swc_core::common::SourceMap; +use swc_core::ecma::ast::Program; +use swc_ecma_codegen::text_writer::JsWriter; +use swc_ecma_codegen::{Config, Emitter as CodeEmitter}; + +pub struct TestEmitter { + pub diagnostics: Arc>>, +} + +impl Emitter for TestEmitter { + fn emit(&mut self, diagnostic: &DiagnosticBuilder) { + let mut diags = self.diagnostics.lock().unwrap(); + diags.push(diagnostic.message()); + } +} + +#[allow(dead_code)] +pub fn generate_code(program: &Program, cm: &Lrc) -> String { + let mut buf = vec![]; + { + let mut emitter = CodeEmitter { + cfg: Config::default(), + comments: None, + wr: JsWriter::new(cm.clone(), "\n", &mut buf, None), + cm: cm.clone(), + }; + emitter + .emit_program(program) + .expect("Failed to generate code"); + } + String::from_utf8(buf).expect("Invalid UTF8") +} diff --git a/rust/cubetranspilers/tests/cube_prop_ctx_test.rs b/rust/cubetranspilers/tests/cube_prop_ctx_test.rs new file mode 100644 index 0000000000000..1203fd54c94e1 --- /dev/null +++ b/rust/cubetranspilers/tests/cube_prop_ctx_test.rs @@ -0,0 +1,833 @@ +mod common; + +// Recommended strategy to test plugin's transform is verify +// the Visitor's behavior, instead of trying to run `process_transform` with mocks +// unless explicitly required to do so. + +use std::collections::{HashMap, HashSet}; +use std::sync::{Arc, LazyLock, Mutex}; + +use common::{generate_code, TestEmitter}; +use cubetranspilers::cube_prop_ctx_transpiler::*; +use insta::assert_snapshot; +use swc_core::ecma::ast::{EsVersion, Program}; +use swc_core::{ + common::{ + errors::{Handler, HandlerFlags}, + sync::Lrc, + FileName, SourceMap, + }, + ecma::visit::VisitMutWith, +}; +use swc_ecma_parser::{lexer::Lexer, Parser, StringInput, Syntax}; + +static CONTEXT_SYMBOLS: LazyLock> = LazyLock::new(|| { + let mut map = HashMap::new(); + map.insert( + "SECURITY_CONTEXT".to_string(), + "securityContext".to_string(), + ); + map.insert( + "security_context".to_string(), + "securityContext".to_string(), + ); + map.insert("securityContext".to_string(), "securityContext".to_string()); + map.insert("FILTER_PARAMS".to_string(), "filterParams".to_string()); + map.insert("FILTER_GROUP".to_string(), "filterGroup".to_string()); + map.insert("SQL_UTILS".to_string(), "sqlUtils".to_string()); + map +}); + +#[test] +fn test_incorrect_args_to_cube() { + let cm: Lrc = Default::default(); + let diagnostics = Arc::new(Mutex::new(Vec::new())); + let emitter = Box::new(TestEmitter { + diagnostics: diagnostics.clone(), + }); + let handler = Handler::with_emitter_and_flags( + emitter, + HandlerFlags { + can_emit_warnings: true, + ..Default::default() + }, + ); + + let js_code = r#" + cube(`cube1`, { sql: `xxx` }, 25); + "#; + + let fm = cm.new_source_file( + Arc::new(FileName::Custom("input.js".into())), + js_code.into(), + ); + let lexer = Lexer::new( + Syntax::Es(Default::default()), + EsVersion::Es2020, + StringInput::from(&*fm), + None, + ); + let mut parser = Parser::new_from(lexer); + let mut program: Program = parser.parse_program().expect("Failed to parse the JS code"); + + let mut visitor = CubePropTransformVisitor::new( + HashSet::new(), + HashMap::new(), + HashMap::new(), + None, + &handler, + ); + program.visit_mut_with(&mut visitor); + + let _output_code = generate_code(&program, &cm); + let diags = diagnostics.lock().unwrap(); + let msgs: Vec<_> = diags + .iter() + .filter(|msg| msg.contains("Incorrect number of arguments")) + .collect(); + assert!(msgs.len() > 0, "Should emit errors",); +} + +#[test] +fn test_simple_transform() { + let cm: Lrc = Default::default(); + let diagnostics = Arc::new(Mutex::new(Vec::new())); + let emitter = Box::new(TestEmitter { + diagnostics: diagnostics.clone(), + }); + let handler = Handler::with_emitter_and_flags( + emitter, + HandlerFlags { + can_emit_warnings: true, + ..Default::default() + }, + ); + + let js_code = r#" + cube(`cube1`, { + sql: `SELECT * FROM table`, + + dimensions: { + id: { + sql: `id`, + type: `number`, + primary_key: true, + }, + created_at: { + sql: `created_at`, + type: `time`, + }, + dim1Number: { + sql: `dim1Number`, + type: `number`, + }, + dim2Number: { + sql: `dim2Number`, + type: `number`, + }, + }, + + measures: { + count: { + type: `count`, + sql: `id`, + }, + measureDim1: { + sql: `dim1Number`, + type: + `max`, + }, + measureDim2: { + sql: `dim1Number`, + type: `min`, + }, + }, + }); + "#; + // Should generate + // cube(`cube1`, { + // sql: () => `SELECT * + // FROM table`, + // dimensions: { + // id: { + // sql: () => `id`, + // type: `number`, + // primary_key: true + // }, + // created_at: { + // sql: () => `created_at`, + // type: `time` + // }, + // dim1Number: { + // sql: () => `dim1Number`, + // type: `number` + // }, + // dim2Number: { + // sql: () => `dim2Number`, + // type: `number` + // } + // }, + // measures: { + // count: { + // type: `count`, + // sql: () => `id` + // }, + // measureDim1: { + // sql: () => `dim1Number`, + // type: `max` + // }, + // measureDim2: { + // sql: () => `dim1Number`, + // type: `min` + // } + // } + // }); + + let fm = cm.new_source_file( + Arc::new(FileName::Custom("input.js".into())), + js_code.into(), + ); + let lexer = Lexer::new( + Syntax::Es(Default::default()), + EsVersion::Es2020, + StringInput::from(&*fm), + None, + ); + let mut parser = Parser::new_from(lexer); + let mut program: Program = parser.parse_program().expect("Failed to parse the JS code"); + + let mut visitor = CubePropTransformVisitor::new( + HashSet::new(), + HashMap::new(), + HashMap::new(), + None, + &handler, + ); + program.visit_mut_with(&mut visitor); + + let output_code = generate_code(&program, &cm); + + assert_snapshot!("simple_transform", output_code); + + let diags = diagnostics.lock().unwrap(); + assert!( + diags.is_empty(), + "Should not emit errors, got: {:?}", + *diags + ); +} + +#[test] +fn test_complicated_transform_1st_stage() { + let cm: Lrc = Default::default(); + let diagnostics = Arc::new(Mutex::new(Vec::new())); + let emitter = Box::new(TestEmitter { + diagnostics: diagnostics.clone(), + }); + let handler = Handler::with_emitter_and_flags( + emitter, + HandlerFlags { + can_emit_warnings: true, + ..Default::default() + }, + ); + + let js_code = r#" + cube(`Orders`, { + sql: ` + SELECT * + FROM public.orders + WHERE ${FILTER_GROUP( + FILTER_PARAMS.Orders.status.filter('status') + )} + `, + preAggregations: { + main_test_range: { + measures: [count, rolling_count_month], + dimensions: [status], + timeDimension: createdAt, + granularity: `day`, + partitionGranularity: `month`, + refreshKey: { + every: `1 day`, + }, + buildRangeStart: { + sql: `SELECT '2021-01-01'::DATE` + }, + build_range_end: { + sql: `SELECT '2021-12-31'::DATE` + } + + } + }, + measures: { + division_error_test: { + sql: `CASE WHEN ${zero_sum} = 0 THEN 1 ELSE 1/${zero_sum} end`, + type: `sum` + }, + zero_sum: { + sql: `id`, + type: `sum` + }, + rolling_count_month: { + sql: `id`, + type: `count`, + rollingWindow: { + trailing: `1 month`, + }, + }, + count: { + type: `count`, + drillMembers: [id, createdAt], + meta: { + test: 1 + } + }, + countShipped: { + type: `count`, + filters: [{ + sql: `${CUBE}.status = 'shipped'` + }], + drillMembers: [id, createdAt] + }, + maxDate: { + type: `max`, + sql: `${CUBE.completedAt}`, + } + }, + dimensions: { + id: { + sql: `id`, + type: `number`, + primaryKey: true, + shown: true + }, + status: { + sql: `status`, + type: `string` + }, + createdAt: { + sql: `created_at`, + type: `time` + }, + completedAt: { + sql: `completed_at`, + type: `time` + }, + test_boolean: { + sql: `CASE WHEN status = 'completed' THEN TRUE ELSE FALSE END`, + type: `boolean` + }, + localTime: { + type: 'time', + sql: SQL_UTILS.convertTz(`completed_at`) + }, + localYear: { + type: 'number', + sql: `EXTRACT(year from ${SQL_UTILS.convertTz('completed_at')})` + }, + }, + segments: { + status_completed: { + sql: `${CUBE}.status = 'completed'` + } + }, + accessPolicy: [ + { + role: "*", + rowLevel: { + allowAll: true, + }, + }, + { + role: 'admin', + conditions: [ + { + if: `true`, + }, + ], + rowLevel: { + filters: [ + { + member: `${CUBE}.id`, + operator: 'equals', + values: [`1`, `2`, `3`], + }, + ], + }, + memberLevel: { + includes: `*`, + excludes: [`localTime`, `completedAt`], + }, + }, + ] + }); + "#; + // Should generate + // cube(`Orders`, { + // sql: (FILTER_GROUP, FILTER_PARAMS) => ` + // SELECT * + // FROM public.orders + // WHERE ${FILTER_GROUP(FILTER_PARAMS.Orders.status.filter('status'))} + // `, + // preAggregations: { + // main_test_range: { + // measures: () => [count, rolling_count_month], + // dimensions: () => [status], + // timeDimension: () => createdAt, + // granularity: `day`, + // partitionGranularity: `month`, + // refreshKey: { + // every: `1 day`, + // }, + // buildRangeStart: { + // sql: () => `SELECT '2021-01-01'::DATE`, + // }, + // build_range_end: { + // sql: () => `SELECT '2021-12-31'::DATE`, + // }, + // }, + // }, + // measures: { + // division_error_test: { + // sql: () => `CASE WHEN ${zero_sum} = 0 THEN 1 ELSE 1/${zero_sum} end`, + // type: `sum`, + // }, + // zero_sum: { + // sql: () => `id`, + // type: `sum`, + // }, + // rolling_count_month: { + // sql: () => `id`, + // type: `count`, + // rollingWindow: { + // trailing: `1 month`, + // }, + // }, + // count: { + // type: `count`, + // drillMembers: () => [id, createdAt], + // meta: { + // test: 1, + // }, + // }, + // countShipped: { + // type: `count`, + // filters: [{ + // sql: CUBE => `${CUBE}.status = 'shipped'`, + // }], + // drillMembers: () => [id, createdAt], + // }, + // maxDate: { + // type: `max`, + // sql: CUBE => `${CUBE.completedAt}`, + // }, + // }, + // dimensions: { + // id: { + // sql: () => `id`, + // type: `number`, + // primaryKey: true, + // shown: true, + // }, + // status: { + // sql: () => `status`, + // type: `string`, + // }, + // createdAt: { + // sql: () => `created_at`, + // type: `time`, + // }, + // completedAt: { + // sql: () => `completed_at`, + // type: `time`, + // }, + // test_boolean: { + // sql: () => `CASE WHEN status = 'completed' THEN TRUE ELSE FALSE END`, + // type: `boolean`, + // }, + // localTime: { + // type: 'time', + // sql: SQL_UTILS => SQL_UTILS.convertTz(`completed_at`), + // }, + // localYear: { + // type: 'number', + // sql: SQL_UTILS => `EXTRACT(year from ${SQL_UTILS.convertTz('completed_at')})`, + // }, + // }, + // segments: { + // status_completed: { + // sql: CUBE => `${CUBE}.status = 'completed'`, + // }, + // }, + // accessPolicy: [{ + // role: "*", + // rowLevel: { + // allowAll: true + // } + // }, + // { + // role: 'admin', + // conditions: [{ + // if: () => `true` + // }], + // rowLevel: { + // filters: [{ + // member: CUBE => `${CUBE}.id`, + // operator: 'equals', + // values: () => [`1`, `2`, `3`] + // }] + // }, + // memberLevel: { + // includes: `*`, + // excludes: [`localTime`, `completedAt`] + // } + // }] + // }); + + let fm = cm.new_source_file( + Arc::new(FileName::Custom("input.js".into())), + js_code.into(), + ); + let lexer = Lexer::new( + Syntax::Es(Default::default()), + EsVersion::Es2020, + StringInput::from(&*fm), + None, + ); + let mut parser = Parser::new_from(lexer); + let mut program: Program = parser.parse_program().expect("Failed to parse the JS code"); + + let mut visitor = CubePropTransformVisitor::new( + HashSet::new(), + HashMap::new(), + CONTEXT_SYMBOLS.clone(), + None, + &handler, + ); + + program.visit_mut_with(&mut visitor); + + let output_code = generate_code(&program, &cm); + + assert_snapshot!("complicated_transform_1st_stage", output_code); + + let diags = diagnostics.lock().unwrap(); + assert!( + diags.is_empty(), + "Should not emit errors, got: {:?}", + *diags + ); +} + +#[test] +fn test_complicated_transform_2nd_stage() { + let cm: Lrc = Default::default(); + let diagnostics = Arc::new(Mutex::new(Vec::new())); + let emitter = Box::new(TestEmitter { + diagnostics: diagnostics.clone(), + }); + let handler = Handler::with_emitter_and_flags( + emitter, + HandlerFlags { + can_emit_warnings: true, + ..Default::default() + }, + ); + + let js_code = r#" + cube(`Orders`, { + sql: (FILTER_GROUP, FILTER_PARAMS) => ` + SELECT * + FROM public.orders + WHERE ${FILTER_GROUP(FILTER_PARAMS.Orders.status.filter('status'))} + `, + preAggregations: { + main_test_range: { + measures: () => [count, rolling_count_month], + dimensions: () => [status], + timeDimension: () => createdAt, + granularity: `day`, + partitionGranularity: `month`, + refreshKey: { + every: `1 day` + }, + buildRangeStart: { + sql: () => `SELECT '2021-01-01'::DATE` + }, + build_range_end: { + sql: () => `SELECT '2021-12-31'::DATE` + } + } + }, + measures: { + division_error_test: { + sql: () => `CASE WHEN ${zero_sum} = 0 THEN 1 ELSE 1/${zero_sum} end`, + type: `sum` + }, + zero_sum: { + sql: () => `id`, + type: `sum` + }, + rolling_count_month: { + sql: () => `id`, + type: `count`, + rollingWindow: { + trailing: `1 month` + } + }, + count: { + type: `count`, + drillMembers: () => [id, createdAt], + meta: { + test: 1 + } + }, + countShipped: { + type: `count`, + filters: [{ + sql: CUBE => `${CUBE}.status = 'shipped'` + }], + drillMembers: () => [id, createdAt] + }, + maxDate: { + type: `max`, + sql: CUBE => `${CUBE.completedAt}` + } + }, + dimensions: { + id: { + sql: () => `id`, + type: `number`, + primaryKey: true, + shown: true + }, + status: { + sql: () => `status`, + type: `string` + }, + createdAt: { + sql: () => `created_at`, + type: `time` + }, + completedAt: { + sql: () => `completed_at`, + type: `time` + }, + test_boolean: { + sql: () => `CASE WHEN status = 'completed' THEN TRUE ELSE FALSE END`, + type: `boolean` + }, + localTime: { + type: 'time', + sql: SQL_UTILS => SQL_UTILS.convertTz(`completed_at`) + }, + localYear: { + type: 'number', + sql: SQL_UTILS => `EXTRACT(year from ${SQL_UTILS.convertTz('completed_at')})` + } + }, + segments: { + status_completed: { + sql: CUBE => `${CUBE}.status = 'completed'` + } + }, + accessPolicy: [{ + role: "*", + rowLevel: { + allowAll: true + } + }, + { + role: 'admin', + conditions: [{ + if: () => `true` + }], + rowLevel: { + filters: [{ + member: CUBE => `${CUBE}.id`, + operator: 'equals', + values: () => [`1`, `2`, `3`] + }] + }, + memberLevel: { + includes: `*`, + excludes: [`localTime`, `completedAt`] + } + }] + }); + "#; + // Should generate + // cube(`Orders`, { + // sql: (FILTER_GROUP, FILTER_PARAMS) => ` + // SELECT * + // FROM public.orders + // WHERE ${FILTER_GROUP(FILTER_PARAMS.Orders.status.filter('status'))} + // `, + // preAggregations: { + // main_test_range: { + // measures: (count, rolling_count_month) => [count, rolling_count_month], + // dimensions: status => [status], + // timeDimension: createdAt => createdAt, + // granularity: `day`, + // partitionGranularity: `month`, + // refreshKey: { + // every: `1 day` + // }, + // buildRangeStart: { + // sql: () => `SELECT '2021-01-01'::DATE` + // }, + // build_range_end: { + // sql: () => `SELECT '2021-12-31'::DATE` + // } + // } + // }, + // measures: { + // division_error_test: { + // sql: zero_sum => `CASE WHEN ${zero_sum} = 0 THEN 1 ELSE 1/${zero_sum} end`, + // type: `sum` + // }, + // zero_sum: { + // sql: () => `id`, + // type: `sum` + // }, + // rolling_count_month: { + // sql: () => `id`, + // type: `count`, + // rollingWindow: { + // trailing: `1 month` + // } + // }, + // count: { + // type: `count`, + // drillMembers: (id, createdAt) => [id, createdAt], + // meta: { + // test: 1 + // } + // }, + // countShipped: { + // type: `count`, + // filters: [{ + // sql: CUBE => `${CUBE}.status = 'shipped'` + // }], + // drillMembers: (id, createdAt) => [id, createdAt] + // }, + // maxDate: { + // type: `max`, + // sql: CUBE => `${CUBE.completedAt}` + // } + // }, + // dimensions: { + // id: { + // sql: () => `id`, + // type: `number`, + // primaryKey: true, + // shown: true + // }, + // status: { + // sql: () => `status`, + // type: `string` + // }, + // createdAt: { + // sql: () => `created_at`, + // type: `time` + // }, + // completedAt: { + // sql: () => `completed_at`, + // type: `time` + // }, + // test_boolean: { + // sql: () => `CASE WHEN status = 'completed' THEN TRUE ELSE FALSE END`, + // type: `boolean` + // }, + // localTime: { + // type: 'time', + // sql: SQL_UTILS => SQL_UTILS.convertTz(`completed_at`) + // }, + // localYear: { + // type: 'number', + // sql: SQL_UTILS => `EXTRACT(year from ${SQL_UTILS.convertTz('completed_at')})` + // } + // }, + // segments: { + // status_completed: { + // sql: CUBE => `${CUBE}.status = 'completed'` + // } + // }, + // accessPolicy: [{ + // role: "*", + // rowLevel: { + // allowAll: true + // } + // }, + // { + // role: 'admin', + // conditions: [{ + // if: () => `true` + // }], + // rowLevel: { + // filters: [{ + // member: CUBE => `${CUBE}.id`, + // operator: 'equals', + // values: () => [`1`, `2`, `3`] + // }] + // }, + // memberLevel: { + // includes: `*`, + // excludes: [`localTime`, `completedAt`] + // } + // }] + // }); + + let fm = cm.new_source_file( + Arc::new(FileName::Custom("input.js".into())), + js_code.into(), + ); + let lexer = Lexer::new( + Syntax::Es(Default::default()), + EsVersion::Es2020, + StringInput::from(&*fm), + None, + ); + let mut parser = Parser::new_from(lexer); + let mut program: Program = parser.parse_program().expect("Failed to parse the JS code"); + let mut cube_names = HashSet::new(); + cube_names.insert("Orders".to_string()); + let mut cube_symbols = HashMap::>::new(); + let mut orders_cube_symbols = HashMap::new(); + orders_cube_symbols.insert("division_error_test".to_string(), true); + orders_cube_symbols.insert("zero_sum".to_string(), true); + orders_cube_symbols.insert("rolling_count_month".to_string(), true); + orders_cube_symbols.insert("count".to_string(), true); + orders_cube_symbols.insert("countShipped".to_string(), true); + orders_cube_symbols.insert("id".to_string(), true); + orders_cube_symbols.insert("status".to_string(), true); + orders_cube_symbols.insert("createdAt".to_string(), true); + orders_cube_symbols.insert("completedAt".to_string(), true); + orders_cube_symbols.insert("test_boolean".to_string(), true); + orders_cube_symbols.insert("localTime".to_string(), true); + orders_cube_symbols.insert("localYear".to_string(), true); + orders_cube_symbols.insert("status_completed".to_string(), true); + orders_cube_symbols.insert("main_test_range".to_string(), true); + cube_symbols.insert("Orders".to_string(), orders_cube_symbols); + + let mut visitor = CubePropTransformVisitor::new( + cube_names, + cube_symbols, + CONTEXT_SYMBOLS.clone(), + None, + &handler, + ); + program.visit_mut_with(&mut visitor); + + let output_code = generate_code(&program, &cm); + + assert_snapshot!("complicated_transform_2nd_stage", output_code); + + let diags = diagnostics.lock().unwrap(); + assert!( + diags.is_empty(), + "Should not emit errors, got: {:?}", + *diags + ); +} diff --git a/rust/cubetranspilers/tests/import_export_test.rs b/rust/cubetranspilers/tests/import_export_test.rs new file mode 100644 index 0000000000000..7e4389ab8d1eb --- /dev/null +++ b/rust/cubetranspilers/tests/import_export_test.rs @@ -0,0 +1,365 @@ +mod common; + +// Recommended strategy to test plugin's transform is verify +// the Visitor's behavior, instead of trying to run `process_transform` with mocks +// unless explicitly required to do so. + +use std::sync::{Arc, Mutex}; + +use common::{generate_code, TestEmitter}; +use cubetranspilers::import_export_transpiler::*; +use insta::assert_snapshot; +use swc_core::ecma::ast::{EsVersion, Program}; +use swc_core::{ + common::{ + errors::{Handler, HandlerFlags}, + sync::Lrc, + FileName, SourceMap, + }, + ecma::visit::VisitMutWith, +}; +use swc_ecma_parser::{lexer::Lexer, Parser, StringInput, Syntax}; + +#[test] +fn test_export_default_declaration() { + let cm: Lrc = Default::default(); + let diagnostics = Arc::new(Mutex::new(Vec::new())); + let emitter = Box::new(TestEmitter { + diagnostics: diagnostics.clone(), + }); + let handler = Handler::with_emitter_and_flags( + emitter, + HandlerFlags { + can_emit_warnings: true, + ..Default::default() + }, + ); + + let js_code = r#" + export default function exp() { console.log('exported function'); }; + "#; + + let fm = cm.new_source_file( + Arc::new(FileName::Custom("input.js".into())), + js_code.into(), + ); + let lexer = Lexer::new( + Syntax::Es(Default::default()), + EsVersion::Es2020, + StringInput::from(&*fm), + None, + ); + let mut parser = Parser::new_from(lexer); + let mut program: Program = parser.parse_program().expect("Failed to parse the JS code"); + + let mut visitor = ImportExportTransformVisitor::new(None, &handler); + program.visit_mut_with(&mut visitor); + + let output_code = generate_code(&program, &cm); + + assert_snapshot!("export_default_declaration", output_code); + + let diags = diagnostics.lock().unwrap(); + assert!( + diags.is_empty(), + "Should not emit errors, got: {:?}", + *diags + ); +} + +#[test] +fn test_export_default_expression() { + let cm: Lrc = Default::default(); + let diagnostics = Arc::new(Mutex::new(Vec::new())); + let emitter = Box::new(TestEmitter { + diagnostics: diagnostics.clone(), + }); + let handler = Handler::with_emitter_and_flags( + emitter, + HandlerFlags { + can_emit_warnings: true, + ..Default::default() + }, + ); + + let js_code = r#" + let myVar = 5; + export default myVar; + "#; + + let fm = cm.new_source_file( + Arc::new(FileName::Custom("input.js".into())), + js_code.into(), + ); + let lexer = Lexer::new( + Syntax::Es(Default::default()), + EsVersion::Es2020, + StringInput::from(&*fm), + None, + ); + let mut parser = Parser::new_from(lexer); + let mut program: Program = parser.parse_program().expect("Failed to parse the JS code"); + + let mut visitor = ImportExportTransformVisitor::new(None, &handler); + program.visit_mut_with(&mut visitor); + + let output_code = generate_code(&program, &cm); + + assert_snapshot!("export_default_expression", output_code); + + let diags = diagnostics.lock().unwrap(); + assert!( + diags.is_empty(), + "Should not emit errors, got: {:?}", + *diags + ); +} + +#[test] +fn test_export_const_expression() { + let cm: Lrc = Default::default(); + let diagnostics = Arc::new(Mutex::new(Vec::new())); + let emitter = Box::new(TestEmitter { + diagnostics: diagnostics.clone(), + }); + let handler = Handler::with_emitter_and_flags( + emitter, + HandlerFlags { + can_emit_warnings: true, + ..Default::default() + }, + ); + + let js_code = r#" + export const sql = (input) => intput + 5; + export const a1 = 5, a2 = ()=>111, a3 = (inputA3)=>inputA3+"Done"; + "#; + + let fm = cm.new_source_file( + Arc::new(FileName::Custom("input.js".into())), + js_code.into(), + ); + let lexer = Lexer::new( + Syntax::Es(Default::default()), + EsVersion::Es2020, + StringInput::from(&*fm), + None, + ); + let mut parser = Parser::new_from(lexer); + let mut program: Program = parser.parse_program().expect("Failed to parse the JS code"); + + let mut visitor = ImportExportTransformVisitor::new(None, &handler); + program.visit_mut_with(&mut visitor); + + let output_code = generate_code(&program, &cm); + + assert_snapshot!("export_const_expression", output_code); + + let diags = diagnostics.lock().unwrap(); + assert!( + diags.is_empty(), + "Should not emit errors, got: {:?}", + *diags + ); +} + +#[test] +fn test_import_named_default() { + let cm: Lrc = Default::default(); + let diagnostics = Arc::new(Mutex::new(Vec::new())); + let emitter = Box::new(TestEmitter { + diagnostics: diagnostics.clone(), + }); + let handler = Handler::with_emitter_and_flags( + emitter, + HandlerFlags { + can_emit_warnings: true, + ..Default::default() + }, + ); + + let js_code = r#" + import def, { foo, bar as baz } from "module"; + "#; + // Should generate + // const def = require("module"), foo = require("module").foo, baz = require("module").bar; + // + + let fm = cm.new_source_file( + Arc::new(FileName::Custom("import.js".into())), + js_code.into(), + ); + let lexer = Lexer::new( + Syntax::Es(Default::default()), + EsVersion::Es2020, + StringInput::from(&*fm), + None, + ); + let mut parser = Parser::new_from(lexer); + let mut program: Program = parser.parse_program().expect("Failed to parse the JS code"); + + let mut visitor = ImportExportTransformVisitor::new(None, &handler); + program.visit_mut_with(&mut visitor); + + let output_code = generate_code(&program, &cm); + + assert_snapshot!("import_named_default", output_code); + + let diags = diagnostics.lock().unwrap(); + assert!( + diags.is_empty(), + "Should not emit errors, got: {:?}", + *diags + ); +} + +#[test] +fn test_namespace_import() { + let cm: Lrc = Default::default(); + let diagnostics = Arc::new(Mutex::new(Vec::new())); + let emitter = Box::new(TestEmitter { + diagnostics: diagnostics.clone(), + }); + let handler = Handler::with_emitter_and_flags( + emitter, + HandlerFlags { + can_emit_warnings: true, + ..Default::default() + }, + ); + + let js_code = r#" + import * as ns from "module"; + "#; + + let fm = cm.new_source_file( + Arc::new(FileName::Custom("ns_import.js".into())), + js_code.into(), + ); + let lexer = Lexer::new( + Syntax::Es(Default::default()), + EsVersion::Es2020, + StringInput::from(&*fm), + None, + ); + let mut parser = Parser::new_from(lexer); + let mut program: Program = parser.parse_program().expect("Failed to parse the JS code"); + + let mut visitor = ImportExportTransformVisitor::new(None, &handler); + program.visit_mut_with(&mut visitor); + + let diags = diagnostics.lock().unwrap(); + let errors: Vec<_> = diags + .iter() + .filter(|msg| msg.contains("Namespace import not supported")) + .collect(); + assert!( + !errors.is_empty(), + "Expected error for namespace import, got diagnostics: {:?}", + *diags + ); +} + +#[test] +fn test_export_named() { + let cm: Lrc = Default::default(); + let diagnostics = Arc::new(Mutex::new(Vec::new())); + let emitter = Box::new(TestEmitter { + diagnostics: diagnostics.clone(), + }); + let handler = Handler::with_emitter_and_flags( + emitter, + HandlerFlags { + can_emit_warnings: true, + ..Default::default() + }, + ); + + let js_code = r#" + export { foo, bar as baz }; + "#; + // Should generate: + // addExport({ + // foo: foo, + // baz: bar + // }); + + let fm = cm.new_source_file( + Arc::new(FileName::Custom("export_named.js".into())), + js_code.into(), + ); + let lexer = Lexer::new( + Syntax::Es(Default::default()), + EsVersion::Es2020, + StringInput::from(&*fm), + None, + ); + let mut parser = Parser::new_from(lexer); + let mut program: Program = parser.parse_program().expect("Failed to parse the JS code"); + + let mut visitor = ImportExportTransformVisitor::new(None, &handler); + program.visit_mut_with(&mut visitor); + + let output_code = generate_code(&program, &cm); + + assert_snapshot!("export_named", output_code); + + let diags = diagnostics.lock().unwrap(); + assert!( + diags.is_empty(), + "Should not emit errors, got: {:?}", + *diags + ); +} + +#[test] +fn test_export_default_ts_interface() { + let cm: Lrc = Default::default(); + let diagnostics = Arc::new(Mutex::new(Vec::new())); + let emitter = Box::new(TestEmitter { + diagnostics: diagnostics.clone(), + }); + let handler = Handler::with_emitter_and_flags( + emitter, + HandlerFlags { + can_emit_warnings: true, + ..Default::default() + }, + ); + + let js_code = r#" + export default interface Foo {} + "#; + + let fm = cm.new_source_file( + Arc::new(FileName::Custom("export_default_ts_interface.ts".into())), + js_code.into(), + ); + let lexer = Lexer::new( + Syntax::Typescript(Default::default()), + EsVersion::Es2020, + StringInput::from(&*fm), + None, + ); + let mut parser = Parser::new_from(lexer); + let mut program: Program = parser.parse_program().expect("Failed to parse the TS code"); + + let mut visitor = ImportExportTransformVisitor::new(None, &handler); + program.visit_mut_with(&mut visitor); + + let output_code = generate_code(&program, &cm); + // When exporting a TS interface, setExport is called with null as a fallback. + + assert_snapshot!("export_default_ts_interface", output_code); + + let diags = diagnostics.lock().unwrap(); + let errors: Vec<_> = diags + .iter() + .filter(|msg| msg.contains("Unsupported default export declaration")) + .collect(); + assert!( + !errors.is_empty(), + "Expected error for TS interface default export, got diagnostics: {:?}", + *diags + ); +} diff --git a/rust/cubetranspilers/tests/snapshots/cube_prop_ctx_test__complicated_transform_1st_stage.snap b/rust/cubetranspilers/tests/snapshots/cube_prop_ctx_test__complicated_transform_1st_stage.snap new file mode 100644 index 0000000000000..4fc109996c6db --- /dev/null +++ b/rust/cubetranspilers/tests/snapshots/cube_prop_ctx_test__complicated_transform_1st_stage.snap @@ -0,0 +1,150 @@ +--- +source: tests/cube_prop_ctx_test.rs +expression: output_code +--- +cube(`Orders`, { + sql: (FILTER_GROUP, FILTER_PARAMS)=>` + SELECT * + FROM public.orders + WHERE ${FILTER_GROUP(FILTER_PARAMS.Orders.status.filter('status'))} + `, + preAggregations: { + main_test_range: { + measures: ()=>[ + count, + rolling_count_month + ], + dimensions: ()=>[ + status + ], + timeDimension: ()=>createdAt, + granularity: `day`, + partitionGranularity: `month`, + refreshKey: { + every: `1 day` + }, + buildRangeStart: { + sql: ()=>`SELECT '2021-01-01'::DATE` + }, + build_range_end: { + sql: ()=>`SELECT '2021-12-31'::DATE` + } + } + }, + measures: { + division_error_test: { + sql: ()=>`CASE WHEN ${zero_sum} = 0 THEN 1 ELSE 1/${zero_sum} end`, + type: `sum` + }, + zero_sum: { + sql: ()=>`id`, + type: `sum` + }, + rolling_count_month: { + sql: ()=>`id`, + type: `count`, + rollingWindow: { + trailing: `1 month` + } + }, + count: { + type: `count`, + drillMembers: ()=>[ + id, + createdAt + ], + meta: { + test: 1 + } + }, + countShipped: { + type: `count`, + filters: [ + { + sql: (CUBE)=>`${CUBE}.status = 'shipped'` + } + ], + drillMembers: ()=>[ + id, + createdAt + ] + }, + maxDate: { + type: `max`, + sql: (CUBE)=>`${CUBE.completedAt}` + } + }, + dimensions: { + id: { + sql: ()=>`id`, + type: `number`, + primaryKey: true, + shown: true + }, + status: { + sql: ()=>`status`, + type: `string` + }, + createdAt: { + sql: ()=>`created_at`, + type: `time` + }, + completedAt: { + sql: ()=>`completed_at`, + type: `time` + }, + test_boolean: { + sql: ()=>`CASE WHEN status = 'completed' THEN TRUE ELSE FALSE END`, + type: `boolean` + }, + localTime: { + type: 'time', + sql: (SQL_UTILS)=>SQL_UTILS.convertTz(`completed_at`) + }, + localYear: { + type: 'number', + sql: (SQL_UTILS)=>`EXTRACT(year from ${SQL_UTILS.convertTz('completed_at')})` + } + }, + segments: { + status_completed: { + sql: (CUBE)=>`${CUBE}.status = 'completed'` + } + }, + accessPolicy: [ + { + role: "*", + rowLevel: { + allowAll: true + } + }, + { + role: 'admin', + conditions: [ + { + if: ()=>`true` + } + ], + rowLevel: { + filters: [ + { + member: (CUBE)=>`${CUBE}.id`, + operator: 'equals', + values: ()=>[ + `1`, + `2`, + `3` + ] + } + ] + }, + memberLevel: { + includes: `*`, + excludes: [ + `localTime`, + `completedAt` + ] + } + } + ] +}); diff --git a/rust/cubetranspilers/tests/snapshots/cube_prop_ctx_test__complicated_transform_2nd_stage.snap b/rust/cubetranspilers/tests/snapshots/cube_prop_ctx_test__complicated_transform_2nd_stage.snap new file mode 100644 index 0000000000000..82c5fa1e29911 --- /dev/null +++ b/rust/cubetranspilers/tests/snapshots/cube_prop_ctx_test__complicated_transform_2nd_stage.snap @@ -0,0 +1,150 @@ +--- +source: tests/cube_prop_ctx_test.rs +expression: output_code +--- +cube(`Orders`, { + sql: (FILTER_GROUP, FILTER_PARAMS)=>` + SELECT * + FROM public.orders + WHERE ${FILTER_GROUP(FILTER_PARAMS.Orders.status.filter('status'))} + `, + preAggregations: { + main_test_range: { + measures: (count, rolling_count_month)=>[ + count, + rolling_count_month + ], + dimensions: (status)=>[ + status + ], + timeDimension: (createdAt)=>createdAt, + granularity: `day`, + partitionGranularity: `month`, + refreshKey: { + every: `1 day` + }, + buildRangeStart: { + sql: ()=>`SELECT '2021-01-01'::DATE` + }, + build_range_end: { + sql: ()=>`SELECT '2021-12-31'::DATE` + } + } + }, + measures: { + division_error_test: { + sql: (zero_sum)=>`CASE WHEN ${zero_sum} = 0 THEN 1 ELSE 1/${zero_sum} end`, + type: `sum` + }, + zero_sum: { + sql: ()=>`id`, + type: `sum` + }, + rolling_count_month: { + sql: ()=>`id`, + type: `count`, + rollingWindow: { + trailing: `1 month` + } + }, + count: { + type: `count`, + drillMembers: (id, createdAt)=>[ + id, + createdAt + ], + meta: { + test: 1 + } + }, + countShipped: { + type: `count`, + filters: [ + { + sql: (CUBE)=>`${CUBE}.status = 'shipped'` + } + ], + drillMembers: (id, createdAt)=>[ + id, + createdAt + ] + }, + maxDate: { + type: `max`, + sql: (CUBE)=>`${CUBE.completedAt}` + } + }, + dimensions: { + id: { + sql: ()=>`id`, + type: `number`, + primaryKey: true, + shown: true + }, + status: { + sql: ()=>`status`, + type: `string` + }, + createdAt: { + sql: ()=>`created_at`, + type: `time` + }, + completedAt: { + sql: ()=>`completed_at`, + type: `time` + }, + test_boolean: { + sql: ()=>`CASE WHEN status = 'completed' THEN TRUE ELSE FALSE END`, + type: `boolean` + }, + localTime: { + type: 'time', + sql: (SQL_UTILS)=>SQL_UTILS.convertTz(`completed_at`) + }, + localYear: { + type: 'number', + sql: (SQL_UTILS)=>`EXTRACT(year from ${SQL_UTILS.convertTz('completed_at')})` + } + }, + segments: { + status_completed: { + sql: (CUBE)=>`${CUBE}.status = 'completed'` + } + }, + accessPolicy: [ + { + role: "*", + rowLevel: { + allowAll: true + } + }, + { + role: 'admin', + conditions: [ + { + if: ()=>`true` + } + ], + rowLevel: { + filters: [ + { + member: (CUBE)=>`${CUBE}.id`, + operator: 'equals', + values: ()=>[ + `1`, + `2`, + `3` + ] + } + ] + }, + memberLevel: { + includes: `*`, + excludes: [ + `localTime`, + `completedAt` + ] + } + } + ] +}); diff --git a/rust/cubetranspilers/tests/snapshots/cube_prop_ctx_test__simple_transform.snap b/rust/cubetranspilers/tests/snapshots/cube_prop_ctx_test__simple_transform.snap new file mode 100644 index 0000000000000..291d619e13990 --- /dev/null +++ b/rust/cubetranspilers/tests/snapshots/cube_prop_ctx_test__simple_transform.snap @@ -0,0 +1,40 @@ +--- +source: tests/cube_prop_ctx_test.rs +expression: output_code +--- +cube(`cube1`, { + sql: ()=>`SELECT * FROM table`, + dimensions: { + id: { + sql: ()=>`id`, + type: `number`, + primary_key: true + }, + created_at: { + sql: ()=>`created_at`, + type: `time` + }, + dim1Number: { + sql: ()=>`dim1Number`, + type: `number` + }, + dim2Number: { + sql: ()=>`dim2Number`, + type: `number` + } + }, + measures: { + count: { + type: `count`, + sql: ()=>`id` + }, + measureDim1: { + sql: ()=>`dim1Number`, + type: `max` + }, + measureDim2: { + sql: ()=>`dim1Number`, + type: `min` + } + } +}); diff --git a/rust/cubetranspilers/tests/snapshots/import_export_test__export_const_expression.snap b/rust/cubetranspilers/tests/snapshots/import_export_test__export_const_expression.snap new file mode 100644 index 0000000000000..b4ae3ffe5a765 --- /dev/null +++ b/rust/cubetranspilers/tests/snapshots/import_export_test__export_const_expression.snap @@ -0,0 +1,14 @@ +--- +source: tests/import_export_test.rs +expression: output_code +--- +const sql = (input)=>intput + 5; +addExport({ + sql: sql +}); +const a1 = 5, a2 = ()=>111, a3 = (inputA3)=>inputA3 + "Done"; +addExport({ + a1: a1, + a2: a2, + a3: a3 +}); diff --git a/rust/cubetranspilers/tests/snapshots/import_export_test__export_default_declaration.snap b/rust/cubetranspilers/tests/snapshots/import_export_test__export_default_declaration.snap new file mode 100644 index 0000000000000..2f4ca6461c826 --- /dev/null +++ b/rust/cubetranspilers/tests/snapshots/import_export_test__export_default_declaration.snap @@ -0,0 +1,8 @@ +--- +source: tests/import_export_test.rs +expression: output_code +--- +setExport(function() { + console.log('exported function'); +}); +; diff --git a/rust/cubetranspilers/tests/snapshots/import_export_test__export_default_expression.snap b/rust/cubetranspilers/tests/snapshots/import_export_test__export_default_expression.snap new file mode 100644 index 0000000000000..68db36c47cbad --- /dev/null +++ b/rust/cubetranspilers/tests/snapshots/import_export_test__export_default_expression.snap @@ -0,0 +1,6 @@ +--- +source: tests/import_export_test.rs +expression: output_code +--- +let myVar = 5; +setExport(myVar); diff --git a/rust/cubetranspilers/tests/snapshots/import_export_test__export_default_ts_interface.snap b/rust/cubetranspilers/tests/snapshots/import_export_test__export_default_ts_interface.snap new file mode 100644 index 0000000000000..a1467b7ed3ada --- /dev/null +++ b/rust/cubetranspilers/tests/snapshots/import_export_test__export_default_ts_interface.snap @@ -0,0 +1,5 @@ +--- +source: tests/import_export_test.rs +expression: output_code +--- +setExport(null); diff --git a/rust/cubetranspilers/tests/snapshots/import_export_test__export_named.snap b/rust/cubetranspilers/tests/snapshots/import_export_test__export_named.snap new file mode 100644 index 0000000000000..42d00a232f4b4 --- /dev/null +++ b/rust/cubetranspilers/tests/snapshots/import_export_test__export_named.snap @@ -0,0 +1,8 @@ +--- +source: tests/import_export_test.rs +expression: output_code +--- +addExport({ + foo: foo, + baz: bar +}); diff --git a/rust/cubetranspilers/tests/snapshots/import_export_test__import_named_default.snap b/rust/cubetranspilers/tests/snapshots/import_export_test__import_named_default.snap new file mode 100644 index 0000000000000..dd733cd77069d --- /dev/null +++ b/rust/cubetranspilers/tests/snapshots/import_export_test__import_named_default.snap @@ -0,0 +1,5 @@ +--- +source: tests/import_export_test.rs +expression: output_code +--- +const def = require("module"), foo = require("module").foo, baz = require("module").bar; diff --git a/rust/cubetranspilers/tests/validation_test.rs b/rust/cubetranspilers/tests/validation_test.rs new file mode 100644 index 0000000000000..c635f8e14d7f3 --- /dev/null +++ b/rust/cubetranspilers/tests/validation_test.rs @@ -0,0 +1,99 @@ +mod common; + +// Recommended strategy to test plugin's transform is verify +// the Visitor's behavior, instead of trying to run `process_transform` with mocks +// unless explicitly required to do so. + +use std::sync::{Arc, Mutex}; + +use common::TestEmitter; +use cubetranspilers::validation_transpiler::*; +use swc_core::ecma::ast::{EsVersion, Program}; +use swc_core::{ + common::{ + errors::{Handler, HandlerFlags}, + sync::Lrc, + FileName, SourceMap, + }, + ecma::visit::VisitMutWith, +}; +use swc_ecma_parser::{lexer::Lexer, Parser, StringInput, Syntax}; + +#[test] +fn test_warning_for_user_context() { + let cm: Lrc = Default::default(); + let diagnostics = Arc::new(Mutex::new(Vec::new())); + let emitter = Box::new(TestEmitter { + diagnostics: diagnostics.clone(), + }); + let handler = Handler::with_emitter_and_flags( + emitter, + HandlerFlags { + can_emit_warnings: true, + ..Default::default() + }, + ); + + let js_code = "USER_CONTEXT.something;"; + + let fm = cm.new_source_file( + Arc::new(FileName::Custom("input.js".into())), + js_code.into(), + ); + let lexer = Lexer::new( + Syntax::Es(Default::default()), + EsVersion::Es2020, + StringInput::from(&*fm), + None, + ); + let mut parser = Parser::new_from(lexer); + let mut program: Program = parser.parse_program().expect("Failed to parse the JS code"); + + let mut visitor = ValidationTransformVisitor::new(None, &handler); + program.visit_mut_with(&mut visitor); + + let diags = diagnostics.lock().unwrap(); + assert!( + diags + .iter() + .any(|msg| msg.contains("Support for USER_CONTEXT was removed")), + "Should emit warning", + ); +} + +#[test] +fn test_no_warnings() { + let cm: Lrc = Default::default(); + let diagnostics = Arc::new(Mutex::new(Vec::new())); + let emitter = Box::new(TestEmitter { + diagnostics: diagnostics.clone(), + }); + let handler = Handler::with_emitter_and_flags( + emitter, + HandlerFlags { + can_emit_warnings: true, + ..Default::default() + }, + ); + + let js_code = "SECURITY_CONTEXT.something; let someOtherVar = 5;"; + + let fm = cm.new_source_file( + Arc::new(FileName::Custom("input.js".into())), + js_code.into(), + ); + let lexer = Lexer::new( + Syntax::Es(Default::default()), + EsVersion::Es2020, + StringInput::from(&*fm), + None, + ); + let mut parser = Parser::new_from(lexer); + let mut program: Program = parser.parse_program().expect("Failed to parse the JS code"); + + let mut visitor = ValidationTransformVisitor::new(None, &handler); + program.visit_mut_with(&mut visitor); + + let diags = diagnostics.lock().unwrap(); + assert!(diags.is_empty(), "Should not emit warning",); +} diff --git a/yarn.lock b/yarn.lock index e1fcc9fb89c53..4f0ed1970f745 100644 --- a/yarn.lock +++ b/yarn.lock @@ -27218,7 +27218,16 @@ string-length@^5.0.1: char-regex "^2.0.0" strip-ansi "^7.0.1" -"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -27316,7 +27325,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -27344,6 +27353,13 @@ strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.0, strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -29543,7 +29559,7 @@ workerpool@^9.2.0: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-9.2.0.tgz#f74427cbb61234708332ed8ab9cbf56dcb1c4371" integrity sha512-PKZqBOCo6CYkVOwAxWxQaSF2Fvb5Iv2fCeTP7buyWI2GiynWr46NcXSgK/idoV6e60dgCBfgYc+Un3HMvmqP8w== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -29569,6 +29585,15 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"