-
Notifications
You must be signed in to change notification settings - Fork 193
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add codegen helper for defining operation-specific CustomizableOperation
methods.
#3918
Add codegen helper for defining operation-specific CustomizableOperation
methods.
#3918
Conversation
A new generated diff is ready to view.
A new doc preview is ready to view. |
Thanks for the PR. This gives us a good starting point for discussion.
Talked about this briefly offline, and it might be better to keep the presign method for all operations so we don’t break things for people. There's also an issue (linked off from this TODO) that says |
presign
customize operation method for ops that support itCustomizableOperation
methods.
@@ -80,3 +80,13 @@ fun <T : Section> RustWriter.writeCustomizationsOrElse( | |||
orElse(this) | |||
} | |||
} | |||
|
|||
fun <T : Section> allCustomizationsAreEmpty( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this necessary? If customizations
is empty then how would writeCustomizations
end up with a "dirty" writer?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great question!
How can you tell if the following function will write when called through RustWriter.writeCustomizations
?
override fun operationCustomizations(
codegenContext: ClientCodegenContext,
operation: OperationShape,
baseCustomizations: List<OperationCustomization>,
): List<OperationCustomization> {
return baseCustomizations +
object : OperationCustomization() {
override fun section(section: OperationSection): Writable {
return writable {
when (section) {
is OperationSection.CustomizableOperationImpl -> {
rust("// Writing occurred!")
}
}
else -> {}
}
}
}
}
}
It just returns a list of customizations with method implementations. You can't know anything about the implementations, including what sections they actually care about. There's no way to know if this will write anything without actually calling it and trying to write something.
So why is it important to know if something was written? It's important in cases where you want to avoid empty wrappers. Imagine that you want to generate a block of code with functions for converting a type. If you always have n+1
customizations, then it's always OK to write the enclosing block. But if you only have n
customizations then you'll write an empty block and clippy will be mad at you.
Enter allCustomizationsAreEmpty
. With this function, we can see if anything would actually be written, giving us the power to avoid writing an enclosing block when it would be empty.
Does that make sense?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does, one of my concerns was triggering unrelated/additional side effects (e.g. imports that trigger dependencies being added, etc). We saw this in Kotlin though for a similar but slightly different reason.
Would be nice if we had a cleaner way to do this but I suppose it's fine for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe adding source code comment with an example Rust snippet that this function is trying to solve is useful?
A new generated diff is ready to view.
A new doc preview is ready to view. |
…tion` methods. (#3918) This PR lays the groundwork for defining operation-specific `CustomizableOperation` methods. ## Codegen Example If we were to define pre-signable ops this way, code like the following would be emitted for each op that supported presigning. ```rust impl<E, B> CustomizableOperation<crate::operation::put_object::PutObject, E, B> { /// Sends the request and returns the response. #[allow(unused_mut)] pub async fn presigned( mut self, presigning_config: crate::presigning::PresigningConfig, ) -> ::std::result::Result<crate::presigning::PresignedRequest, crate::error::SdkError<E>> where E: std::error::Error + ::std::marker::Send + ::std::marker::Sync + 'static, B: crate::client::customize::internal::CustomizablePresigned<E>, { self.execute(move |sender, conf| sender.presign(conf, presigning_config)).await } } ``` _By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice._
This PR lays the groundwork for defining operation-specific
CustomizableOperation
methods.Codegen Example
If we were to define pre-signable ops this way, code like the following would be emitted for each op that supported presigning.
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.