mirror of
https://github.com/deps-rs/deps.rs.git
synced 2024-11-21 18:06:30 +00:00
Allow analyzing crates in sub-directories of repo root (#170)
* Allow analyzing crates in sub-directories (#95) * Add field to the main page form for selecting an inner path * chore: make clippy happy * Display sub-directory tree in status overview * Append the query parameter to the SVG links * Clippy fixes * Update assets/links.js Co-authored-by: Eduardo Pinho <enet4mikeenet@gmail.com> Co-authored-by: Eduardo Pinho <enet4mikeenet@gmail.com>
This commit is contained in:
parent
477d83a4e0
commit
f9d545f9ff
6 changed files with 96 additions and 28 deletions
14
assets/links.js
vendored
14
assets/links.js
vendored
|
@ -4,6 +4,12 @@ function buildRepoLink() {
|
|||
let hoster = formRef.elements["hosterSelect"].value.toLowerCase();
|
||||
let owner = formRef.elements["owner"].value;
|
||||
let repoName = formRef.elements["repoName"].value;
|
||||
let innerPath = formRef.elements["innerPath"].value;
|
||||
|
||||
let qparams = "";
|
||||
if (innerPath.length > 0) {
|
||||
qparams = "?path=" + encodeURIComponent(innerPath);
|
||||
}
|
||||
|
||||
if (hoster === "gitea") {
|
||||
let baseUrl = formRef.elements["baseUrl"].value;
|
||||
|
@ -18,9 +24,9 @@ function buildRepoLink() {
|
|||
return;
|
||||
}
|
||||
|
||||
window.location.href = `/repo/${hoster}/${baseUrl}/${owner}/${repoName}`;
|
||||
window.location.assign(`/repo/${hoster}/${baseUrl}/${owner}/${repoName}${qparams}`);
|
||||
} else {
|
||||
window.location.href = `/repo/${hoster}/${owner}/${repoName}`;
|
||||
window.location.assign(`/repo/${hoster}/${owner}/${repoName}${qparams}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,8 +38,8 @@ function buildCrateLink() {
|
|||
|
||||
if (crateVer.length == 0) {
|
||||
// default to latest version
|
||||
window.location.href = `/crate/${crate}`;
|
||||
window.location.assign(`/crate/${crate}`);
|
||||
} else {
|
||||
window.location.href = `/crate/${crate}/${crateVer}`;
|
||||
window.location.assign(`/crate/${crate}/${crateVer}`);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -170,20 +170,25 @@ impl Engine {
|
|||
pub async fn analyze_repo_dependencies(
|
||||
&self,
|
||||
repo_path: RepoPath,
|
||||
sub_path: &Option<String>,
|
||||
) -> Result<AnalyzeDependenciesOutcome, Error> {
|
||||
let start = Instant::now();
|
||||
|
||||
let entry_point = RelativePath::new("/").to_relative_path_buf();
|
||||
let mut entry_point = RelativePath::new("/").to_relative_path_buf();
|
||||
|
||||
if let Some(inner_path) = sub_path {
|
||||
entry_point.push(inner_path);
|
||||
}
|
||||
|
||||
let engine = self.clone();
|
||||
|
||||
let manifest_output = crawl_manifest(self.clone(), repo_path.clone(), entry_point).await?;
|
||||
|
||||
let engine_for_analyze = engine.clone();
|
||||
let futures = manifest_output
|
||||
.crates
|
||||
.into_iter()
|
||||
.map(|(crate_name, deps)| async {
|
||||
let analyzed_deps = analyze_dependencies(engine_for_analyze.clone(), deps).await?;
|
||||
let analyzed_deps = analyze_dependencies(engine.clone(), deps).await?;
|
||||
Ok::<_, Error>((crate_name, analyzed_deps))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
|
|
@ -186,7 +186,7 @@ impl App {
|
|||
let qual = params.find("qual").expect("route param 'qual' not found");
|
||||
let name = params.find("name").expect("route param 'name' not found");
|
||||
|
||||
let badge_knobs = BadgeKnobs::from_query_string(req.uri().query());
|
||||
let extra_knobs = ExtraConfig::from_query_string(req.uri().query());
|
||||
|
||||
let repo_path_result = RepoPath::from_parts(site, qual, name);
|
||||
|
||||
|
@ -204,7 +204,7 @@ impl App {
|
|||
Ok(repo_path) => {
|
||||
let analyze_result = server
|
||||
.engine
|
||||
.analyze_repo_dependencies(repo_path.clone())
|
||||
.analyze_repo_dependencies(repo_path.clone(), &extra_knobs.path)
|
||||
.await;
|
||||
|
||||
match analyze_result {
|
||||
|
@ -214,7 +214,7 @@ impl App {
|
|||
None,
|
||||
format,
|
||||
SubjectPath::Repo(repo_path),
|
||||
badge_knobs,
|
||||
extra_knobs,
|
||||
);
|
||||
Ok(response)
|
||||
}
|
||||
|
@ -223,7 +223,7 @@ impl App {
|
|||
Some(analysis_outcome),
|
||||
format,
|
||||
SubjectPath::Repo(repo_path),
|
||||
badge_knobs,
|
||||
extra_knobs,
|
||||
);
|
||||
Ok(response)
|
||||
}
|
||||
|
@ -345,7 +345,7 @@ impl App {
|
|||
};
|
||||
|
||||
let crate_path_result = CratePath::from_parts(name, &version);
|
||||
let badge_knobs = BadgeKnobs::from_query_string(req.uri().query());
|
||||
let badge_knobs = ExtraConfig::from_query_string(req.uri().query());
|
||||
|
||||
match crate_path_result {
|
||||
Err(err) => {
|
||||
|
@ -393,11 +393,13 @@ impl App {
|
|||
analysis_outcome: Option<AnalyzeDependenciesOutcome>,
|
||||
format: StatusFormat,
|
||||
subject_path: SubjectPath,
|
||||
badge_knobs: BadgeKnobs,
|
||||
badge_knobs: ExtraConfig,
|
||||
) -> Response<Body> {
|
||||
match format {
|
||||
StatusFormat::Svg => views::badge::response(analysis_outcome.as_ref(), badge_knobs),
|
||||
StatusFormat::Html => views::html::status::render(analysis_outcome, subject_path),
|
||||
StatusFormat::Html => {
|
||||
views::html::status::render(analysis_outcome, subject_path, badge_knobs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -430,27 +432,34 @@ fn not_found() -> Response<Body> {
|
|||
static SELF_BASE_URL: Lazy<String> =
|
||||
Lazy::new(|| env::var("BASE_URL").unwrap_or_else(|_| "http://localhost:8080".to_string()));
|
||||
|
||||
/// Configuration options supplied through Get Parameters
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct BadgeKnobs {
|
||||
pub struct ExtraConfig {
|
||||
/// Badge style to show
|
||||
style: BadgeStyle,
|
||||
/// Whether the inscription _"dependencies"_ should be abbreviated as _"deps"_ in the badge.
|
||||
compact: bool,
|
||||
/// Path in which the crate resides within the repository
|
||||
path: Option<String>,
|
||||
}
|
||||
|
||||
impl BadgeKnobs {
|
||||
impl ExtraConfig {
|
||||
fn from_query_string(qs: Option<&str>) -> Self {
|
||||
#[derive(Debug, Clone, Default, Deserialize)]
|
||||
struct BadgeKnobsPartial {
|
||||
struct ExtraConfigPartial {
|
||||
style: Option<BadgeStyle>,
|
||||
compact: Option<bool>,
|
||||
path: Option<String>,
|
||||
}
|
||||
|
||||
let badge_knobs = qs
|
||||
.and_then(|qs| serde_urlencoded::from_str::<BadgeKnobsPartial>(qs).ok())
|
||||
let extra_config = qs
|
||||
.and_then(|qs| serde_urlencoded::from_str::<ExtraConfigPartial>(qs).ok())
|
||||
.unwrap_or_default();
|
||||
|
||||
Self {
|
||||
style: badge_knobs.style.unwrap_or_default(),
|
||||
compact: badge_knobs.compact.unwrap_or_default(),
|
||||
style: extra_config.style.unwrap_or_default(),
|
||||
compact: extra_config.compact.unwrap_or_default(),
|
||||
path: extra_config.path,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,11 +3,11 @@ use hyper::header::CONTENT_TYPE;
|
|||
use hyper::{Body, Response};
|
||||
|
||||
use crate::engine::AnalyzeDependenciesOutcome;
|
||||
use crate::server::BadgeKnobs;
|
||||
use crate::server::ExtraConfig;
|
||||
|
||||
pub fn badge(
|
||||
analysis_outcome: Option<&AnalyzeDependenciesOutcome>,
|
||||
badge_knobs: BadgeKnobs,
|
||||
badge_knobs: ExtraConfig,
|
||||
) -> Badge {
|
||||
let subject = if badge_knobs.compact {
|
||||
"deps"
|
||||
|
@ -74,7 +74,7 @@ pub fn badge(
|
|||
|
||||
pub fn response(
|
||||
analysis_outcome: Option<&AnalyzeDependenciesOutcome>,
|
||||
badge_knobs: BadgeKnobs,
|
||||
badge_knobs: ExtraConfig,
|
||||
) -> Response<Body> {
|
||||
let badge = badge(analysis_outcome, badge_knobs).to_svg();
|
||||
|
||||
|
|
|
@ -57,6 +57,16 @@ fn link_forms() -> Markup {
|
|||
p class="help" id="baseUrlHelp" { "Base URL of the Git instance the project is hosted on. Only relevant for Gitea Instances." }
|
||||
}
|
||||
|
||||
div class="field" {
|
||||
label class="label" { "Path in Repository" }
|
||||
|
||||
div class="control" {
|
||||
input class="input" type="text" id="innerPath" placeholder="project1/rust-stuff";
|
||||
}
|
||||
|
||||
p class="help" id="baseUrlHelp" { "Path within the repository where the " code { "Cargo.toml" } " file is located." }
|
||||
}
|
||||
|
||||
input type="submit" class="button is-primary" value="Check" onclick="buildRepoLink();";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ use crate::models::crates::{AnalyzedDependencies, AnalyzedDependency, CrateName}
|
|||
use crate::models::repo::RepoSite;
|
||||
use crate::models::SubjectPath;
|
||||
use crate::server::views::badge;
|
||||
use crate::server::BadgeKnobs;
|
||||
use crate::server::ExtraConfig;
|
||||
|
||||
fn get_crates_url(name: impl AsRef<str>) -> String {
|
||||
format!("https://crates.io/crates/{}", name.as_ref())
|
||||
|
@ -161,6 +161,23 @@ fn render_title(subject_path: &SubjectPath) -> Markup {
|
|||
}
|
||||
}
|
||||
|
||||
/// Renders a path within a repository as HTML.
|
||||
///
|
||||
/// Panics, when the string is empty.
|
||||
fn render_path(inner_path: &str) -> Markup {
|
||||
let path_icon = PreEscaped(fa(FaType::Regular, "folder-open").unwrap());
|
||||
|
||||
let mut splitted = inner_path.trim_matches('/').split('/');
|
||||
let init = splitted.next().unwrap().to_string();
|
||||
let path_spaced = splitted.fold(init, |b, val| b + " / " + val);
|
||||
|
||||
html! {
|
||||
{ (path_icon) }
|
||||
" / "
|
||||
(path_spaced)
|
||||
}
|
||||
}
|
||||
|
||||
fn dependencies_pluralized(count: usize) -> &'static str {
|
||||
if count == 1 {
|
||||
"dependency"
|
||||
|
@ -332,6 +349,7 @@ fn render_failure(subject_path: SubjectPath) -> Markup {
|
|||
fn render_success(
|
||||
analysis_outcome: AnalyzeDependenciesOutcome,
|
||||
subject_path: SubjectPath,
|
||||
extra_config: ExtraConfig,
|
||||
) -> Markup {
|
||||
let self_path = match subject_path {
|
||||
SubjectPath::Repo(ref repo_path) => format!(
|
||||
|
@ -347,7 +365,7 @@ fn render_success(
|
|||
let status_base_url = format!("{}/{}", &super::SELF_BASE_URL as &str, self_path);
|
||||
|
||||
let status_data_uri =
|
||||
badge::badge(Some(&analysis_outcome), BadgeKnobs::default()).to_svg_data_uri();
|
||||
badge::badge(Some(&analysis_outcome), extra_config.clone()).to_svg_data_uri();
|
||||
|
||||
let hero_class = if analysis_outcome.any_always_insecure() {
|
||||
"is-danger"
|
||||
|
@ -357,6 +375,15 @@ fn render_success(
|
|||
"is-success"
|
||||
};
|
||||
|
||||
// NOTE(feliix42): While we could encode the whole `ExtraConfig` struct here, I've decided
|
||||
// against doing so as this would always append the defaults for badge style and compactness
|
||||
// settings to the URL, bloating it unnecessarily, we can do that once it's needed.
|
||||
let options = serde_urlencoded::to_string([(
|
||||
"path",
|
||||
extra_config.path.clone().unwrap_or_default().as_str(),
|
||||
)])
|
||||
.unwrap();
|
||||
|
||||
html! {
|
||||
section class=(format!("hero {}", hero_class)) {
|
||||
div class="hero-head" { (super::render_navbar()) }
|
||||
|
@ -366,13 +393,23 @@ fn render_success(
|
|||
(render_title(&subject_path))
|
||||
}
|
||||
|
||||
@if let Some(ref path) = extra_config.path {
|
||||
p class="subtitle" {
|
||||
(render_path(path))
|
||||
}
|
||||
}
|
||||
|
||||
img src=(status_data_uri);
|
||||
}
|
||||
}
|
||||
div class="hero-footer" {
|
||||
div class="container" {
|
||||
pre class="is-size-7" {
|
||||
(format!("[![dependency status]({}/status.svg)]({})", status_base_url, status_base_url))
|
||||
@if extra_config.path.is_some() {
|
||||
(format!("[![dependency status]({}/status.svg?{opt})]({}?{opt})", status_base_url, status_base_url, opt = options))
|
||||
} @else {
|
||||
(format!("[![dependency status]({}/status.svg)]({})", status_base_url, status_base_url))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -416,6 +453,7 @@ fn render_success(
|
|||
pub fn render(
|
||||
analysis_outcome: Option<AnalyzeDependenciesOutcome>,
|
||||
subject_path: SubjectPath,
|
||||
extra_config: ExtraConfig,
|
||||
) -> Response<Body> {
|
||||
let title = match subject_path {
|
||||
SubjectPath::Repo(ref repo_path) => {
|
||||
|
@ -427,7 +465,7 @@ pub fn render(
|
|||
};
|
||||
|
||||
if let Some(outcome) = analysis_outcome {
|
||||
super::render_html(&title, render_success(outcome, subject_path))
|
||||
super::render_html(&title, render_success(outcome, subject_path, extra_config))
|
||||
} else {
|
||||
super::render_html(&title, render_failure(subject_path))
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue