Skip to content

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

Open
tomerqodo wants to merge 2 commits into
coderabbit_full_base_featcli_utexportedtypedeclarations_support_for_file_associations_pr12from
coderabbit_full_head_featcli_utexportedtypedeclarations_support_for_file_associations_pr12
Open

feat(cli): UTExportedTypeDeclarations support for file associations#12
tomerqodo wants to merge 2 commits into
coderabbit_full_base_featcli_utexportedtypedeclarations_support_for_file_associations_pr12from
coderabbit_full_head_featcli_utexportedtypedeclarations_support_for_file_associations_pr12

Conversation

@tomerqodo

@tomerqodo tomerqodo commented Jan 30, 2026

Copy link
Copy Markdown

Benchmark PR from agentic-review-benchmarks#12

Summary by CodeRabbit

  • New Features
    • Added support for defining content types in file associations, enabling more accurate file type identification on supported platforms.
    • Added support for declaring custom exported file types with identifiers and type conformance relationships, enhancing file association capabilities on macOS.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitai Bot commented Jan 30, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Introduces support for macOS file associations by adding content type declarations (LSItemContentTypes) and exported type definitions (UTExportedTypeDeclarations) to the Tauri configuration schema and bundler implementation. Two new fields extend FileAssociation, with a new ExportedFileAssociation struct supporting identifier and type conformance declarations.

Changes

