Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
🏗️ Beginning setup for tagged enums
type_taglike thistests/tagged.rswith the same demo as in that commentDevelopment log:
Outline of work (giacometti dev journal entry)
Journal Entry: 2025-01-07 Tagged Enum Deserialization
Current State
type_tag: Option<&'static str>field for format-specific type identificationtype_tagfrom#[facet(type_tag = "...")]container attribute into Shape builder (process_enum.rs:434, process_struct.rs:199-205)Missing
Implementation plan
Analysis of Current Implementation
Analysis of Current Implementation
facet-core Shape representation
Shape::type_tagfield exists asOption<&'static str>(used in facet-asn1/src/lib.rs:79-83).type_tag(value)builder method in generated Shape constructiontype_tagto specify protocol tag values (facet-asn1/src/tag.rs:92-208)type_tagto specify discriminator field namefacet-macros-emit enum handling
process_enum.rsemitstype_tagon EnumType's Shape if#[facet(type_tag = "...")]present on container (line 434)Variant::builder().name(token)where token is the effective name after rename rules (line 115)PVariant::namehas bothraw(IdentOrLiteral) andeffective(String) components (used at line 113)#[facet(rename = "...")]attributesfacet-json deserialization
deserialize.rsimplementsFormattrait withnext()andskip()methods (lines 23-121)Outcomeenum variants:Scalar,ObjectStarted,ObjectEnded,ListStarted,ListEnded(lib.rs:92-103)Shape::type_tagfieldfacet-deserialize framework
lib.rs:deserialize_wip()drives parsing loop (lines 340-433)StackRunnermaintains parsing state with instruction stack (lines 839-905)StackRunner::object_key_or_object_close()handles object field matching (lines 1133-1299)wip.find_variant(&key)when key matches variant name (line 1173)NextDatacarries input, runner state, and work-in-progress value through parsing (lines 226-245)Target Behavior
Target Behavior
Example code
Example JSON inputs
{ "type": "REPEAT", "content": { "type": "SYMBOL", "name": "x" } } { "type": "REPEAT1", "content": { "type": "SYMBOL", "name": "y" } } { "type": "SYMBOL", "name": "z" }Deserialization behavior
type_tag = "type", look for object field named"type""REPEAT","REPEAT1","SYMBOL")Serialization behavior
type_tag = "type", emit discriminator field firstshape.type_tag, value from active variant's effective nameRequired Behaviour
Required Changes
1. Add peek functionality to Tokenizer
File: facet-json/src/tokenizer.rs
Location: Add new method after
parse_literal()(around line 384)Changes required:
peek_object_field(&mut self, field_name: &str) -> Option<Spanned<Token<'input>>>methodImplementation:
2. Modify Format::next() to handle tagged enums
File: facet-json/src/deserialize.rs
Location: Token::LBrace handler in next() method (around line 75)
Changes required:
TypeandUserTypefrom facet_core at top of fileToken::LBraceencountered, check ifnd.wip.shape()is enum withtype_tagnd.start()peek_object_field()with discriminator field namend.wip.select_nth_variant(index)when match foundDeserErrorKind::NoSuchVariantwhen no matchObjectStartedreturnImplementation:
3. Skip discriminator field during object deserialization
File: facet-deserialize/src/lib.rs
Location:
StackRunner::object_key_or_object_close()enum handling (around line 1173)Changes required:
Type::User(UserType::Enum(_ed))branchshape.type_tagignore = trueand skip processingImplementation:
4. Emit discriminator field during serialization
File: facet-json/src/serialize.rs
Location: Requires examining facet-serialize's serialize_iterative usage
Investigation needed: The serialize.rs file uses
facet_serialize::serialize_iterative()(line 33) which takes aPeekand drives serialization. Need to examine how serialize_iterative calls the serializer for enum variants.Expected location: When serialize_iterative processes an enum variant's fields, before calling
serialize_field_name()for the first actual field.Changes required:
type_tagserializer.serialize_field_name(discriminator_field)?thenserializer.serialize_str(variant.name)?Note: Exact implementation depends on facet-serialize's serialize_iterative structure. May require changes in facet-serialize crate itself if serialize_iterative doesn't expose hooks for pre-field emission. If changes needed in facet-serialize, document as separate task.
Fallback approach if serialize_iterative modification not feasible: Override serialization for tagged enums by implementing custom logic in JsonSerializer that checks for enum with type_tag before calling serialize_iterative.
Testing Requirements
Testing Requirements
Unit tests in facet-json
File: facet-json/tests/tagged_enums.rs (new file)
Test cases:
Integration test
File: facet-json/tests/tree_sitter_schema.rs (new file)
Test case:
Documentation Requirements
Documentation Requirements
README.md updates
File: facet-json/README.md
Location: After existing content, add new section
Content:
The
type_tagattribute specifies which JSON object field contains the variant discriminator. Variant names (after#[facet(rename = "...")]rules) are matched against discriminator values.Example JSON:
{"type": "REPEAT", "content": {"type": "SYMBOL", "name": "x"}} {"type": "SYMBOL", "name": "z"}This enables deserialization of schemas like tree-sitter's grammar.json where different rule types share similar structure but are distinguished by a discriminator field.
Migration Path
Migration Path
Backwards compatibility
#[facet(type_tag = "...")]continue working unchangedtype_tagattribute present on enum containertype_tagunaffected (different interpretation per format)Deprecations
Known Limitations
Known Limitations
Not implemented in MVP
#[serde(tag = "t", content = "c")])#[serde(untagged)])Workarounds