-
-
Notifications
You must be signed in to change notification settings - Fork 496
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
RFC: Unify crate public APIs #4455
Comments
Researchbuilder patternhttps://github.com/colin-kiegel/rust-derive-builder
|
ProposalWhat am looking for is consistency and future compatibility. // Some tools return `Result`, some tools return recoverable errors.
// `OxcDiagnostic` can also contain warnings.
// When multiples tools are chained, errors can be chained as well.
pub struct ToolReturn {
pub errors: Vec<OxcDiagnostic>,
pub data: Data,
}
/// behavioral options
/// What should be the best pattern for breaking behavioral changes? e.g. Default value changed.
#{non_exhaustive]
#[derive(Default)]
pub struct ToolOptions {
pub foo: Foo,
}
impl ToolOptions {
pub fn with_foo(foo: Foo, yes: bool) -> Self {
if yes {
self.foo = foo;
}
self
}
}
pub struct Tool {
options: ToolOptions
}
impl Tool {
pub fn new(essential_args: EssentialArgs) -> Self {
Self { ... }
}
pub fn with_options(mut self, options: ToolOptions) -> Self {
self.options = options;
self
}
pub fn build(mut self, borrowed_arg: &BorrowedArg) -> ToolReturn {
ToolReturn { ... }
}
}
let options = ToolOptions::default().with_foo(foo, true);
let ret = Tool::new(essential_args).with_options(options).build(); |
Happy to dig in deeper next week, but a few smaller things I'd recommend keeping an eye on immediately:
|
Oh yes, I forgot to mention another goal is to enable |
Woo! 🎉 Feel free to ping me anytime if you run into any issues with it, or just want a second pair of eyes on anything! |
Just chiming in here, but I personally prefer builders that use |
#3485 Could be related. Hope Kind of hope the API is friendly for compiler to checkout breaking or unexpected changes. If the API requires users to use it with defensive programming in mind, it's probably not a good idea. |
We currently have an I plan to expose a The Every step should have an before and after interceptor (plugin if you will). When a user open the docs page of the Options API seems to be easier for this to work, compared to a builder pattern. Here's the building block I propose, so I can start changing all the sub-crates to be consistent with this pattern: struct CompilerOptions {
parser: ParserOptions,
semantic: Option<SemanticOptions>,
transform: Option<TransformOptions>,
minify: Option<MinifyOptions>,
codegen: Option<CodegenOptions>
}
struct Compiler {
}
impl Compiler {
pub fn new(options: CompilerOptions) {
}
pub fn run(self) {
}
} |
This PR adds a full compiler pipeline to the `oxc` crate, to stop us from implementing the same pipeline over and over again. relates #4455
I decided to not include any builder patterns in the public APIs, they are super confusing for new comers from JavaScript, who are used to passing objects with spread All the tools should conform to // `OxcDiagnostic` can also contain warnings.
// When multiples tools are chained, errors can be chained as well.
pub struct ToolReturn {
pub errors: Vec<OxcDiagnostic>,
pub data: Data,
}
/// Behavioral options
#[derive(Default)]
pub struct ToolOptions {
pub foo: bool,
}
pub struct Tool {
options: ToolOptions
}
impl Tool {
pub fn new(essential_args: EssentialArgs) -> Self {
Self { ... }
}
pub fn with_options(mut self, options: ToolOptions) -> Self {
self.options = options;
self
}
pub fn build(mut self, borrowed_arg: &BorrowedArg) -> ToolReturn {
ToolReturn { ... }
}
}
let options = ToolOptions { foo: true, ..ToolOptions::default() };
let ret = Tool::new(essential_args).with_options(options).build(); |
part of #4455 use `with_options` API instead
part of #4455 use `with_options` API instead
Boshen and I just discussed this on video chat. Plan is to standardize all the interfaces based on scheme above, and then to look at it again. But just to write it down before I forget, my suggestion for API is this:
The advantages are:
pub struct ToolReturn {
pub errors: Vec<OxcDiagnostic>,
pub data: Data,
}
#[derive(Default)]
pub struct ToolOptions {
pub foo: bool,
}
pub struct Tool {
options: ToolOptions
}
impl Tool {
pub fn new() -> Self {
Self { options: ToolOptions::default() }
}
// NB: Does not take `self`
pub fn with_options(options: ToolOptions) -> Self {
Self { options }
}
pub fn build(&self, arg1: &Arg1, arg2: &Arg2) -> ToolReturn {
// Pre-process options here
let foo_str = if self.options.foo { "yes" } else { "no" };
let tool_impl = ToolImpl { foo_str, arg1, arg2 };
tool_impl.run()
}
}
struct ToolImpl<'a> {
foo_str: &'a str,
arg1: &'a Arg1,
arg2: &'a Arg2,
}
impl<'a> ToolImpl<'a> {
fn run() -> ToolReturn {
// Do the stuff
}
}
// User-facing API
// With default options
let ret = Tool::new().build(arg1, arg2);
// With options
let options = ToolOptions { foo: true, ..ToolOptions::default() };
let ret = Tool::with_options(options).build(arg1, arg2);
// Reusing same options
let options = ToolOptions { foo: true, ..ToolOptions::default() };
let tool = Tool::with_options(options);
let ret = tool.build(arg1, arg2);
let other_ret = tool.build(other_arg1, other_arg2); |
During flights without internet access, I was confused about all the inconsistencies from our crates' public APIs (
oxc_parser
,oxc_transformer
,oxc_minifier
etc.) - builder pattern or options pattern?I'd like to research different Rust public API patterns, so we can have one less thing to do for v1.0.
The text was updated successfully, but these errors were encountered: