use zeroize::Zeroize;
#[derive(Clone, Default, PartialEq, Eq)]
pub struct SecureString(String);
impl SecureString {
#[must_use]
pub const fn new(value: String) -> Self {
Self(value)
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
#[must_use]
pub const fn len(&self) -> usize {
self.0.len()
}
#[must_use]
pub const fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn insert(&mut self, idx: usize, ch: char) {
self.0.insert(idx, ch);
}
pub fn remove(&mut self, idx: usize) -> char {
self.0.remove(idx)
}
pub fn push(&mut self, ch: char) {
self.0.push(ch);
}
pub fn clear(&mut self) {
self.0.clear();
}
}
impl std::ops::Deref for SecureString {
type Target = str;
fn deref(&self) -> &Self::Target {
self.as_str()
}
}
impl std::fmt::Debug for SecureString {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("SecureString([REDACTED])")
}
}
impl From<String> for SecureString {
fn from(value: String) -> Self {
Self::new(value)
}
}
impl From<&str> for SecureString {
fn from(value: &str) -> Self {
Self::new(value.to_string())
}
}
impl Drop for SecureString {
fn drop(&mut self) {
self.0.zeroize();
}
}
#[derive(Clone, Debug)]
pub struct NewsItem {
pub date: String,
pub title: String,
pub url: String,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AppMode {
Package,
News,
}
#[derive(
Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
)]
pub enum NewsFeedSource {
ArchNews,
SecurityAdvisory,
InstalledPackageUpdate,
AurPackageUpdate,
AurComment,
}
#[derive(
Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
)]
pub enum AdvisorySeverity {
Unknown,
Low,
Medium,
High,
Critical,
}
#[must_use]
pub const fn severity_rank(severity: Option<AdvisorySeverity>) -> u8 {
match severity {
Some(AdvisorySeverity::Critical) => 5,
Some(AdvisorySeverity::High) => 4,
Some(AdvisorySeverity::Medium) => 3,
Some(AdvisorySeverity::Low) => 2,
Some(AdvisorySeverity::Unknown) => 1,
None => 0,
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum NewsSortMode {
DateDesc,
DateAsc,
Title,
SourceThenTitle,
SeverityThenDate,
UnreadThenDate,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum NewsReadFilter {
All,
Read,
Unread,
}
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct NewsFeedItem {
pub id: String,
pub date: String,
pub title: String,
pub summary: Option<String>,
pub url: Option<String>,
pub source: NewsFeedSource,
pub severity: Option<AdvisorySeverity>,
pub packages: Vec<String>,
}
#[derive(Clone, Debug)]
pub struct NewsFeedPayload {
pub items: Vec<NewsFeedItem>,
pub seen_pkg_versions: std::collections::HashMap<String, String>,
pub seen_aur_comments: std::collections::HashMap<String, String>,
}
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct NewsBookmark {
pub item: NewsFeedItem,
pub content: Option<String>,
pub html_path: Option<String>,
}
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub enum Source {
Official {
repo: String,
arch: String,
},
Aur,
}
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct PackageItem {
pub name: String,
pub version: String,
pub description: String,
pub source: Source,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub popularity: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub out_of_date: Option<u64>,
#[serde(default, skip_serializing_if = "is_false")]
pub orphaned: bool,
}
#[derive(Clone, Debug, Default, serde::Serialize, serde::Deserialize)]
pub struct PackageDetails {
pub repository: String,
pub name: String,
pub version: String,
pub description: String,
pub architecture: String,
pub url: String,
pub licenses: Vec<String>,
pub groups: Vec<String>,
pub provides: Vec<String>,
pub depends: Vec<String>,
pub opt_depends: Vec<String>,
pub required_by: Vec<String>,
pub optional_for: Vec<String>,
pub conflicts: Vec<String>,
pub replaces: Vec<String>,
pub download_size: Option<u64>,
pub install_size: Option<u64>,
pub owner: String, pub build_date: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub popularity: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub out_of_date: Option<u64>,
#[serde(default, skip_serializing_if = "is_false")]
pub orphaned: bool,
}
#[derive(Clone, Debug)]
pub struct QueryInput {
pub id: u64,
pub text: String,
pub fuzzy: bool,
}
#[derive(Clone, Debug)]
pub struct SearchResults {
pub id: u64,
pub items: Vec<PackageItem>,
}
#[derive(Clone, Debug)]
pub struct PkgbuildCheckRequest {
pub package_name: String,
pub pkgbuild_text: String,
pub dry_run: bool,
}
#[derive(Clone, Debug)]
pub struct PkgbuildCheckResponse {
pub package_name: String,
pub findings: Vec<crate::state::app_state::PkgbuildCheckFinding>,
pub raw_results: Vec<crate::state::app_state::PkgbuildToolRawResult>,
pub missing_tools: Vec<String>,
pub last_error: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SortMode {
RepoThenName,
AurPopularityThenOfficial,
BestMatches,
}
impl SortMode {
#[must_use]
pub const fn as_config_key(&self) -> &'static str {
match self {
Self::RepoThenName => "alphabetical",
Self::AurPopularityThenOfficial => "aur_popularity",
Self::BestMatches => "best_matches",
}
}
#[must_use]
pub fn from_config_key(s: &str) -> Option<Self> {
match s.trim().to_lowercase().as_str() {
"alphabetical" | "repo_then_name" | "pacman" => Some(Self::RepoThenName),
"aur_popularity" | "popularity" => Some(Self::AurPopularityThenOfficial),
"best_matches" | "relevance" => Some(Self::BestMatches),
_ => None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum InstalledPackagesMode {
#[default]
LeafOnly,
AllExplicit,
}
impl InstalledPackagesMode {
#[must_use]
pub const fn as_config_key(&self) -> &'static str {
match self {
Self::LeafOnly => "leaf",
Self::AllExplicit => "all",
}
}
#[must_use]
pub fn from_config_key(s: &str) -> Option<Self> {
match s.trim().to_lowercase().as_str() {
"leaf" | "leaf_only" => Some(Self::LeafOnly),
"all" | "all_explicit" => Some(Self::AllExplicit),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use super::{InstalledPackagesMode, SortMode};
#[test]
fn state_sortmode_config_roundtrip_and_aliases() {
assert_eq!(SortMode::RepoThenName.as_config_key(), "alphabetical");
assert_eq!(
SortMode::from_config_key("alphabetical"),
Some(SortMode::RepoThenName)
);
assert_eq!(
SortMode::from_config_key("repo_then_name"),
Some(SortMode::RepoThenName)
);
assert_eq!(
SortMode::from_config_key("pacman"),
Some(SortMode::RepoThenName)
);
assert_eq!(
SortMode::from_config_key("aur_popularity"),
Some(SortMode::AurPopularityThenOfficial)
);
assert_eq!(
SortMode::from_config_key("popularity"),
Some(SortMode::AurPopularityThenOfficial)
);
assert_eq!(
SortMode::from_config_key("best_matches"),
Some(SortMode::BestMatches)
);
assert_eq!(
SortMode::from_config_key("relevance"),
Some(SortMode::BestMatches)
);
assert_eq!(SortMode::from_config_key("unknown"), None);
}
#[test]
fn state_installedpackagesmode_config_roundtrip_and_aliases() {
assert_eq!(InstalledPackagesMode::LeafOnly.as_config_key(), "leaf");
assert_eq!(InstalledPackagesMode::AllExplicit.as_config_key(), "all");
assert_eq!(
InstalledPackagesMode::from_config_key("leaf"),
Some(InstalledPackagesMode::LeafOnly)
);
assert_eq!(
InstalledPackagesMode::from_config_key("all"),
Some(InstalledPackagesMode::AllExplicit)
);
assert_eq!(
InstalledPackagesMode::from_config_key("leaf_only"),
Some(InstalledPackagesMode::LeafOnly)
);
assert_eq!(
InstalledPackagesMode::from_config_key("all_explicit"),
Some(InstalledPackagesMode::AllExplicit)
);
assert_eq!(
InstalledPackagesMode::from_config_key(InstalledPackagesMode::LeafOnly.as_config_key()),
Some(InstalledPackagesMode::LeafOnly)
);
assert_eq!(
InstalledPackagesMode::from_config_key(
InstalledPackagesMode::AllExplicit.as_config_key()
),
Some(InstalledPackagesMode::AllExplicit)
);
assert_eq!(
InstalledPackagesMode::from_config_key("LEAF"),
Some(InstalledPackagesMode::LeafOnly)
);
assert_eq!(
InstalledPackagesMode::from_config_key("Leaf"),
Some(InstalledPackagesMode::LeafOnly)
);
assert_eq!(
InstalledPackagesMode::from_config_key("LEAF_ONLY"),
Some(InstalledPackagesMode::LeafOnly)
);
assert_eq!(
InstalledPackagesMode::from_config_key("All"),
Some(InstalledPackagesMode::AllExplicit)
);
assert_eq!(
InstalledPackagesMode::from_config_key("ALL_EXPLICIT"),
Some(InstalledPackagesMode::AllExplicit)
);
assert_eq!(
InstalledPackagesMode::from_config_key(" leaf "),
Some(InstalledPackagesMode::LeafOnly)
);
assert_eq!(
InstalledPackagesMode::from_config_key(" all "),
Some(InstalledPackagesMode::AllExplicit)
);
assert_eq!(
InstalledPackagesMode::from_config_key(" leaf_only "),
Some(InstalledPackagesMode::LeafOnly)
);
assert_eq!(
InstalledPackagesMode::from_config_key(" all_explicit "),
Some(InstalledPackagesMode::AllExplicit)
);
assert_eq!(InstalledPackagesMode::from_config_key("unknown"), None);
assert_eq!(InstalledPackagesMode::from_config_key(""), None);
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ArchStatusColor {
None,
Operational,
IncidentToday,
IncidentSevereToday,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Focus {
Search,
Recent,
Install,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RightPaneFocus {
Install,
Downgrade,
Remove,
}
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct OptionalDepRow {
pub label: String,
pub package: String,
#[serde(default)]
pub installed: bool,
#[serde(default)]
pub selectable: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub note: Option<String>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RepositoryPacmanStatus {
Absent,
Active,
Commented,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RepositoryKeyTrust {
NotApplicable,
Trusted,
NotTrusted,
Unknown,
}
#[derive(Clone, Debug)]
pub struct RepositoryModalRow {
pub pacman_section_name: String,
pub results_filter_display: String,
pub pacman_status: RepositoryPacmanStatus,
pub source_hint: Option<String>,
pub key_trust: RepositoryKeyTrust,
}
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
pub struct AurComment {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
pub author: String,
pub date: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub date_timestamp: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub date_url: Option<String>,
pub content: String,
#[serde(default)]
pub pinned: bool,
}
#[allow(clippy::trivially_copy_pass_by_ref)]
const fn is_false(b: &bool) -> bool {
!(*b)
}