pacco/src/routes/ui/pkg.rs
JMARyA 5ae9b80302
Some checks failed
ci/woodpecker/push/build Pipeline failed
refactor
2025-01-18 22:14:02 +01:00

567 lines
26 KiB
Rust

use based::request::{RequestContext, StringResponse};
use based::ui::primitives::flex::Strategy;
use based::ui::primitives::space::SpaceBetweenWidget;
use based::ui::primitives::text::{Code, TextWidget};
use based::ui::{UIWidget, prelude::*};
use maud::{PreEscaped, Render, html};
use rocket::get;
use pacco::pkg::{Package, Repository, arch::Architecture, find_package_by_name};
use crate::routes::render;
use super::take_out;
#[get("/<repo>/<pkg_name>?<ver>")]
pub async fn pkg_ui(
repo: &str,
pkg_name: &str,
ctx: RequestContext,
ver: Option<&str>,
) -> Option<StringResponse> {
let repo = Repository::new(repo).unwrap();
let mut pkg = repo.get_pkg_by_name(pkg_name)?;
if let Some(ver) = ver {
let (version, rel) = Package::version(ver);
pkg = pkg.get_version(&version);
pkg.rel = rel;
}
let versions = pkg.versions();
let arch = pkg.arch();
let install_script = pkg.install_script();
let systemd_units = pkg.systemd_units();
let pacman_hooks = pkg.pacman_hooks();
let binaries = pkg.binaries();
let man_entries = pkg.man_entries();
let etc_entries = pkg.etc_entries();
let kernel_modules = pkg.kernel_modules();
let firmware = pkg.firmware();
let keyrings = pkg.keyrings();
let shared_objects = pkg.shared_objects();
let icons = pkg.icons();
let readmes = pkg.readmes();
let mut pkginfo = pkg.pkginfo();
let pkg_info_header = Flex(
// Package Name
Div().vanish()
.push(
Link(&format!("/{}", repo.name),
Text(&repo.name).bold().color(&Gray::_100)._3xl()
).use_htmx()
)
.push(Padding(Text("/").bold().color(&Gray::_400)).all(ScreenValue::_2))
.push(
Padding(Text(&pkg.name)._3xl().bold().color(&Gray::_100)).right(ScreenValue::_2)
)
.push_if(pkg.is_signed(), || {
Flex(
Padding(
Div().vanish()
.push(Span("")._2xl().bold().color(&Slate::_300))
.push(Span("Signed").sm().medium().color(&Slate::_300))
// TODO : Add more info: Who signed? + Public Key recv
).right(ScreenValue::_4)
).items_center().gap(ScreenValue::_2)
})
.push_for_each(&arch, |arch: &Architecture| {
Rounded(
Padding(
Background(Gray::_700,
Span(&arch.to_string()).medium().sm())
).x(ScreenValue::_3).y(ScreenValue::_1)
).size(Size::Full)
})
.push(Margin(
Flex(
Padding(
Hover(Background(Gray::_300, Nothing())).on(Background(Gray::_200,
Rounded(Shadow::medium(
Link(&format!("/pkg/{}/{}/{}", pkg.repo, pkg.arch.to_string(), pkg.file_name()),
Paragraph(
html! {
svg class="w-5 h-5" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" viewBox="0 0 24 24" stroke-width="2" {
path stroke-linecap="round" stroke-linejoin="round" d="M12 5v14m7-7l-7 7-7-7" {};
};
p class="ml-2 text-black" { "Download" };
}
).black().xs()))).size(Size::Large)
))
).x(ScreenValue::_2).y(ScreenValue::_2)
).items_center()
).left(ScreenValue::_4)
)
).wrap(Wrap::Wrap).gap(ScreenValue::_2).full_center().group();
let pkg_info_table = Padding(
Flex(
Div()
.vanish()
.push(Context(FlexGrow(
Strategy::Grow,
Animated(Background(
Gray::_800,
Shadow::large(
Rounded(
Margin(
Padding(
SpaceBetween(
Div()
.push(Context(
Text("Info")
.xl()
.semibold()
.color(&Gray::_300)
.underlined(),
))
.push_some(
take_out(&mut pkginfo, |x| x.0 == "pkgdesc").1,
|x: (String, String)| build_info(x.0, x.1),
)
.push_some(
take_out(&mut pkginfo, |x| x.0 == "packager").1,
|x: (String, String)| build_info(x.0, x.1),
)
.push_some(
take_out(&mut pkginfo, |x| x.0 == "url").1,
|x: (String, String)| build_info(x.0, x.1),
)
.push_some(
take_out(&mut pkginfo, |x| x.0 == "size").1,
|x: (String, String)| build_info(x.0, x.1),
)
.push_for_each(&pkginfo, |(key, val)| {
build_info(key.clone(), val.clone())
}),
)
.y(ScreenValue::_2),
)
.all(ScreenValue::_4),
)
.all(ScreenValue::_2),
)
.size(Size::Large),
),
)),
)))
.push(
SpaceBetween(
Padding(Background(
Gray::_800,
Shadow::large(
Rounded(
Margin(
Screen::medium(FlexGrow(Strategy::NoGrow, Nothing())).on(
FlexGrow(
Strategy::Grow,
Div()
.vanish()
.push(
Text("Versions")
.xl()
.semibold()
.color(&Gray::_300),
)
.push(
SpaceBetween(Div().vanish().push_for_each(
&versions,
|version: &String| {
Animated(
Link(
&format!(
"/{}/{}?ver={version}",
repo.name, &pkg.name
),
if pkg
.version
.as_ref()
.map(|x| {
*x == Package::version(
&version,
)
.0
})
.unwrap_or_default()
{
Text(&version)
.color(&Blue::_500)
.render()
} else {
Hover(
Text("")
.color(&Blue::_400),
)
.on(Text(&version)
.color(&Gray::_100))
.render()
},
)
.use_htmx(),
)
},
))
.y(ScreenValue::_1),
),
),
),
)
.all(ScreenValue::_2),
)
.size(Size::Large),
),
))
.all(ScreenValue::_4),
)
.y(ScreenValue::_2),
),
)
.wrap(Wrap::Wrap)
.group(),
)
.top(ScreenValue::_6);
let pkg_content = Padding(
Flex(
SpaceBetween(
Div()
.vanish()
.push(Text("Content").xl().bold().color(&Gray::_300))
.push(
Flex(
Div()
.vanish()
.push_if(!systemd_units.is_empty(), || {
InfoCard(
Div()
.vanish()
.push(CardTitle("Systemd Units"))
.push(ListElements(&systemd_units)),
)
})
.push_if(!pacman_hooks.is_empty(), || {
InfoCard(
Div().vanish().push(CardTitle("Pacman Hooks")).push(
Paragraph(
SpaceBetween(Div().vanish().push_for_each(
&pacman_hooks,
|hook: &(String, String)| {
Div()
.vanish()
.push(
Text(&hook.0)
.xl()
.semibold()
.color(&Gray::_300),
)
.push(
Padding(
Rounded(Background(
Gray::_700,
Code(&hook.1)
.sm()
.color(&Gray::_100),
))
.size(Size::Large),
)
.all(ScreenValue::_4),
)
},
))
.y(ScreenValue::_1),
)
.list_style(ListStyle::Disc)
.color(&Gray::_300),
),
)
})
.push_if(!readmes.is_empty(), || {
InfoCard(
Div().vanish().push(CardTitle("READMEs")).push(
Paragraph(
SpaceBetween(Div().vanish().push_for_each(
&readmes,
|readme: &(String, String)| {
Div()
.vanish()
.push(
Text(&readme.0)
.xl()
.semibold()
.color(&Gray::_300),
)
.push(
Padding(
Rounded(Background(
Gray::_700,
Code(&readme.1)
.sm()
.color(&Gray::_100),
))
.size(Size::Large),
)
.all(ScreenValue::_4),
)
},
))
.y(ScreenValue::_1),
)
.list_style(ListStyle::Disc)
.color(&Gray::_300),
),
)
})
.push_if(!binaries.is_empty(), || {
InfoCard(
Div()
.vanish()
.push(CardTitle("Binaries"))
.push(ListElements(&binaries)),
)
})
.push_if(!kernel_modules.is_empty(), || {
InfoCard(
Div()
.vanish()
.push(CardTitle("Kernel Modules"))
.push(ListElements(&kernel_modules)),
)
})
.push_if(!shared_objects.is_empty(), || {
InfoCard(
Div()
.vanish()
.push(CardTitle("Shared Objects (Libraries)"))
.push(ListElements(&shared_objects)),
)
})
.push_if(!firmware.is_empty(), || {
InfoCard(
Div()
.vanish()
.push(CardTitle("Firmware"))
.push(ListElements(&firmware)),
)
})
.push_if(!keyrings.is_empty(), || {
InfoCard(
Div()
.vanish()
.push(CardTitle("Pacman Keyrings"))
.push(ListElements(&keyrings)),
)
})
.push_if(!icons.is_empty(), || {
InfoCard(
Div()
.vanish()
.push(CardTitle("Icons"))
.push(ListElements(&icons)),
)
})
.push_if(!man_entries.is_empty(), || {
InfoCard(
Div()
.vanish()
.push(CardTitle("Man Entries"))
.push(ListElements(&man_entries)),
)
})
.push_if(!etc_entries.is_empty(), || {
InfoCard(
Div()
.vanish()
.push(CardTitle("Config Files"))
.push(ListElements(&etc_entries)),
)
})
.push(InfoCard(
Div()
.vanish()
.push(CardTitle("Package Files"))
.push(ListElements(&pkg.file_list())),
)),
)
.group()
.wrap(Wrap::Wrap),
),
)
.y(ScreenValue::_2),
)
.wrap(Wrap::Wrap)
.group(),
)
.top(ScreenValue::_6);
let content = Div()
.vanish()
.push(pkg_info_header)
.push(pkg_info_table)
.push(pkg_content)
.push_some(install_script.as_ref(), |install_script: &String| {
SpaceBetween(
Margin(
Div()
.push(
Text("Install Script")
._3xl()
.semibold()
.color(&Gray::_300)
.indentation(ScreenValue::_2),
)
.push(
Rounded(
Padding(Background(
Gray::_700,
Code(install_script.trim())
.color(&Gray::_100)
.sm()
.indentation(ScreenValue::_0),
))
.all(ScreenValue::_2),
)
.size(Size::Large),
),
)
.top(ScreenValue::_6),
)
.y(ScreenValue::_4)
})
.render();
Some(render(content, pkg_name, ctx).await)
}
pub fn build_info(key: String, value: String) -> PreEscaped<String> {
match key.as_str() {
"pkgname" => {}
"xdata" => {}
"arch" => {}
"pkbase" => {}
"pkgver" => {}
"pkgdesc" => {
return key_value("Description".to_string(), value);
}
"packager" => {
return key_value("Packager".to_string(), value);
}
"url" => {
return Flex(
Div()
.vanish()
.push(Width(ScreenValue::_32, Span("Website: ").bold()))
.push(
Margin(Link(&value, Span(&value).color(&Blue::_400))).left(ScreenValue::_6),
),
)
.group()
.render();
}
"builddate" => {
let date = chrono::DateTime::from_timestamp(value.parse().unwrap(), 0).unwrap();
return key_value(
"Build at".to_string(),
date.format("%d.%m.%Y %H:%M").to_string(),
);
}
"depend" => {
return pkg_list_info("Depends on", &value);
}
"makedepend" => {
return pkg_list_info("Build Dependencies", &value);
}
"license" => {
return key_value("License".to_string(), value);
}
"size" => {
return key_value(
"Size".to_string(),
bytesize::to_string(value.parse().unwrap(), false),
);
}
_ => {
log::warn!("Unhandled PKGINFO {key} = {value}");
}
}
html! {}
}
pub fn key_value(key: String, value: String) -> PreEscaped<String> {
Flex(
Div()
.vanish()
.push(Width(ScreenValue::_32, Span(&format!("{key}: ")).bold()))
.push(Margin(Span(&value)).left(ScreenValue::_6)),
)
.items_center()
.group()
.render()
}
#[allow(non_snake_case)]
pub fn ListElements(el: &[String]) -> TextWidget {
Paragraph(
SpaceBetween(html! {
@for unit in el {
li { (unit) }
}
})
.y(ScreenValue::_1),
)
.list_style(ListStyle::Disc)
.color(&Gray::_300)
}
#[allow(non_snake_case)]
pub fn CardTitle(title: &str) -> PreEscaped<String> {
Context(Text(title).large().medium().color(&Gray::_100).underlined())
}
#[allow(non_snake_case)]
pub fn InfoCard<T: UIWidget + 'static>(inner: T) -> SpaceBetweenWidget {
SpaceBetween(
Padding(Background(
Gray::_800,
Shadow::large(
Rounded(Margin(FlexGrow(Strategy::Grow, inner)).all(ScreenValue::_2))
.size(Size::Large),
),
))
.all(ScreenValue::_4),
)
.y(ScreenValue::_2)
}
pub fn pkg_list_info(key: &str, value: &str) -> PreEscaped<String> {
let pkgs = value.split_whitespace().map(|pkg| {
if let Some(pkg_url) = find_pkg_url(pkg) {
Margin(Link(&pkg_url, Text(&pkg).color(&Blue::_400)).use_htmx()).left(ScreenValue::_6)
} else {
Margin(Span(&pkg)).left(ScreenValue::_6)
}
});
html! {
(Flex(
Div().vanish()
.push(Width(ScreenValue::_32, Span(&format!("{key}: ")).bold()))
.push(Flex(
html! {
@for pkg in pkgs {
(pkg)
}
}
).group().wrap(Wrap::Wrap))
).items_center().group())
}
}
pub fn find_pkg_url(pkg: &str) -> Option<String> {
if let Some(pkg) = find_package_by_name(pkg) {
return Some(format!("/{}/{}", pkg.repo, pkg.name));
}
None
}