Skip to content

feat(cli): UTExportedTypeDeclarations support for file associations - #51

Open
tomerqodo wants to merge 2 commits into
qodo_claude_vs_qodo_base_featcli_utexportedtypedeclarations_support_for_file_associations_pr12from
qodo_claude_vs_qodo_head_featcli_utexportedtypedeclarations_support_for_file_associations_pr12
Open

feat(cli): UTExportedTypeDeclarations support for file associations#51
tomerqodo wants to merge 2 commits into
qodo_claude_vs_qodo_base_featcli_utexportedtypedeclarations_support_for_file_associations_pr12from
qodo_claude_vs_qodo_head_featcli_utexportedtypedeclarations_support_for_file_associations_pr12

Conversation

@tomerqodo

Copy link
Copy Markdown

Benchmark PR from agentic-review-benchmarks#12

@qodo-code-review

Copy link
Copy Markdown

Review Summary by Qodo

Add UTExportedTypeDeclarations and content types support for file associations

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Add UTExportedTypeDeclarations support for custom file types on macOS
• Add content_types field to declare file format support via LSItemContentTypes
• Add ExportedFileAssociation struct for defining custom type metadata
• Update file association logic to conditionally populate plist entries
• Improve error handling with explicit expect() for required name field
Diagram
flowchart LR
  FA["FileAssociation struct"]
  CT["content_types field"]
  ET["exported_type field"]
  EFA["ExportedFileAssociation struct"]
  PLIST["macOS Info.plist"]
  LSI["LSItemContentTypes"]
  UTED["UTExportedTypeDeclarations"]
  
  FA --> CT
  FA --> ET
  ET --> EFA
  CT --> PLIST
  EFA --> PLIST
  PLIST --> LSI
  PLIST --> UTED
Loading

Grey Divider

File Changes

1. crates/tauri-bundler/src/bundle/macos/app.rs ✨ Enhancement +71/-11

Implement UTExportedTypeDeclarations plist generation

crates/tauri-bundler/src/bundle/macos/app.rs


2. crates/tauri-utils/src/config.rs ✨ Enhancement +25/-1

Add exported_type and content_types to FileAssociation

crates/tauri-utils/src/config.rs


3. .changes/file-association-content-type.md 📝 Documentation +6/-0

Changelog entry for content type support

.changes/file-association-content-type.md


View more (7)
4. .changes/file-association-exported-type-cli.md 📝 Documentation +6/-0

Changelog entry for exported type CLI support

.changes/file-association-exported-type-cli.md


5. .changes/file-association-exported-type.md 📝 Documentation +5/-0

Changelog entry for exported type utils support

.changes/file-association-exported-type.md


6. crates/tauri-cli/config.schema.json ⚙️ Configuration changes +46/-1

Update schema with new file association fields

crates/tauri-cli/config.schema.json


7. crates/tauri-schema-generator/schemas/config.schema.json ⚙️ Configuration changes +46/-1

Update schema generator with new fields

crates/tauri-schema-generator/schemas/config.schema.json


8. examples/file-associations/README.md 📝 Documentation +6/-0

Document exported type and association examples

examples/file-associations/README.md


9. examples/file-associations/src-tauri/Cargo.toml ⚙️ Configuration changes +1/-1

Enable protocol-asset feature for example

examples/file-associations/src-tauri/Cargo.toml


10. examples/file-associations/src-tauri/tauri.conf.json ⚙️ Configuration changes +19/-2

Add exported type examples and asset protocol config

examples/file-associations/src-tauri/tauri.conf.json


Grey Divider

Qodo Logo

@qodo-code-review

qodo-code-review Bot commented Mar 10, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (1) 📎 Requirement gaps (0)

Grey Divider


Action required

1. association.name uses expect 📘 Rule violation ⛯ Reliability
Description
create_info_plist panics when association.name is None due to an expect, instead of
returning a Result error for invalid user configuration. This can crash bundling for valid configs
that omit name (it is optional elsewhere), violating the no-panic guidance for fallible
operations.
Code

