Skip to content

Added new code sections to support deser overrides#1090

Open
timocov wants to merge 1 commit intosmithy-lang:mainfrom
timocov:deser-code-sections
Open

Added new code sections to support deser overrides#1090
timocov wants to merge 1 commit intosmithy-lang:mainfrom
timocov:deser-code-sections

Conversation

@timocov
Copy link
Contributor

@timocov timocov commented Mar 19, 2026

Issue #, if available: fixes #1053

Description of changes:

I was playing around with the code in order to find a good solution for #1053 and it seems this is the one (well, at least it works, it doesn't expose too much and kinda fits into current architecture 😂).

To summarize, this PR adds 2 new code sections that consuming code can override:

  • MemberDeserializerSection
  • MemberSerializerSection

For instance, considering an example from #1053 (comment) you can write something like that in your codegen integration in order to get proper deser and type change:

final class MemberSerializerInterceptor implements CodeInterceptor<MemberSerializerSection, JavaWriter> {
    private final CodeGenerationContext context;

    public MemberSerializerInterceptor(CodeGenerationContext context) {
        this.context = context;
    }

    @Override
    public Class<MemberSerializerSection> sectionType() {
        return MemberSerializerSection.class;
    }

    @Override
    public void write(JavaWriter writer, String previousText, MemberSerializerSection section) {
        section.targetedShape().accept(new Visitor(context, writer, previousText, section));
    }

    private static class Visitor extends ShapeVisitor.Default<Void> {
        private final CodeGenerationContext context;
        private final JavaWriter writer;
        private final String previousText;
        private final MemberSerializerSection section;

        public Visitor(CodeGenerationContext context, JavaWriter writer, String previousText, MemberSerializerSection section) {
            this.context = context;
            this.writer = writer;
            this.previousText = previousText;
            this.section = section;
        }

        @Override
        public Void stringShape(StringShape stringShape) {
            if (stringShape.hasTrait(DateFormatTrait.class)) {
                writer.putContext("state", section.state());
                writer.putContext("schema", section.shapeSchemaVariable());
                writer.write("SharedSerde.serializeDate(${schema:L}, ${state:L}, serializer)");
                return null;
            }

            return getDefault(stringShape);
        }

        @Override
        public Void memberShape(MemberShape memberShape) {
            return context.model().expectShape(memberShape.getTarget()).accept(this);
        }

        @Override
        protected Void getDefault(Shape shape) {
            writer.writeWithNoFormatting(previousText);
            return null;
        }
    }
}
final class MemberDeserializerInterceptor implements CodeInterceptor<MemberDeserializerSection, JavaWriter> {
    private final CodeGenerationContext context;

    public MemberDeserializerInterceptor(CodeGenerationContext context) {
        this.context = context;
    }

    @Override
    public Class<MemberDeserializerSection> sectionType() {
        return MemberDeserializerSection.class;
    }

    @Override
    public void write(JavaWriter writer, String previousText, MemberDeserializerSection section) {
        section.targetedShape().accept(new Visitor(context, writer, previousText, section));
    }

    private static class Visitor extends ShapeVisitor.Default<Void> {
        private final CodeGenerationContext context;
        private final JavaWriter writer;
        private final String previousText;
        private final MemberDeserializerSection section;

        public Visitor(CodeGenerationContext context, JavaWriter writer, String previousText, MemberDeserializerSection section) {
            this.context = context;
            this.writer = writer;
            this.previousText = previousText;
            this.section = section;
        }

        @Override
        public Void stringShape(StringShape stringShape) {
            if (stringShape.hasTrait(DateFormatTrait.class)) {
                writer.putContext("schemaName", section.shapeSchemaVariable());
                writer.putContext("deserializer", section.deserializerVariable());
                writer.write("SharedSerde.deserializeDate(${schemaName:L}, ${deserializer:L})");
                return null;
            }

            return getDefault(stringShape);
        }

        @Override
        public Void memberShape(MemberShape memberShape) {
            return context.model().expectShape(memberShape.getTarget()).accept(this);
        }

        @Override
        protected Void getDefault(Shape shape) {
            writer.writeWithNoFormatting(previousText);
            return null;
        }
    }
}

This doesn't feel like a huge change (even tho it might expose something you don't want to expose - let me know) and the amount of changes is relatively small, but it feels like it gives enough flexibility for consumers if they need to (e.g. I also was able to add support for alloy#discriminated as well, which feels like a huge win tbh 😂)

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@timocov timocov force-pushed the deser-code-sections branch from 78dfbc6 to 9f0c265 Compare March 20, 2026 15:43
@timocov timocov force-pushed the deser-code-sections branch from 9f0c265 to 2cc4ff1 Compare March 21, 2026 11:58
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.

Thoughts on providing a way to customize type-system?

1 participant