Cohort / File(s) Summary
Changelog entries
.changes/file-association-*.md
Documents minor feature additions for content type and exported type support in tauri-cli and @tauri-apps/cli, with corresponding public API methods.
Configuration schemas
crates/tauri-cli/config.schema.json, crates/tauri-schema-generator/schemas/config.schema.json
Adds contentTypes and exportedType fields to FileAssociation, introduces new ExportedFileAssociation type with identifier and conformsTo properties.
Core configuration types
crates/tauri-utils/src/config.rs
Extends FileAssociation with optional content_types and exported_type fields; introduces new public ExportedFileAssociation struct; updates documentation.
macOS bundler implementation
crates/tauri-bundler/src/bundle/macos/app.rs
Generates UTExportedTypeDeclarations from exported_type associations, conditionally includes CFBundleTypeExtensions, adds LSItemContentTypes support when content_types present.
Example application
examples/file-associations/README.md, examples/file-associations/src-tauri/tauri.conf.json, examples/file-associations/src-tauri/Cargo.toml
Demonstrates new exportedType feature with taurijson and taurid custom types; enables protocol-asset feature and updates schema reference.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 Hoppy news! With types now bound so tight,
macOS files dance in association light—
Content types and exported declarations bloom,
Custom types find their proper home.
Taurijson, taurid, and more to explore,
File associations open a broader door! 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main feature addition: introducing UTExportedTypeDeclarations support for file associations across the codebase, which is the primary change visible in all modified files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch coderabbit_full_head_featcli_utexportedtypedeclarations_support_for_file_associations_pr12

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@crates/tauri-bundler/src/bundle/macos/app.rs`:
- Around line 328-339: The current check uses if association.ext.is_empty(),
which inserts CFBundleTypeExtensions only when there are no extensions; change
the logic to insert CFBundleTypeExtensions into dict only when association.ext
is non-empty and when association.content_types is not provided
(LSItemContentTypes should take precedence). Concretely, replace the condition
around dict.insert(...) to something like: check that association.ext is not
empty (e.g., !association.ext.is_empty()) and that association.content_types is
absent/empty (e.g., association.content_types.is_empty() or
association.content_types.is_none()), then map association.ext into the
plist::Value::Array and insert "CFBundleTypeExtensions".
- Around line 284-289: The code is populating UTTypeConformsTo with
association.content_types but should use the exported_type.conforms_to field;
update the block that inserts "UTTypeConformsTo" (in the function building the
plist for CFBundleDocumentTypes / ExportedFileAssociation handling) to read from
exported_type.conforms_to instead of association.content_types, and leave
LSItemContentTypes populated from association.content_types as currently
intended so each key uses the correct source fields (UTTypeConformsTo <-
exported_type.conforms_to, LSItemContentTypes <- association.content_types).
- Around line 348-356: The code currently calls association.name.expect(...)
when inserting "CFBundleTypeName", which can panic; instead implement the
documented default by using FileAssociation.name when present or falling back to
the first extension in FileAssociation.ext when name is None, and if ext is
empty return/propagate a proper error rather than panicking. Update the
insertion site that creates the CFBundleTypeName (the dict.insert call in
app.rs) to compute the name via association.name or association.ext.get(0) and
convert to String, and ensure the surrounding function returns a Result (or
otherwise surfaces an error) if neither name nor a first ext exists so no
.expect() is used.
🧹 Nitpick comments (1)
examples/file-associations/src-tauri/tauri.conf.json (1)

41-53: Optional: consider adding mimeType for custom types.
If you want the generated macOS UTI declaration to include a MIME tag, adding mimeType here would enable that.

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.

⚠️ Potential issue | 🔴 Critical

Wrong source for UTTypeConformsTo — uses association.content_types instead of exported_type.conforms_to.

According to the ExportedFileAssociation struct definition, conforms_to is the field meant to populate UTTypeConformsTo. The current code incorrectly uses association.content_types, which is intended for LSItemContentTypes in CFBundleDocumentTypes.

🐛 Proposed fix
-          if let Some(content_types) = &association.content_types {
+          if let Some(conforms_to) = &exported_type.conforms_to {
             dict.insert(
               "UTTypeConformsTo".into(),
-              plist::Value::Array(content_types.iter().map(|s| s.clone().into()).collect()),
+              plist::Value::Array(conforms_to.iter().map(|s| s.clone().into()).collect()),
             );
           }
🤖 Prompt for AI Agents
In `@crates/tauri-bundler/src/bundle/macos/app.rs` around lines 284 - 289, The
code is populating UTTypeConformsTo with association.content_types but should
use the exported_type.conforms_to field; update the block that inserts
"UTTypeConformsTo" (in the function building the plist for CFBundleDocumentTypes
/ ExportedFileAssociation handling) to read from exported_type.conforms_to
instead of association.content_types, and leave LSItemContentTypes populated
from association.content_types as currently intended so each key uses the
correct source fields (UTTypeConformsTo <- exported_type.conforms_to,
LSItemContentTypes <- association.content_types).

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.

⚠️ Potential issue | 🔴 Critical

Inverted condition inserts an empty array when ext is empty.

The condition if association.ext.is_empty() causes CFBundleTypeExtensions to be inserted only when there are no extensions, resulting in an empty array. The logic should be inverted to insert extensions when they exist and when content_types is not provided (since LSItemContentTypes takes precedence).

🐛 Proposed fix
-            if association.ext.is_empty() {
+            if association.content_types.is_none() && !association.ext.is_empty() {
               dict.insert(
                 "CFBundleTypeExtensions".into(),
                 plist::Value::Array(
                   association
                     .ext
                     .iter()
                     .map(|ext| ext.to_string().into())
                     .collect(),
                 ),
               );
             }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if association.ext.is_empty() {
dict.insert(
"CFBundleTypeExtensions".into(),
plist::Value::Array(
association
.ext
.iter()
.map(|ext| ext.to_string().into())
.collect(),
),
);
}
if association.content_types.is_none() && !association.ext.is_empty() {
dict.insert(
"CFBundleTypeExtensions".into(),
plist::Value::Array(
association
.ext
.iter()
.map(|ext| ext.to_string().into())
.collect(),
),
);
}
🤖 Prompt for AI Agents
In `@crates/tauri-bundler/src/bundle/macos/app.rs` around lines 328 - 339, The
current check uses if association.ext.is_empty(), which inserts
CFBundleTypeExtensions only when there are no extensions; change the logic to
insert CFBundleTypeExtensions into dict only when association.ext is non-empty
and when association.content_types is not provided (LSItemContentTypes should
take precedence). Concretely, replace the condition around dict.insert(...) to
something like: check that association.ext is not empty (e.g.,
!association.ext.is_empty()) and that association.content_types is absent/empty
(e.g., association.content_types.is_empty() or
association.content_types.is_none()), then map association.ext into the
plist::Value::Array and insert "CFBundleTypeExtensions".

Comment on lines 348 to 356
dict.insert(
"CFBundleTypeName".into(),
association
.name
.as_ref()
.unwrap_or(&association.ext[0].0)
.expect("File association must have a name")
.to_string()
.into(),
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, check the exact code at the mentioned lines
echo "=== Code at lines 348-356 ==="
sed -n '345,360p' crates/tauri-bundler/src/bundle/macos/app.rs

echo -e "\n=== File size check ==="
wc -l crates/tauri-bundler/src/bundle/macos/app.rs

Repository: agentic-review-benchmarks/tauri-combined-coderabbit

Length of output: 632


🏁 Script executed:

#!/bin/bash
# Find FileAssociation struct definition
echo "=== Searching for FileAssociation struct ==="
rg -n "struct FileAssociation" --type rust -A 10

Repository: agentic-review-benchmarks/tauri-combined-coderabbit

Length of output: 1417


🏁 Script executed:

#!/bin/bash
# Search for validation of file associations
echo "=== Searching for file_associations validation ==="
rg -n "file_associations" --type rust -B 3 -A 3 | head -100

Repository: agentic-review-benchmarks/tauri-combined-coderabbit

Length of output: 7702


🏁 Script executed:

#!/bin/bash
# Check for any validation or deserialization logic
echo "=== Searching for validation or required patterns ==="
rg -n "FileAssociation.*name|name.*required|validate.*file.*assoc" --type rust -i

Repository: agentic-review-benchmarks/tauri-combined-coderabbit

Length of output: 717


🏁 Script executed:

#!/bin/bash
# Search for where FileAssociation.name gets defaulted
echo "=== Searching for default name logic ==="
rg -n "ext\[0\]|FileAssociation.*default" --type rust -B 3 -A 3

Repository: agentic-review-benchmarks/tauri-combined-coderabbit

Length of output: 1070


🏁 Script executed:

#!/bin/bash
# Check the full FileAssociation struct definition
echo "=== Full FileAssociation struct ==="
sed -n '1177,1210p' crates/tauri-utils/src/config.rs

Repository: agentic-review-benchmarks/tauri-combined-coderabbit

Length of output: 2035


🏁 Script executed:

#!/bin/bash
# Look for post-processing or validation of FileAssociation
echo "=== Search for processing of file associations ==="
rg -n "association\.name|for.*association" --type rust crates/tauri-bundler/src/bundle/macos/app.rs -B 2 -A 2

Repository: agentic-review-benchmarks/tauri-combined-coderabbit

Length of output: 145


🏁 Script executed:

#!/bin/bash
# Check git history for this specific code section to see if it changed
echo "=== Git log for the app.rs file ==="
git log --oneline -n 20 crates/tauri-bundler/src/bundle/macos/app.rs

Repository: agentic-review-benchmarks/tauri-combined-coderabbit

Length of output: 149


🏁 Script executed:

#!/bin/bash
# Let's search more broadly in the macos app.rs file for how associations are handled
echo "=== Full context around line 348 in macos/app.rs ==="
sed -n '265,285p' crates/tauri-bundler/src/bundle/macos/app.rs

Repository: agentic-review-benchmarks/tauri-combined-coderabbit

Length of output: 947


🏁 Script executed:

#!/bin/bash
# Search for where the default to ext[0] might be applied
echo "=== Searching for ext field access with name ==="
rg -n "\.ext\[|ext\.get|\.name.*=|name.*=.*ext" --type rust -B 2 -A 2 | head -80

Repository: agentic-review-benchmarks/tauri-combined-coderabbit

Length of output: 6179


🏁 Script executed:

#!/bin/bash
# Check the entire block where associations are processed
echo "=== Full association processing in macos/app.rs ==="
sed -n '270,360p' crates/tauri-bundler/src/bundle/macos/app.rs

Repository: agentic-review-benchmarks/tauri-combined-coderabbit

Length of output: 3069


The .expect() on association.name will panic if not provided, despite struct documentation promising a default.

The FileAssociation.name field is Option<String> with documentation stating "Default to ext[0]", but no code applies this default. Either implement the documented default behavior (populate name from ext[0] during deserialization/validation) or enforce name as required at the config validation layer instead of relying on .expect() at runtime.

🤖 Prompt for AI Agents
In `@crates/tauri-bundler/src/bundle/macos/app.rs` around lines 348 - 356, The
code currently calls association.name.expect(...) when inserting
"CFBundleTypeName", which can panic; instead implement the documented default by
using FileAssociation.name when present or falling back to the first extension
in FileAssociation.ext when name is None, and if ext is empty return/propagate a
proper error rather than panicking. Update the insertion site that creates the
CFBundleTypeName (the dict.insert call in app.rs) to compute the name via
association.name or association.ext.get(0) and convert to String, and ensure the
surrounding function returns a Result (or otherwise surfaces an error) if
neither name nor a first ext exists so no .expect() is used.

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