Skip to content

refactor#208

Merged
mre merged 4 commits intolycheeverse:masterfrom
lebensterben:develop
Apr 14, 2021
Merged

refactor#208
mre merged 4 commits intolycheeverse:masterfrom
lebensterben:develop

Conversation

@lebensterben
Copy link
Member

@lebensterben lebensterben commented Apr 3, 2021

Changes:

  • The binary component and library component are separated as two packages in the same workspace.
    • lychee is the binary component, in lychee-bin/*.
    • lychee-lib is the library component, in lychee-lib/*.
    • Users can now install only the lychee-lib, instead of both components, that would require fewer dependencies and faster compilation.
    • Dependencies for each component are adjusted and updated. E.g., no CLI dependencies for lychee-lib.
    • CLI tests are only moved to lychee, as it has nothing to do with the library component.
  • Status::Error is refactored to contain dedicated error enum, ErrorKind.
    • The motivation is to delay the formatting of errors to strings. Note that e.to_string() is not necessarily cheap (though trivial in many cases). The formatting is no delayed until the error is needed to be displayed to users. So in some cases, if the error is never used, it means that it won't be formatted at all.
  • Replaced regex based matching with one of the following:
    • Simple string equality test in the case of 'false positivie'.
    • URL parsing based test, in the case of extracting repository and user name for GitHub links.
    • Either cases would be much more efficient than regex based matching. First, there's no need to construct a state machine for regex. Second, URL is already verified and parsed on its creation, and extracting its components is fairly cheap. Also, this removes the dependency on lazy-static in lychee-lib.
  • types module now has a sub-directory, and its components are now separated into their own modules (in that sub-directory).
  • lychee-lib::test_utils module is only compiled for tests.
  • wiremock is moved to dev-dependency as it's only needed for test modules.
  • Dependencies are listed in alphabetical order.
  • Imports are organized in the following fashion:
    • Imports from std
    • Imports from 3rd-party crates, and lychee-lib.
    • Imports from crate::* or super::*.
  • No glob import.
  • I followed suggestion from cargo clippy, with clippy::all and clippy:pedantic.

Benchmark results of running the refactored version and the current origin/HEAD version is listed below.
The sample size is very small (n=6)
Frankly, I don't know which part contribute to most of improvement.

@lebensterben
Copy link
Member Author

The data is obtained by heaptrack, and I did some t-tests.

My conclusion is there's no significant improvement in speed, but in memory usage.

The difference in run-time is insignificant.

data:  c(126.91, 129.91, 133.03, 130.06, 126.17, 128.88) and c(125.21, 137.37, 111.35, 128.21, 127.47, 118.25)
t = 1.1916, df = 5.7553, p-value = 0.2802
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -4.854592 13.887925
sample estimates:
mean of x mean of y
 129.1600  124.6433

My refactored version has 3.22% fewer calls to memory allocation in total.

data:  c(5290434, 5240945, 5275566, 5263350, 5262841, 5244248) and c(5434387, 5309354, 5455931, 5449631, 5484685, 5496514)
t = -6.1582, df = 5.7684, p-value = 0.000975
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -245945.6 -105093.8
sample estimates:
mean of x mean of y
  5262897   5438417

The memory allocation calls per second is less significant, and it's 7.03% fewer.

calls/s:
data:  c(41687, 40341, 39658, 40470, 41713, 40689) and c(43403, 38649, 48996, 42505, 43025, 46480)
t = -2.0728, df = 5.5129, p-value = 0.0877
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -6802.6509   635.9842
sample estimates:
mean of x mean of y
 40759.67  43843.00

The difference in temporary memory allocation in total and per second are not significant

data:  c(510614, 604372, 515672, 514999, 517575, 501116) and c(522805, 450923, 525175, 513906, 524612, 517626)
t = 0.93237, df = 9.3099, p-value = 0.3747
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -25758.59  62192.25
sample estimates:
mean of x mean of y
 527391.3  509174.5
data:  c(4023, 3882, 3876, 3959, 4102, 3888) and c(4175, 3282, 4716, 4008, 4115, 4377)
t = -0.79212, df = 5.371, p-value = 0.4618
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -656.7887  342.4554
sample estimates:
mean of x mean of y
 3955.000  4112.167

My refactored version has 12.79% fewer peak memory usage, and it's very significant.

peak heap:
data:  c(29.88, 29.43, 30.81, 31.18, 30.89, 31.02) and c(33.69, 35.33, 34.23, 34.08, 35.28, 36.04)
t = -9.0237, df = 9.4371, p-value = 6.048e-06
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -5.295475 -3.184525
sample estimates:
mean of x mean of y
   30.535    34.775

My refactored version has 4.14% fewer peak resident memory.

data:  c(108.36, 111.94, 111.99, 108.42, 118.81, 107.58) and c(114.86, 116.04, 115.83, 116.75, 116.85, 115.59)
t = -2.7615, df = 5.3159, p-value = 0.03724
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -9.195788 -0.410879
sample estimates:
mean of x mean of y
 111.1833  115.9867

The only downside, I have significantly much more leaked memory (25.90% increase).
My guess is this probably is caused by my usage of Box<dyn Error> (as a result of refactoring Status::Error) or static reference of RegexSet.

This should not be a big deal though. And notice that heaptrack's analysis on memory leak is not as fine as valgrind, so it could overestimate.

data:  c(279.68, 301.38, 299.75, 322.15, 300.96) and c(242.67, 251.02, 240.71, 232.52, 232.52, 234.05)
t = 8.4081, df = 5.572, p-value = 0.0002269
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 43.52331 80.21469
sample estimates:
mean of x mean of y
  300.784   238.915

@lebensterben
Copy link
Member Author

In the latest forced push, I replaced regex based matching with url parsing based matching.
Note that we stored URL in URI, where URL is parsed and verified when created.
And extracting host, path, or any other components from URL is cheap.

So for example, to extract repo and user name from a github URL, there's really no need to use regex at all.

@mre
Copy link
Member

mre commented Apr 4, 2021

This is great! Love the improvements. 👏
One thing: can we keep lychee for the name of the library instead of lychee-lib? I'd like to keep the old name because it looks a bit nicer when calling it. 🙂

@lebensterben
Copy link
Member Author

Need to do more investigation on the performance changes.

Then should lychee-bin be the name for the binary?

Also, the CI would fail unless the library component is actually published.

@mre
Copy link
Member

mre commented Apr 5, 2021

Need to do more investigation on the performance changes.

Thanks. 👍

Then should lychee-bin be the name for the binary?

Yes, please.

Also, the CI would fail unless the library component is actually published.

All good. Just let me know when this is ready for review and I'll try it locally and then we can merge this.

@lebensterben
Copy link
Member Author

figure1

It looks good, but there are a few outliers.

@lebensterben
Copy link
Member Author

lebensterben commented Apr 7, 2021

To replicate, you need to install heaptrack

git checkout develop
cargo clean && cargo build --release --all
mkdir develop
cd develop
for _i in {0..9}; do heaptrack ../target/release/lychee --no-progress -E https://wiki.archlinux.org/index.php/List_of_applications &> /dev/null; done
popd

git checkout master
cargo clean && cargo build --release
mkdir master
cd master
for _i in {0..9}; do heaptrack ../target/release/lychee --no-progress -E https://wiki.archlinux.org/index.php/List_of_applications &> /dev/null; done
popd

echo "ID, total_runtime, calls_to_alloc, calls_to_alloc_per_second, temp_mem_alloc, temp_mem_alloc_per_second, peak_heap_mem_MB, peak_RSS_MB, total_mem_leaked_KB" > data.csv
for _i in ./develop/heaptrack*; do echo "ID: develop"; heaptrack_print $_i | tail -n 6; done >> data.csv
for _i in ./master/heaptrack*; do echo "ID: master"; heaptrack_print $_i | tail -n 6; done >> data.csv

The next part is data cleaning. You can do that in vim, store this in process.vim.

2,$s/[[:alnum:]| ()]*: //
2,$s/s\.$\|MB$\|KB$//
2,$s/\ (/\r/
2,$s/\/s)$//
2,$j
2s/ \(developer\|master\)/\r\1/g
2,$s/ /, /g

Then

  • vi data.csv
  • :source process.vim

Next is to generate the output, you need R and install a few packages

install.packages(c("ggplot2", "ggpubr", "stringi", "data.table", "rlang", "rstatix"))

library(ggplot2)
library(ggpubr)
library(stringi)
library(data.table)
library(rlang)
library(rstatix)

DT <- data.table::fread(file = "data.csv")

pps <-
  lapply(names(DT)[-1],
         function (var) {
           df <- DT[, .SD, .SDcols = var, by = ID]
           caption <-
             DT %>% t_test(new_formula(sym(var), sym("ID"))) %>% get_test_label(detailed = TRUE)
           names(df) <- c("ID", "Measure")
           ggplot(df, aes(x = ID, y = Measure, color = ID)) +
             geom_boxplot() +
             geom_jitter() +
             labs(x = "", y = "", caption = caption) +
             theme_minimal() +
             theme(legend.position = "none")
         })

labels <-
  sapply(names(DT)[-1],
         function (name) {
           name %>%
             stri_replace_all_fixed("_", " ") %>%
             stri_trans_totitle() %>%
             stri_replace_last_fixed("b", "B")
         },
         USE.NAMES = FALSE)

pp <-
  eval(expr(
    ggarrange(
      !!!pps,
      labels = labels,
      ncol = 2,
      nrow = 4,
      font.label = list(size = 10)
    )
  ))

ggexport(
  pp,
  filename = "figure1.png",
  width = 1000,
  height = 1400,
  res = 100
)

@lebensterben
Copy link
Member Author

figure1

When running on test data (those under fixtures), my refactored version is way much better.

@lebensterben
Copy link
Member Author

I'm replacing ad-hoc errors (via anyhow) to structural errors (via this-error).
The biggest disadvantage of anyhow is that errors are basically strings which is readable to human readers, but the computer library is not able to differentiate different types of errors.

@mre
Copy link
Member

mre commented Apr 7, 2021

I'm replacing ad-hoc errors (via anyhow) to structural errors (via this-error).

Yeah that was on the list anyway. Thanks for tackling this as well. 😀

@lebensterben
Copy link
Member Author

lebensterben commented Apr 8, 2021

@mre

I had handwritten the errors without using this-error.
You can take a look.

Another advantage is that I get rid of all dynamic dispatch of Errors. All errors have size known at compile time.

The downside is I have to implement PartialEq, Eq, and Hash manually as errors in many upstream crates doesn't implement them, while Result<Response> is kept in a HashMap so these traits are required.

For lychee-bin, I kept the dependency of anyhow but I can remove it if you want. For binary package using ad-hoc error probably is more convenient in the long run.

As for the naming:
If you want the library component to keep the name lychee, it will cause breaking error in CIs and other applications that uses cargo install lychee. That's why I have the binary component to take the name.

@lebensterben
Copy link
Member Author

lebensterben commented Apr 8, 2021

If this looks good I will do some final clean ups and rebase the commits.

}
FALSE_POSITIVES.is_match(input)
pub fn is_false_positive(input: &str) -> bool {
input == FALSE_POSITIVE_PAT[0]
Copy link
Member

Choose a reason for hiding this comment

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

Even though FALSE_POSITIVE_PAT only contains one string so far, I wonder if we still want to iterate through all elements of that slice already so that we don't forget to update this code once we start adding more strings to FALSE_POSITIVE_PAT. What do you think?

Copy link
Member Author

Choose a reason for hiding this comment

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

In the future we may want. So this is not future-proof.

But it's really hard to come up with a second 'false-positive' example.
Also, I haven't thought thoroughly but the naming 'false-positive' is not clear enough.

Copy link
Member

Choose a reason for hiding this comment

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

True. Let's keep it like that then. Also can't come up with a better name at the moment. Maybe something like IGNORE_PATTERNS or KNOWN_PHONY_URLS could work. 🤔 We can come up with something better in the future.

let url = if let Uri::Website(url) = self {
url
} else {
unsafe { std::hint::unreachable_unchecked() }
Copy link
Member

Choose a reason for hiding this comment

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

Just wanted to mention that I still have mixed feelings about the use of unsafe. 😄
It's fine and we can merge it like that, but I don't see the need for introducing unsafe if the perf improvement is not significant and there are no other significant upsides. It might be beneficial, but we'd have to measure that.
Yes, I'm aware that this totally safe in this case with the debug_assert, but why introduce unsafe when we can have a fully "safe" crate with 99% of the upsides?
My main concern is about future refactorings and missing an edge-case.
Sorry, don't want to start another long discussion. It's just a note that we have to revisit that topic and we might revert that decision in the future. 😅

Copy link
Member Author

Choose a reason for hiding this comment

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

Well for this particular case, I'm able to avoid writing any explicit unwrap, expect, unrechable or std::hint::unreachable_unchecked.

        if let Uri::Website(url) = self {
            if matches!(url.domain(), Some("github.com") | Some("www.github.com")) {
                if let Some(mut path) = url.path_segments() {
                    if let Some(owner) = path.next() {
                        if let Some(repo) = path.next() {
                            return Some((owner, repo));
                        }
                    }
                }
            }
        }

Copy link

@drahnr drahnr Apr 23, 2021

Choose a reason for hiding this comment

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

Extracting this in a separate fn extract_github_info(url: &_) -> Option<(String, String)> { .. } would allow you to simply use the ? operator (Option<_> impls Try) and there would no need to be a single nesting layer 😃

Copy link
Member

Choose a reason for hiding this comment

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

Good idea @drahnr. Would you be willing to open a PR for that?

Copy link

Choose a reason for hiding this comment

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

Actually, it was already implemented as extract_github in a follow up which I missed

Copy link
Member

Choose a reason for hiding this comment

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

All good then. 👍

@mre
Copy link
Member

mre commented Apr 8, 2021

That pull request is great! I learned a lot about making the code more ergonomic.
Just added a few more hints and then we're good to go.

Let's keep lychee-lib because you have a point about cargo install lychee. My guess is that more people will use the cli than the library. 👍

@lebensterben
Copy link
Member Author

Well a few things happened during last few days.

I further slimmed downed the new ErrorKind and figured out that we can have HttpError(reqwest::Error), instead of HttpError(String, StatusCode). Note that we can extract the error message and status code from reqwest::Error.

The only downside is we are not able to create create a superficial http error with empty message and a status code. The impact is minimal because we only need to do that in tests ( specifically lychee::stat::test module), and I figured out a way to fix this by returning a real reqwest::Error from a mock server. This would be a bit more resource intensive during test but no impact on any other parts of the program.

I updated dependencies, and ordered in alphabetically. Now wiremock would be a dev-dependency and lychee-lib::test_utils is only compiled for tests. The downside is I have to duplicate two functions in lychee (binary component)'s test modules. But cutting down dependency would greatly benefit CI usage of lychee.

@lebensterben
Copy link
Member Author

And about the performance gain.

I wrote quite a lot on the benchmarks. To sum up

  • I found no significant change in speed when testing on the real world case ( that arch wiki page with hundreds of external links ). But my refactored version cuts off many memory allocations.
  • I found HUGE improvement in both speed and memory allocations when testing on fixutres/*. It's actually 4 times faster.

So

  • The bottom line is my refactor is not making things worse.
  • My refactor seems to optimize extremely well with certain cases.

My guess is, fixtures/* contains links which are either right away excluded, or known to work or known to fail. There's hardly any timeouts. So timeout is the real culprit why you see no improvement in speed.

To verify my hypothesis, I made a markdown file with top 1,000 websites in Alexa's rank, and removed any of them that timed out.

𝛌> hyperfine --min-runs 5 -i './develop/lychee --no-progress -E -t 5 top-1k-no-timeout.md' './master/lychee --no-progress -E -t 5 top-1k-no-timeout.md'
Benchmark #1: ./develop/lychee --no-progress -E -t 5 top-1k-no-timeout.md
  Time (mean ± σ):     29.645 s ±  1.900 s    [User: 1.379 s, System: 0.471 s]
  Range (min … max):   27.431 s … 31.543 s    5 runs

  Warning: Ignoring non-zero exit code.

Benchmark #2: ./master/lychee --no-progress -E -t 5 top-1k-no-timeout.md
  Time (mean ± σ):     33.484 s ±  6.254 s    [User: 1.392 s, System: 0.474 s]
  Range (min … max):   26.550 s … 39.519 s    5 runs

  Warning: Ignoring non-zero exit code.

Summary
  './develop/lychee --no-progress -E -t 5 top-1k-no-timeout.md' ran
    1.13 ± 0.22 times faster than './master/lychee --no-progress -E -t 5 top-1k-no-timeout.md'

As you can see, my refactored version IS faster for about 13%.

@mre
Copy link
Member

mre commented Apr 13, 2021

@lebensterben, guess there is one more CI issue to fix before we can finally merge this:

 error[E0277]: the trait bound `types::error::ErrorKind: From<reqwest::error::Error>` is not satisfied
   --> src/types/error.rs:126:45
    |
126 |             hubcaps::Error::Reqwest(e) => e.into(),
    |                                             ^^^^ the trait `From<reqwest::error::Error>` is not implemented for `types::error::ErrorKind`

Source: https://github.com/lycheeverse/lychee/pull/208/checks?check_run_id=2331614860

@lebensterben
Copy link
Member Author

lebensterben commented Apr 13, 2021

@mre
This doesn't make any sense...
It passes the cargo test CI, and how's it possible to fail?

https://github.com/lebensterben/lychee/blob/d994af2da29315378efc33ebc3bde503deec0024/lychee-lib/src/types/error.rs#L123-L131

The error comes from L126 where it coverts a specific discriminant of hubcaps::Error to our custom ErrorKind. Note that e here is of type reqwest::Error. https://github.com/softprops/hubcaps/blob/4f992af64d69d5ff51a64c7ae7f89762299b6d65/src/errors.rs#L29

And the rule for converting reqwest::Error to `ErrorKind is provided in

https://github.com/lebensterben/lychee/blob/d994af2da29315378efc33ebc3bde503deec0024/lychee-lib/src/types/error.rs#L117-L121

@lebensterben
Copy link
Member Author

BTW...

I've found other interfaces for further refactor. But I will keep them in another PR.
I'll be back to the HTTPS PR later.

@lebensterben
Copy link
Member Author

@mre

You didn't merge it right. Now the problem should be fixed.

@lebensterben
Copy link
Member Author

lebensterben commented Apr 14, 2021

Since it passes both cargo test and cargo lint, I'm going to squash those commits.


Merge with care. I almost lost all the work...


Added a few links. See lychee-lib/src/lib.rs and lychee-bin/src/main.rs, which supersedes #219.

@lebensterben lebensterben force-pushed the develop branch 4 times, most recently from 8db2423 to 5e6faae Compare April 14, 2021 02:53
@mre
Copy link
Member

mre commented Apr 14, 2021

You didn't merge it right. Now the problem should be fixed.

Resolved the conflict via Github directly as it only showed a single file to be changed: the Cargo.toml in the project root. It looked like a trivial change because the diff only showed that the dependencies had moved into the individual lib and bin crates, so I wonder how this could have happened. My only guess is that Github squashed the commits right after and deleted the history. 😨 Anyway, sorry for that.

I'm seeing a list of conflicting files now. Given my bad experiences with the last merge, perhaps you could try to resolve that this time to avoid further churn.

Other than that, let's merge it in. 😊

@mre mre mentioned this pull request Apr 14, 2021
- The binary component and library component are separated as two
  packages in the same workspace.
  - `lychee` is the binary component, in `lychee-bin/*`.
  - `lychee-lib` is the library component, in `lychee-lib/*`.
  - Users can now install only the `lychee-lib`, instead of both
    components, that would require fewer dependencies and faster
    compilation.
  - Dependencies for each component are adjusted and updated. E.g.,
    no CLI dependencies for `lychee-lib`.
  - CLI tests are only moved to `lychee`, as it has nothing to do
    with the library component.
- `Status::Error` is refactored to contain dedicated error enum,
  `ErrorKind`.
  - The motivation is to delay the formatting of errors to strings.
    Note that `e.to_string()` is not necessarily cheap (though
    trivial in many cases). The formatting is no delayed until the
    error is needed to be displayed to users. So in some cases, if
    the error is never used, it means that it won't be formatted at
    all.
- Replaced `regex` based matching with one of the following:
  - Simple string equality test in the case of 'false positivie'.
  - URL parsing based test, in the case of extracting repository and
    user name for GitHub links.
  - Either cases would be much more efficient than `regex` based
    matching. First, there's no need to construct a state machine for
    regex. Second, URL is already verified and parsed on its creation,
    and extracting its components is fairly cheap. Also, this removes
    the dependency on `lazy-static` in `lychee-lib`.
- `types` module now has a sub-directory, and its components are now
  separated into their own modules (in that sub-directory).
- `lychee-lib::test_utils` module is only compiled for tests.
- `wiremock` is moved to `dev-dependency` as it's only needed for
  `test` modules.
- Dependencies are listed in alphabetical order.
- Imports are organized in the following fashion:
  - Imports from `std`
  - Imports from 3rd-party crates, and `lychee-lib`.
  - Imports from `crate::*` or `super::*`.
- No glob import.
- I followed suggestion from `cargo clippy`, with `clippy::all` and
  `clippy:pedantic`.
@lebensterben
Copy link
Member Author

@mre
I just merged them and there's no conflict now.

@mre
Copy link
Member

mre commented Apr 14, 2021

Hate to say it, but there is one more lint error. 😅
Also our old friend is back:

   Compiling lychee-lib v0.6.0 (/home/runner/work/lychee/lychee/target/package/lychee-lib-0.6.0)
error[E0277]: the trait bound `types::error::ErrorKind: From<reqwest::error::Error>` is not satisfied
   --> src/types/error.rs:127:45
    |
127 |             hubcaps::Error::Reqwest(e) => e.into(),
    |                                             ^^^^ the trait `From<reqwest::error::Error>` is not implemented for `types::error::ErrorKind`
    |
    = help: the following implementations were found:
              <types::error::ErrorKind as From<(PathBuf, std::io::Error)>>
              <types::error::ErrorKind as From<(std::string::String, url::ParseError)>>
              <types::error::ErrorKind as From<(std::string::String, url::ParseError, fast_chemail::ParseError)>>
              <types::error::ErrorKind as From<Infallible>>
            and 6 others
    = note: required because of the requirements on the impl of `Into<types::error::ErrorKind>` for `reqwest::error::Error`

I swear I didn't touch it this time. 🤞

Also, shouldn't

cargo install --path lychee-bin

just work? I'm getting

error[E0277]: the trait bound `url::Url: uri::_::_serde::Serialize` is not satisfied
  --> lychee-lib/src/uri.rs:15:5
   |
15 |     /// Website URL or mail address
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `uri::_::_serde::Serialize` is not implemented for `url::Url`
   |
   = note: required by `uri::_::_serde::ser::SerializeStruct::serialize_field`

error[E0277]: the trait bound `url::Url: Deserialize<'_>` is not satisfied
  --> lychee-lib/src/uri.rs:15:5
   |
15 |     /// Website URL or mail address
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Deserialize<'_>` is not implemented for `url::Url`
   |
   = note: required by `next_element`

error[E0277]: the trait bound `url::Url: Deserialize<'_>` is not satisfied
  --> lychee-lib/src/uri.rs:15:5
   |
15 |     /// Website URL or mail address
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Deserialize<'_>` is not implemented for `url::Url`
   |
   = note: required by `next_value`

error: aborting due to 3 previous errors

What's the right command to install it locally?

@lebensterben
Copy link
Member Author

@mre

Mystery solved:

   --> src/types/error.rs:127:62
    |
127 |             hubcaps::Error::Reqwest(e) => Self::ReqwestError(e),
    |                                                              ^ expected struct `reqwest::Error`, found struct `reqwest::error::Error`
    |
    = note: perhaps two different versions of crate `reqwest` are being used?

@lebensterben
Copy link
Member Author

This is some restriction of rust. These two are the same struct.
I cannot use reqwest::error::Error because reqwest::error is private.
But rustc cannot figure out they're the same.

I solved it by introduced a new enum variant.

@mre
Copy link
Member

mre commented Apr 14, 2021

Nicely done. Should cargo install --path lychee-bin work?

@lebensterben
Copy link
Member Author

Yes it works.

   Compiling reqwest v0.11.3
   Compiling hubcaps v0.6.2 (https://github.com/softprops/hubcaps.git#a9e6616e)
   Compiling check-if-email-exists v0.8.21
   Compiling lychee-lib v0.6.0 (/home/lucius/GitHub/lycheeverse/lychee-lib)
   Compiling lychee v0.6.0 (/home/lucius/GitHub/lycheeverse/lychee-bin)
    Finished release [optimized] target(s) in 1m 40s
  Installing /home/lucius/.cargo/bin/lychee
   Installed package `lychee v0.6.0 (/home/lucius/GitHub/lycheeverse/lychee-bin)` (executable `lychee`)

Once lychee-lib is published, lychee should pass the publish check.

@mre mre merged commit 228e5df into lycheeverse:master Apr 14, 2021
@mre
Copy link
Member

mre commented Apr 14, 2021

Yaaaay! You've done it. 🚀 Great success. Thanks a lot for pushing through and for your patience. 👏

@mre
Copy link
Member

mre commented Apr 14, 2021

I'll publish lychee-lib now.
P.S.: we forgot to adjust the Dockerfile, but it's not a big deal; will fix.

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