Skip to content
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

extract library #314

Merged
merged 46 commits into from
Feb 21, 2023
Merged

extract library #314

merged 46 commits into from
Feb 21, 2023

Conversation

MarcoIeni
Copy link
Contributor

@MarcoIeni MarcoIeni commented Jan 21, 2023

A step towards #67
To keep things easier I just created a new lib.rs
In the future, we could extract this lib.rs file into a core crate, that doesn't depend on clap.

/// Latest version published to the cargo registry.
#[default]
LatestVersion,
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to group conflicting cli arguments together in enums.

src/lib.rs Outdated
/// It can be a workspace or a single package.
Manifest(PathBuf),
/// The rustdoc json of the current version of the project.
RustDoc(PathBuf),
Copy link
Contributor Author

@MarcoIeni MarcoIeni Jan 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I saw that this rustdoc also requires the Baseline rustdoc.
At the moment this is not captured by the type system.

  • How would you deal with this?
  • Should we move it outside of this enum?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of these allow the baseline rustdoc to be specified explicitly, while only one option requires it. This makes the decision tricky. It feels like this enum isn't at the right level of abstraction.

The baseline and the current rustdoc can be one of three things:

  • already generated, just load it from an existing file
  • generate it based on code at some path, optionally first checking out a gitrev (maybe better split into two cases? what do you think?)
  • generate it based on a registry version (in the current CLI, only the baseline can do this, but @tonowak has a lib use case where it would be useful for the current rustdoc as well)

Perhaps a pub enum RustdocSource like that might be better? Then the CLI can have its own structs and behaviors around "current directory" etc. For testability reasons, it might be best if the library is ignorant of the current directory and always expects explicit paths to be fed to it.

Not set on it, just wanted to suggest an idea to look at.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we want to create a RustdocSource struct that can be used both by the Baseline and Current rustdoc?

already generated, just load it from an existing file

enum RustdocSource {
    RustDoc(PathBuf)
}

generate it based on code at some path, optionally first checking out a gitrev (maybe better split into two cases? what do you think?)

enum RustdocSource {
    RustDoc(PathBuf),
    Root(PathBuf),
    // (path to root, rev)
    Revision(PathBuf, String)
}

generate it based on a registry version

enum RustdocSource {
    RustDoc(PathBuf),
    Root(PathBuf),
    // (path to root, rev)
    Revision(PathBuf, String),
    Version(Option<String>),
}

This looks similar to the current Baseline enum. Is it valid for Current, too?
If I didn't get it, can you give me the structure of the enum you were thinking about?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it looks reasonable. It currently isn't valid for Current in the CLI, but I think it should be in the lib. Not much reason not to: that would make the types more complex while providing less functionality.

Small nit: let's call the variant Rustdoc instead, since the underlying tool doesn't capitalize the D either.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It currently isn't valid for Current in the CLI, but I think it should be in the lib.

FYI @MarcoIeni, the recent merged chain of PRs (which ended with #351) made it so that the current crate's rustdoc is generated in the same way as the baseline's rustdoc, so now it's possible to create the current rustdoc from registry and from git revision -- your RustdocSource now is also fully valid for the current crates.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And also, this chain of PRs renamed quite a lot of things in the source code, so there might be a big merge conflict, sorry :( I can try to merge main into this branch when I'll have some free time.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you could help with the merge, that would be great @tonowak. Probably the highest impact next thing you can do right now, TBH.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I'll do it in max few days.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

src/lib.rs Show resolved Hide resolved
pub fn with_manifest(&mut self, path: PathBuf) -> &mut Self {
self.current = Current::Manifest(path);
self
}
Copy link
Contributor Author

@MarcoIeni MarcoIeni Jan 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used a builder pattern that matches the cli args.
style of builder pattern: https://rust-lang.github.io/api-guidelines/type-safety.html#non-consuming-builders-preferred

src/lib.rs Outdated
Current::Manifest(path) => path.clone(),
Current::RustDoc(_) => {
anyhow::bail!("error: RustDoc is not supported with these arguments.")
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about this.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, it doesn't seem great. Perhaps the rustdoc-source restructuring might help?

src/lib.rs Show resolved Hide resolved
@MarcoIeni MarcoIeni marked this pull request as ready for review January 21, 2023 23:15
Copy link
Owner

@obi1kenobi obi1kenobi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very excited about this, thanks for putting it together!

src/lib.rs Outdated
/// It can be a workspace or a single package.
Manifest(PathBuf),
/// The rustdoc json of the current version of the project.
RustDoc(PathBuf),
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of these allow the baseline rustdoc to be specified explicitly, while only one option requires it. This makes the decision tricky. It feels like this enum isn't at the right level of abstraction.

The baseline and the current rustdoc can be one of three things:

  • already generated, just load it from an existing file
  • generate it based on code at some path, optionally first checking out a gitrev (maybe better split into two cases? what do you think?)
  • generate it based on a registry version (in the current CLI, only the baseline can do this, but @tonowak has a lib use case where it would be useful for the current rustdoc as well)

Perhaps a pub enum RustdocSource like that might be better? Then the CLI can have its own structs and behaviors around "current directory" etc. For testability reasons, it might be best if the library is ignorant of the current directory and always expects explicit paths to be fed to it.

Not set on it, just wanted to suggest an idea to look at.

src/lib.rs Outdated Show resolved Hide resolved
src/lib.rs Show resolved Hide resolved
src/lib.rs Outdated
Current::Manifest(path) => path.clone(),
Current::RustDoc(_) => {
anyhow::bail!("error: RustDoc is not supported with these arguments.")
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, it doesn't seem great. Perhaps the rustdoc-source restructuring might help?

src/lib.rs Outdated Show resolved Hide resolved
src/lib.rs Outdated
Comment on lines 59 to 69
#[derive(Default)]
struct Scope {
selection: ScopeSelection,
excluded_packages: Vec<String>,
}

/// Which packages to analyze.
#[derive(Default, PartialEq, Eq)]
enum ScopeSelection {
/// Package to process (see `cargo help pkgid`)
Packages(Vec<String>),
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we define explicitly what happens if ScopeSelection::Packages contains a package that's also in excluded_packages? I'm not sure what should happen, or what cargo does assuming the same thing can happen in cargo. But our lib docs should state the expected behavior in any case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In 1190770 I have made it impossible for this to happen

src/lib.rs Outdated Show resolved Hide resolved
MarcoIeni and others added 2 commits January 22, 2023 09:36
Co-authored-by: Predrag Gruevski <2348618+obi1kenobi@users.noreply.github.com>
@tonowak
Copy link
Collaborator

tonowak commented Jan 25, 2023

Hi @MarcoIeni! It turns out that to complete #207 I need exactly what you're doing in this PR -- allowing the tool to be usable as a library. Thus, if you think it would be helpful, I would be happy to help you with this PR. What do you think? Is there anything in particular I could help you with, or are you progressing nicely and don't need any help?

@MarcoIeni
Copy link
Contributor Author

MarcoIeni commented Jan 25, 2023

Hey @tonowak, if I'm blocking you, feel free to contribute to this pr, yes 👍
For example, today I won't have time to look into this.
Feel free to commit directly into the branch, or to raise prs against it. 👍

I was thinking that with the type system, we could avoid the possibility of specifying packages together with the other options.
However, excluded_packages should be compatible with "workspace" or "default members" options.

src/lib.rs Outdated Show resolved Hide resolved
@MarcoIeni
Copy link
Contributor Author

Thanks for helping Tomasz 😄
I think I have addressed all the PR comments. I won't edit this PR anymore unless you still need something else from my side 👍

src/lib.rs Outdated
/// If `None`, uses the largest-numbered non-yanked non-prerelease version
/// published to the cargo registry. If no such version, uses
/// the largest-numbered version including yanked and prerelease versions.
Version(Option<String>),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it would be better to call it Registry instead of Version? It would make it more clear that it requires internet access to use it and from where this specific version is being taken from.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about RegistryVersion? 😄
The user when seeing Registry with a string might think about Registry("crates.io") for example

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that might be better 👍 .

... but going with this logic, the user might think that in RegistryVersion one has to pass the version of the registry to use, not the version of the crate to compare with.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Naming is hard 😁
VersionFromRegistry is more verbose, but more clear maybe. 🤔

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with either RegistryVersion or VersionFromRegistry. I'm not that worried about people thinking that they need to pass a version of a registry, but I'm willing to accept I may be overly optimistic if you think the other option is better.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you both think about 8485cc5?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's fine :)
I would not try to make the library perfect in this PR. Otherwise we will have to deal with too many merge conflicts.

I guess (and hope) that the library will be extracted in a separate crate, so we can still change the library API in future PRs.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR is on my to-do list to review today. Sorry for the delay! Trustfall ended up in the spotlight (it was GitHub's #​1 most-starred project a couple of days ago) and I've been super busy over there for the last couple of days.

I know I probably just merged another PR that causes another merge conflict, and I'm happy to fix the mess of my own making 😅

src/lib.rs Outdated
RustdocSource::Rustdoc(_)
| RustdocSource::Revision(_, _)
| RustdocSource::VersionFromRegistry(_) => {
let name = "<unknown>";
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While developing #207 I've found a bug: when both crates are generated from registry, this code tries to find a package named <unknown> in registry. From how the interface looked, I was convinced that it is enough to pass .with_packages(vec![name]) to Check, but now when I look at the lib.rs, I don't think it's what it's supposed to accept. So, in the scenario "from registry & from registry", there is no way to pass the name of the crate to check.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In #207 I've made a temporary fix 7ecf587 so that I can progress further. In the case where this fix is good enough, I can move it here, otherwise we should discuss better alternatives.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In 7ecf587 it seems like there's still an unknown branch. That looks like it could work if current is given by gitrev, but won't work with a registry version nor with a premade rustdoc file.

Can we add an explicit panic!() in the cases we know for sure won't work, instead of setting a bogus value and then breaking in a confusing way?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 7516ab0.

Copy link
Owner

@obi1kenobi obi1kenobi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks in pretty good shape. Let's target merging tomorrow or Monday morning if possible, so I can include this in the next release (also planned for Monday at the moment).

src/lib.rs Outdated
RustdocSource::Rustdoc(_)
| RustdocSource::Revision(_, _)
| RustdocSource::VersionFromRegistry(_) => {
let name = "<unknown>";
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In 7ecf587 it seems like there's still an unknown branch. That looks like it could work if current is given by gitrev, but won't work with a registry version nor with a premade rustdoc file.

Can we add an explicit panic!() in the cases we know for sure won't work, instead of setting a bogus value and then breaking in a confusing way?

src/lib.rs Outdated
} else if let Some(path) = get_target_dir_from_project_root(&self.baseline.source)? {
path
} else {
get_xdg_cache_dir()
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect this won't work on Windows, unfortunately. Let's instead use the directories crate which is the known-working cross-platform way to do this:

if let Some(proj_dirs) = ProjectDirs::from("cargo-semver-checks") {
    proj_dirs.config_dir();
    // probable dirs (still need to be created! the library won't make them for us)
    // Lin: /home/alice/.config/cargo-semver-checks
    // Win: C:\Users\Alice\AppData\Roaming\cargo-semver-checks\config
    // Mac: /Users/Alice/Library/Application Support/cargo-semver-checks
}

@obi1kenobi
Copy link
Owner

I took care of the merge, it wasn't that bad because I reviewed the code that caused a conflict. Lmk what you think about the panic!() branch and using the cross-platform-compatible app dir crate!

Really looking forward to merging this soon! 🚀🎉

@MarcoIeni
Copy link
Contributor Author

Lmk what you think about the panic!() branch and using the cross-platform-compatible app dir crate!

Is this question for me? If yes, what's the panic!() branch?

@obi1kenobi
Copy link
Owner

The two remaining items needed to merge this are:

I unfortunately have had to shift my primary focus to Trustfall for a bit, given the recent massive uptick in interest in the project. I think the remaining items aren't much work and I trust both of you to do a good job on them.

@MarcoIeni
Copy link
Contributor Author

I replaced xdg with directories. I leave the other fix to tonowak :)

@tonowak
Copy link
Collaborator

tonowak commented Feb 18, 2023

Thanks :) I'll probably do the other fix in the next 48h.

Copy link
Owner

@obi1kenobi obi1kenobi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks polished enough to release, let's not block @MarcoIeni's release-plz work any longer. We will certainly keep improving the library APIs over time, we don't have to get to "perfect" on day 1.

Thanks, everyone!

@obi1kenobi obi1kenobi enabled auto-merge (squash) February 21, 2023 14:17
@obi1kenobi obi1kenobi merged commit 51803af into obi1kenobi:main Feb 21, 2023
@MarcoIeni
Copy link
Contributor Author

Thanks!
This week I'll work on extracting the library into a separate crate.
In this way, the users of the library don't need to compile clap and other specific libraries used only in the cli.

Some naming ideas:

  • semver-checks
  • cargo-semver-checks-core

Please, reserve in crates.io the one you prefer :)

@obi1kenobi
Copy link
Owner

I grabbed both, just to be extra safe. semver-checks seems good to me, but I don't have a strong preference.

My 2 cents: splitting the library into a separate crate would definitely be nice, but it doesn't seem that critical to me. The community impact of having semver-checking made automatic via release-plz seems bigger, and we can always split the library into a separate crate and cut down compile times later.

Of course, release-plz is your crate, this is your work, and it's your call to make :)

@tonowak
Copy link
Collaborator

tonowak commented Feb 21, 2023

Maybe it would be reasonable to have the clippy and CLI under a default flag?

@MarcoIeni
Copy link
Contributor Author

CLI under a default flag

This would prevent us to ship breaking changes independently for the cli and for the library.

For example, if we change a command line argument, should it be a breaking change?

Having two separate crates allows us to ship breaking changes for cli and the lib independenlty

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.

3 participants