crates/tauri-bundler/src/bundle/macos/app.rs[R349-354]

              "CFBundleTypeName".into(),
              association
                .name
                .as_ref()
-                .unwrap_or(&association.ext[0].0)
+                .expect("File association must have a name")
                .to_string()
Evidence
Compliance ID 16 disallows panicking fallible operations; the changed code unwraps an optional
config value with expect, which will panic at runtime if the field is missing.

AGENTS.md
crates/tauri-bundler/src/bundle/macos/app.rs[348-355]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`create_info_plist` unwraps `association.name` with `expect("File association must have a name")`, which can panic during bundling when the config omits `name`.

## Issue Context
`create_info_plist` already returns `crate::Result<()>`, so invalid/missing configuration should be reported via a returned error (or handled with a safe fallback) rather than panicking.

## Fix Focus Areas
- crates/tauri-bundler/src/bundle/macos/app.rs[348-356]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Missing CFBundleTypeExtensions 🐞 Bug ✓ Correctness
Description
In create_info_plist (macOS), CFBundleTypeExtensions is only inserted when association.ext is empty,
so typical associations with extensions omit CFBundleTypeExtensions entirely and cannot be
registered by extension.
Code

crates/tauri-bundler/src/bundle/macos/app.rs[R328-339]

+            if association.ext.is_empty() {
+              dict.insert(
+                "CFBundleTypeExtensions".into(),
+                plist::Value::Array(
+                  association
+                    .ext
+                    .iter()
+                    .map(|ext| ext.to_string().into())
+                    .collect(),
+                ),
+              );
+            }
Evidence
The macOS plist generator writes CFBundleTypeExtensions only under if association.ext.is_empty(),
which skips writing extensions for the common case where ext is non-empty. The config model and
example config both show ext is the primary/required association mechanism, so omitting
CFBundleTypeExtensions is a functional regression.

crates/tauri-bundler/src/bundle/macos/app.rs[320-346]
crates/tauri-utils/src/config.rs[1173-1187]
examples/file-associations/src-tauri/tauri.conf.json[25-40]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`CFBundleTypeExtensions` is currently inserted only when `association.ext.is_empty()`, which suppresses extension-based associations for normal configs.

### Issue Context
macOS file associations by extension rely on `CFBundleDocumentTypes` entries containing `CFBundleTypeExtensions`. The config model describes `ext` as the primary association mechanism.

### Fix Focus Areas
- crates/tauri-bundler/src/bundle/macos/app.rs[328-339]

### Expected change
- Change `if association.ext.is_empty()` to `if !association.ext.is_empty()` (or remove the condition if always desired).
- Ensure the `ext.is_empty()` case does not insert an empty `CFBundleTypeExtensions` array unless explicitly required and documented.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Wrong UTTypeConformsTo source 🐞 Bug ✓ Correctness
Description
UTExportedTypeDeclarations populates UTTypeConformsTo from FileAssociation.content_types instead of
ExportedFileAssociation.conforms_to, so exportedType.conformsTo is ignored and exported type
metadata is incorrect.
Code

crates/tauri-bundler/src/bundle/macos/app.rs[R284-289]

+          if let Some(content_types) = &association.content_types {
+            dict.insert(
+              "UTTypeConformsTo".into(),
+              plist::Value::Array(content_types.iter().map(|s| s.clone().into()).collect()),
+            );
+          }
Evidence
The config model defines ExportedFileAssociation.conforms_to as the mapping to UTTypeConformsTo,
but the macOS bundler reads association.content_types instead and never reads
exported_type.conforms_to. The example config sets exportedType.conformsTo, which will therefore
have no effect.

crates/tauri-bundler/src/bundle/macos/app.rs[274-289]
crates/tauri-utils/src/config.rs[1209-1216]
examples/file-associations/src-tauri/tauri.conf.json[41-54]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`UTTypeConformsTo` for `UTExportedTypeDeclarations` is currently derived from `FileAssociation.content_types`, so `exportedType.conformsTo` is ignored.

### Issue Context
The config type `ExportedFileAssociation.conforms_to` is explicitly documented as mapping to `UTTypeConformsTo`. `contentTypes` is documented as mapping to `LSItemContentTypes`.

### Fix Focus Areas
- crates/tauri-bundler/src/bundle/macos/app.rs[274-289]
- crates/tauri-utils/src/config.rs[1212-1216]

### Expected change
- Replace the `if let Some(content_types) = &association.content_types` block (for UTTypeConformsTo) with logic that uses `exported_type.conforms_to`.
- Do not change the existing `LSItemContentTypes` insertion, which should continue to use `association.content_types`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Comment on lines 349 to 354
"CFBundleTypeName".into(),
association
.name
.as_ref()
.unwrap_or(&association.ext[0].0)
.expect("File association must have a name")
.to_string()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. association.name uses expect 📘 Rule violation ⛯ Reliability

create_info_plist panics when association.name is None due to an expect, instead of
returning a Result error for invalid user configuration. This can crash bundling for valid configs
that omit name (it is optional elsewhere), violating the no-panic guidance for fallible
operations.
Agent Prompt
## Issue description
`create_info_plist` unwraps `association.name` with `expect("File association must have a name")`, which can panic during bundling when the config omits `name`.

## Issue Context
`create_info_plist` already returns `crate::Result<()>`, so invalid/missing configuration should be reported via a returned error (or handled with a safe fallback) rather than panicking.

## Fix Focus Areas
- crates/tauri-bundler/src/bundle/macos/app.rs[348-356]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +328 to +339
if association.ext.is_empty() {
dict.insert(
"CFBundleTypeExtensions".into(),
plist::Value::Array(
association
.ext
.iter()
.map(|ext| ext.to_string().into())
.collect(),
),
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. Missing cfbundletypeextensions 🐞 Bug ✓ Correctness

In create_info_plist (macOS), CFBundleTypeExtensions is only inserted when association.ext is empty,
so typical associations with extensions omit CFBundleTypeExtensions entirely and cannot be
registered by extension.
Agent Prompt
### Issue description
`CFBundleTypeExtensions` is currently inserted only when `association.ext.is_empty()`, which suppresses extension-based associations for normal configs.

### Issue Context
macOS file associations by extension rely on `CFBundleDocumentTypes` entries containing `CFBundleTypeExtensions`. The config model describes `ext` as the primary association mechanism.

### Fix Focus Areas
- crates/tauri-bundler/src/bundle/macos/app.rs[328-339]

### Expected change
- Change `if association.ext.is_empty()` to `if !association.ext.is_empty()` (or remove the condition if always desired).
- Ensure the `ext.is_empty()` case does not insert an empty `CFBundleTypeExtensions` array unless explicitly required and documented.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +284 to +289
if let Some(content_types) = &association.content_types {
dict.insert(
"UTTypeConformsTo".into(),
plist::Value::Array(content_types.iter().map(|s| s.clone().into()).collect()),
);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

3. Wrong uttypeconformsto source 🐞 Bug ✓ Correctness

UTExportedTypeDeclarations populates UTTypeConformsTo from FileAssociation.content_types instead of
ExportedFileAssociation.conforms_to, so exportedType.conformsTo is ignored and exported type
metadata is incorrect.
Agent Prompt
### Issue description
`UTTypeConformsTo` for `UTExportedTypeDeclarations` is currently derived from `FileAssociation.content_types`, so `exportedType.conformsTo` is ignored.

### Issue Context
The config type `ExportedFileAssociation.conforms_to` is explicitly documented as mapping to `UTTypeConformsTo`. `contentTypes` is documented as mapping to `LSItemContentTypes`.

### Fix Focus Areas
- crates/tauri-bundler/src/bundle/macos/app.rs[274-289]
- crates/tauri-utils/src/config.rs[1212-1216]

### Expected change
- Replace the `if let Some(content_types) = &association.content_types` block (for UTTypeConformsTo) with logic that uses `exported_type.conforms_to`.
- Do not change the existing `LSItemContentTypes` insertion, which should continue to use `association.content_types`.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant