diff --git a/src/ir/ir_symbol.cc b/src/ir/ir_symbol.cc index 5cc57ea..789c163 100644 --- a/src/ir/ir_symbol.cc +++ b/src/ir/ir_symbol.cc @@ -2,15 +2,28 @@ #include -#include // std::ranges::reverse -#include // assert -#include // std::istringstream -#include // std::string, std::getline -#include // std::vector +#include // std::ranges::reverse +#include // assert +#include // std::filesystem::path +#include // std::istringstream +#include // std::string, std::getline +#include // std::vector namespace { +// Strip all extensions from a filename (e.g., "user.schema.json" -> "user") +auto strip_extensions(const std::string &filename) -> std::string { + std::filesystem::path path{filename}; + while (path.has_extension()) { + path = path.stem(); + } + return path.string(); +} + // If the input looks like an absolute URI, extract its path segments. +// For file URIs, only the filename (without extensions) is used. +// For other URIs, all path segments are used with extensions stripped from +// the last segment. // Otherwise, add the input as a single segment. // Note: segments are added in reverse order because the caller reverses // the entire result at the end. @@ -30,13 +43,23 @@ auto push_token_segments(std::vector &result, } } - // Reverse segments since the caller will reverse the entire result - std::ranges::reverse(segments); - for (const auto &path_segment : segments) { - result.emplace_back(path_segment); - } + if (!segments.empty()) { + // Strip extensions from the last segment + segments.back() = strip_extensions(segments.back()); + + // For file URIs, only use the filename + if (uri.is_file()) { + result.emplace_back(segments.back()); + } else { + // Reverse segments since the caller will reverse the entire result + std::ranges::reverse(segments); + for (const auto &path_segment : segments) { + result.emplace_back(path_segment); + } + } - return; + return; + } } } // NOLINTNEXTLINE(bugprone-empty-catch) diff --git a/test/e2e/typescript/2020-12/bundled_schema/schema.json b/test/e2e/typescript/2020-12/bundled_schema/schema.json index 6090646..0f40b49 100644 --- a/test/e2e/typescript/2020-12/bundled_schema/schema.json +++ b/test/e2e/typescript/2020-12/bundled_schema/schema.json @@ -5,32 +5,32 @@ "type": "object", "required": [ "data", "meta" ], "properties": { - "data": { "$ref": "https://example.com/schemas/user" }, - "meta": { "$ref": "https://example.com/schemas/metadata" } + "data": { "$ref": "https://example.com/schemas/user.json" }, + "meta": { "$ref": "https://example.com/schemas/metadata.json" } }, "additionalProperties": false, "$defs": { - "https://example.com/schemas/user": { + "https://example.com/schemas/user.json": { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://example.com/schemas/user", + "$id": "https://example.com/schemas/user.json", "type": "object", "required": [ "id", "name" ], "properties": { "id": { "type": "integer" }, "name": { "type": "string" }, - "email": { "$ref": "https://example.com/schemas/email" } + "email": { "$ref": "https://example.com/schemas/email.schema.json" } }, "additionalProperties": false }, - "https://example.com/schemas/email": { + "https://example.com/schemas/email.schema.json": { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://example.com/schemas/email", + "$id": "https://example.com/schemas/email.schema.json", "type": "string", "format": "email" }, - "https://example.com/schemas/metadata": { + "https://example.com/schemas/metadata.json": { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://example.com/schemas/metadata", + "$id": "https://example.com/schemas/metadata.json", "type": "object", "properties": { "timestamp": { "type": "string" }, diff --git a/test/e2e/typescript/2020-12/bundled_schema_file_uris/expected.d.ts b/test/e2e/typescript/2020-12/bundled_schema_file_uris/expected.d.ts new file mode 100644 index 0000000..ec34dfd --- /dev/null +++ b/test/e2e/typescript/2020-12/bundled_schema_file_uris/expected.d.ts @@ -0,0 +1,32 @@ +export type ResponseMeta = ResponseMetadata; + +export type ResponseData = ResponseUser; + +export type ResponseAdditionalProperties = never; + +export type ResponseUserName = string; + +export type ResponseUserId = number; + +export type ResponseUserAdditionalProperties = never; + +export interface ResponseUser { + "id": ResponseUserId; + "name": ResponseUserName; +} + +export type ResponseMetadataVersion = number; + +export type ResponseMetadataTimestamp = string; + +export type ResponseMetadataAdditionalProperties = never; + +export interface ResponseMetadata { + "timestamp"?: ResponseMetadataTimestamp; + "version"?: ResponseMetadataVersion; +} + +export interface Response { + "data": ResponseData; + "meta": ResponseMeta; +} diff --git a/test/e2e/typescript/2020-12/bundled_schema_file_uris/options.json b/test/e2e/typescript/2020-12/bundled_schema_file_uris/options.json new file mode 100644 index 0000000..8a23bd8 --- /dev/null +++ b/test/e2e/typescript/2020-12/bundled_schema_file_uris/options.json @@ -0,0 +1,3 @@ +{ + "defaultPrefix": "Response" +} diff --git a/test/e2e/typescript/2020-12/bundled_schema_file_uris/schema.json b/test/e2e/typescript/2020-12/bundled_schema_file_uris/schema.json new file mode 100644 index 0000000..e7388fb --- /dev/null +++ b/test/e2e/typescript/2020-12/bundled_schema_file_uris/schema.json @@ -0,0 +1,34 @@ +{ + "$id": "file:///schemas/api/response.json", + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "required": [ "data", "meta" ], + "properties": { + "data": { "$ref": "file:///schemas/models/user.json" }, + "meta": { "$ref": "file:///schemas/models/metadata.json" } + }, + "additionalProperties": false, + "$defs": { + "file:///schemas/models/user.json": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "file:///schemas/models/user.json", + "type": "object", + "required": [ "id", "name" ], + "properties": { + "id": { "type": "integer" }, + "name": { "type": "string" } + }, + "additionalProperties": false + }, + "file:///schemas/models/metadata.json": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "file:///schemas/models/metadata.json", + "type": "object", + "properties": { + "timestamp": { "type": "string" }, + "version": { "type": "integer" } + }, + "additionalProperties": false + } + } +} diff --git a/test/e2e/typescript/2020-12/bundled_schema_file_uris/test.ts b/test/e2e/typescript/2020-12/bundled_schema_file_uris/test.ts new file mode 100644 index 0000000..428cd15 --- /dev/null +++ b/test/e2e/typescript/2020-12/bundled_schema_file_uris/test.ts @@ -0,0 +1,80 @@ +import { + Response, + ResponseUser, + ResponseMetadata +} from "./expected"; + + +// Valid: full response with all fields +const fullResponse: Response = { + data: { + id: 123, + name: "John Doe" + }, + meta: { + timestamp: "2024-01-15T10:30:00Z", + version: 1 + } +}; + +// Valid: minimal response (required fields only) +const minimalResponse: Response = { + data: { + id: 1, + name: "Jane" + }, + meta: {} +}; + +// Valid: user object directly +const user: ResponseUser = { + id: 42, + name: "Test User" +}; + +// Valid: metadata object +const metadata: ResponseMetadata = { + timestamp: "2024-01-15", + version: 2 +}; + +// Invalid: missing required field 'data' +// @ts-expect-error - data is required +const missingData: Response = { + meta: {} +}; + +// Invalid: missing required field 'meta' +// @ts-expect-error - meta is required +const missingMeta: Response = { + data: { id: 1, name: "Test" } +}; + +// Invalid: user missing required 'id' +const userMissingId: Response = { + // @ts-expect-error - id is required on user + data: { + name: "Test" + }, + meta: {} +}; + +// Invalid: user missing required 'name' +const userMissingName: Response = { + // @ts-expect-error - name is required on user + data: { + id: 1 + }, + meta: {} +}; + +// Invalid: extra property on user (additionalProperties: false) +const extraUserProp: Response = { + data: { + id: 1, + name: "Test", + // @ts-expect-error - extra property not allowed + extra: "not allowed" + }, + meta: {} +};