-
Notifications
You must be signed in to change notification settings - Fork 925
Audit public API #2639
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
Comments
With 0.6 I've significantly refactored Rustfmt's API, I'm proposing this for the 1.0 API. To summarise:
All need better documentation. I wonder if some of these we should indicate that they are 'de facto unstable'? (Thinking of |
Throwing my ideas: To me Why do you want to expose My concern with the API is that right now there's no real separation of concern between what needs to be formatted and when/how it is done. I believe the api should have one method which produce a report. Then we could have other modules which handle this report to, for example, overwrite the file, produce a diff, write a checkstyle report... If you think that's worth investigating, I could convert that into a more formal API and then into code |
We could use just
The API is used by the binaries and these are needed for the check style output. I couldn't figure out any way of hiding them.
Yeah, I think that is a good point. There is meant to be some distinction between
That would be great, thanks! |
Would this be something that bindgen could link to without having to mess with LD_LIBRARY_PATH? |
That would be the hope, yes |
That's great! I think that this will cover bindgen's use cases, which are essentially format this {path, string} with the default config or the config at this path. |
@nrc Thank you so much for your work! And my apologies for the late reply, I have been busy at work. Some comments:
|
Ok, so after giving it a fair amount of thoughts, here is my observation and suggestion (which of course I am keen to implement) Observations:
Suggestion in code : pub struct FormatResult {
filename: String,
result: Result<SuccessfulFormatting, io::Error>
}
pub struct SuccessfulFormatting{
orig_content: String,
formatted_content: String, // Formatted with correct platform line breaks
warnings: Vec<FormattingWarning>
}
pub struct FormattingWarning {
line: usize,
kind: FormattingWarningKind,
is_comment: bool,
is_string: bool,
line_buffer: String,
}
pub enum FormattingWarningKind {
LineOverflow(usize, usize),
TrailingWhitespace,
BadIssue,
LicenseCheck,
}
pub struct FormatConfig {
//Config related only to formatting
combine_control_expr: bool,
}
pub enum Input {
RustFile(PathBuf), // Link to a file
RustFileAsText{ file: PathBuf, content: String}, // A valid file content
Snippet{code: String} // An incomplete snippet of code
}
pub fn format_input(input: Input, config: &FormatConfig) -> impl Iterator<FormatResult> //Iterator or Stream if we are keen to depend on future
mod util {
pub fn generateCheckstyleOutput<T>(formatResult: T) -> String where T: Iterator<FormatResult>;
pub fn generateDiff<T>(formatResult: T) -> String where T: Iterator<FormatResult>;
pub fn hasModification<T>(formatResult: T) -> bool where T: Iterator<FormatResult>;
// !!! From here, not sure it should be in the API at all, depends on what is the usage of the API
pub struct ControlOptions {
//all options not related to the handling of the formatting result
report_todo: bool,
write_mode: WriteMode,
// .... TBD
}
pub fn load_config_file(file_path: Option<&Path>) -> Result<(Option<Path>, FormatConfig, ControlOptions), io::Error>;
pub fn overwriteFiles<T>(formatResult: T, options: &ControlOptions) -> io::Result<()> where T: Iterator<FormatResult>;
pub fn replaceFiles<T>(formatResult: T, options: &ControlOptions) -> io::Result<()> where T: Iterator<FormatResult>;
// Run the formatter and return true if successful, false otherwise. Successful meanings depends on the control options, write mode , etc..
pub fn run_formatter(input: Input, config: &FormatConfig, options: &ControlOptions) -> bool ;
} |
@nrc Looking at I have kind of ignored range formatting for now. I have some concerns but no definite way of solving it. I'll come back to it. I feel like I am kind of questioning everything and going down the route of a not-so-trivial refactor. I am unsure whether or not I am helping here. I am willing to give it a decent amount of time but I completely understand if you guys, @nrc and @topecongiro, are not keen to start something like that. Just let me know :) |
It's needed in Ranges and used by the RLS there
Why do you prefer the function to the const? I don't see much difference either way. We might not need this at all, depending on what we do with the CLI.
This sounds like a good idea |
This sounds like a good thing to do though
Is it? I'd have thought that even for a large project the source text would not be more than a few MB (e.g., the entire source distributable is 24MB)
I would prefer to be future-proof and allow for lots of detail in our errors, so wrapping io errors seems like a win with not much downside.
I somewhat agree with this I've wanted in the past to separate out a rustfmt-core crate which just does the core formatting stuff, and then have another library crate which does the file handling and ranges, etc. OTOH, I don't want to move much to the binaries because that significantly affects reusability. This might be a refactoring too far right now, I imagine it would be a big project. We might be better doing this in the future and I think we could be backwards compatible by using a facade crate.
Agree. I'm thinking of changing the CLI to not write modes, although I'm not sure how to represent them internally.
This is an interesting question. I guess at the limit, the binary is using the API too, so there has to be something there for the binary to hook into.
Agree, but there is no-one doing this right now, whereas I am very keen to release a 1.0 soon, so I'd rather keep unstable and deal with this post-1.0 |
Separating the config is an interesting idea and one that appeals to me. The downside is that we only want one config file so we'd either need to translate the single parsed config to the multiple API configs, or make the config macro even more complex. I generally disapprove of having a |
Fair point. I guess my point was that Iterator is more future proof. We could then implement something memory efficient and async in a backward compatible way. I am actually unclear about why do we want a stable API? Knowing that it's compiling only on nightly and reading at the main issue, nothing indicate that an API is needed. If we need a stable API just for RLS, then should we be stabilising the only function ( If the answer is yes, and taking into account your comments, what do you think about that minimal API : pub struct Error; //Implement failure::Fail
pub type Result<T> = std::result::Result<T, Error>;
pub struct SuccessfulFormatting{
formatted_content: String, // Formatted with correct platform line breaks, contains only the formatted bit if the range is not the whole file
warnings: Vec<FormattingWarning>
}
pub struct FormattingWarning {
line: usize,
kind: FormattingWarningKind,
is_comment: bool,
is_string: bool,
line_buffer: String,
}
pub enum FormattingWarningKind {
LineOverflow(usize, usize),
TrailingWhitespace,
BadIssue,
LicenseCheck,
}
pub struct FormatConfig {
//Config related only to formatting
combine_control_expr: bool,
}
pub fn format_rust_file<R: RangeArgument<i32>>(content: String, line_range: R, config: &FormatConfig) -> Result<SuccessfulFormatting> |
One aspect of the current API I'm really not happy about is how code for handling CLI options is split between the config module and the binary. We probably want to solve this by replacing |
This is gone altogether now. |
TODO list
|
The RLS and the Rustfmt binaries have to use the API. Once we release a 1.0, then we are bound by semver to stick to the API, however, I guess we could have a major point release pretty easily if we don't change the formatting (the other core component of stability). I also think having a stable API will encourage more users of rustfmt (various tools have considered using it). |
Looks like |
That's what my last suggestion is. RLS doesn't need write mode and file handling. Rustfmt bin needs it and I believe it should handle it itself. Exposing just |
Stabilising also means that it has to compile on stable right? |
Current public API: pub use config::options::CliOptions;
pub use config::summary::Summary;
pub use config::{load_config, Color, Config, FileLines, FileName, Verbosity, EmitMode};
pub enum ErrorKind
pub fn format_input<T: Write>(
input: Input,
config: &Config,
out: Option<&mut T>,
) -> Result<Summary, (ErrorKind, Summary)>
pub enum Input
pub fn format_and_emit_report(input: Input, config: &Config) -> Result<Summary, failure::Error>
pub fn emit_pre_matter(config: &Config) -> Result<(), ErrorKind>
pub fn emit_post_matter(config: &Config) -> Result<(), ErrorKind> This is much better, but I still think there is room for improvement |
No, I don't think we can due to our dependence on libsyntax |
I'm thinking to leave any movement here for later - all the control options are unstable, so we don't need to finalise the format before 1.0 and can change later (that will probably mean changing the API, but hopefully they will be back compat or at least minor changes). |
I feel I might be misunderstanding something, but this statement:
seems to contradict this statement:
since linking internal libraries like libsyntax requires LD_LIBRARY_PATH fiddling. I think it also means that Am I misunderstanding? |
More tweaking:
|
Some thoughts:
|
replaced with isatty crate
I think we need to keep |
OK, I think I'm done:
@topecongiro @thibaultdelor what do you think? |
One thought - I wonder if we should combine the |
I think we should, and probably reorganise the summary too. |
@nrc I like where you are going! Here's my feedback
The reason I think we should really separate concerns is because right now I think the API is internally overly complex and might trick a lot of person. After just a quick look, here's few things I find unexpected in
Whether or not those behaviour are expected or not is not my point. I am just thinking that as a user of the rustfmt API i just want to provide an unformatted input and get a formatted output. Doing the rest (generate a checkstyle report, overriding files) is trivial and can be seen as extension. |
Hmm, those points make a lot of sense. My thoughts to the summary are that we can merge it into the error report and the values are just caches of the computed values, but that the output is an 'out' param that we pass into I think one fundamental problem is the complexity of the problem - we can't abstract a writer, because in So we end up with this tension, between a simple API which feels like a bit of an inelegant mash of functionality, but is in practice very easy for clients to use, or a more elegant, orthogonal API, but which is larger and more complex. Another tension is between formatting a single program (which feels like a natural unit to expose in the API) and formatting a batch of programs (which is what the CLI allows, and leads to requiring combining of I guess I need to think more about what the really fundamental operations are. |
Okay I guess now it's my time to do some coding. What I am thinking about requires a fair amount of refactoring that can't be done incrementally. |
Well, I couldn't work much on it this week. I'll try to give it more love but I can understand if you would like to move forward and go with this version. :) |
I think it is worth pressing on a bit here and trying to get a nice API. On the other hand, given the small number of clients, I think it would be fine to release a 2.0 of Rustfmt which only changed the API (and not formatting) a few months after 1.0. |
@nrc I have started worked on it again and ended up with lots of of merge conflict... |
In 71d3d04 I factored out a |
I'm also finding the current setup with Summary to be weird. I think it probably wants to be encapsulated by |
In a series of commits up to d328884, I've done a bunch of refactoring of the API. It is not complete, but I think we only need to add to it (i.e., backwards compatibly to get where we want). The key difference for clients is the addition of a Internally, I've separated out the file handling etc. (in The last thing I want to consider is merging @thibaultdelor what do you think? |
And now |
@nrc Awesome work! |
@thibaultdelor cool, thanks for looking over it and no worries about not getting to it, work happens. I'm going to close this issue, we can make improvements elsewhere. |
No description provided.
The text was updated successfully, but these errors were encountered: