Fixed the name so it's not a collision
This commit is contained in:
parent
be23d887cb
commit
cec1d5409c
|
@ -130,6 +130,19 @@ version = "3.19.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
|
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "carto"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
|
"clap",
|
||||||
|
"inotify",
|
||||||
|
"memmap2",
|
||||||
|
"regex",
|
||||||
|
"serde",
|
||||||
|
"walkdir",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.30"
|
version = "1.2.30"
|
||||||
|
@ -614,19 +627,6 @@ dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "where-cmd"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"chrono",
|
|
||||||
"clap",
|
|
||||||
"inotify",
|
|
||||||
"memmap2",
|
|
||||||
"regex",
|
|
||||||
"serde",
|
|
||||||
"walkdir",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi-util"
|
name = "winapi-util"
|
||||||
version = "0.1.9"
|
version = "0.1.9"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
[package]
|
[package]
|
||||||
name = "where-cmd"
|
name = "carto"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
|
67
justfile
67
justfile
|
@ -4,16 +4,71 @@ default:
|
||||||
run mode="quiet":
|
run mode="quiet":
|
||||||
#! /bin/bash
|
#! /bin/bash
|
||||||
case "{{ mode }}" in
|
case "{{ mode }}" in
|
||||||
quiet)
|
quiet)
|
||||||
RUSTFLAGS="-A warnings" cargo run --quiet
|
RUSTFLAGS="-A warnings" cargo run --quiet
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
cargo run
|
cargo run
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
build:
|
build:
|
||||||
cargo build --release
|
cargo build --release
|
||||||
|
|
||||||
|
install: build
|
||||||
|
#! /bin/bash
|
||||||
|
# Build release binary
|
||||||
|
echo "Building release binary..."
|
||||||
|
|
||||||
|
# Create local bin directory if it doesn't exist
|
||||||
|
mkdir -p ~/.local/bin
|
||||||
|
|
||||||
|
# Copy binary to local bin (rename from carto to carto)
|
||||||
|
cp target/release/carto ~/.local/bin/carto
|
||||||
|
chmod +x ~/.local/bin/carto
|
||||||
|
|
||||||
|
echo "Installed 'carto' to ~/.local/bin/carto"
|
||||||
|
echo ""
|
||||||
|
echo "Make sure ~/.local/bin is in your PATH:"
|
||||||
|
echo " echo 'export PATH=\"\$HOME/.local/bin:\$PATH\"' >> ~/.zshrc"
|
||||||
|
echo " source ~/.zshrc"
|
||||||
|
echo ""
|
||||||
|
echo "Usage examples:"
|
||||||
|
echo " carto packages --source flatpak"
|
||||||
|
echo " carto tree --package bash"
|
||||||
|
echo " carto file /usr/bin/bash"
|
||||||
|
|
||||||
|
uninstall:
|
||||||
|
#! /bin/bash
|
||||||
|
if [ -f ~/.local/bin/carto ]; then
|
||||||
|
rm ~/.local/bin/carto
|
||||||
|
echo "Uninstalled 'carto' from ~/.local/bin"
|
||||||
|
else
|
||||||
|
echo "'carto' not found in ~/.local/bin"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clean up old names if they exist
|
||||||
|
if [ -f ~/.local/bin/where ]; then
|
||||||
|
rm ~/.local/bin/where
|
||||||
|
echo "Also removed old 'where' binary"
|
||||||
|
fi
|
||||||
|
|
||||||
|
system-install: build
|
||||||
|
#! /bin/bash
|
||||||
|
# System-wide installation (requires sudo)
|
||||||
|
echo "Installing system-wide (requires sudo)..."
|
||||||
|
sudo cp target/release/carto /usr/local/bin/carto
|
||||||
|
sudo chmod +x /usr/local/bin/carto
|
||||||
|
echo "Installed 'carto' to /usr/local/bin/carto"
|
||||||
|
|
||||||
|
system-uninstall:
|
||||||
|
#! /bin/bash
|
||||||
|
if [ -f /usr/local/bin/carto ]; then
|
||||||
|
sudo rm /usr/local/bin/carto
|
||||||
|
echo "Uninstalled 'carto' from /usr/local/bin"
|
||||||
|
else
|
||||||
|
echo "'carto' not found in /usr/local/bin"
|
||||||
|
fi
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
cargo clean
|
cargo clean
|
152
readme.md
152
readme.md
|
@ -1,94 +1,43 @@
|
||||||
# WHERE - Universal System File and Package Mapper
|
# CARTO - Universal System File and Package Mapper
|
||||||
|
|
||||||
A command-line tool that provides comprehensive observability into your Linux system's package installations and file ownership. Built in Rust for performance and reliability.
|
A command-line tool that provides comprehensive observability into your Linux system's package installations and file ownership. Built in Rust for performance and reliability.
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- **Universal Package Manager Support** - Works with DNF, APT, Flatpak, Snap, and more
|
|
||||||
- **File Ownership Tracking** - Instantly identify which package owns any file
|
|
||||||
- **Package Tree Visualization** - See all files installed by a package in tree format
|
|
||||||
- **Cross-Distribution Compatibility** - Single tool that works across Linux distributions
|
|
||||||
- **High Performance** - Memory-mapped binary format for fast queries
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone <repository-url>
|
git clone <repository-url>
|
||||||
cd where
|
cd carto
|
||||||
cargo build --release
|
cargo build --release
|
||||||
|
just install
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
### Show Package File Tree
|
# Show package file tree
|
||||||
Display all files installed by a specific package in a hierarchical tree structure:
|
carto tree --package bash
|
||||||
|
carto tree --package "Signal Desktop (org.signal.Signal)"
|
||||||
|
|
||||||
```bash
|
# Package information
|
||||||
# Show files installed by bash package
|
carto package bash
|
||||||
cargo run -- tree --package bash
|
|
||||||
|
|
||||||
# Show files for Flatpak application
|
# File ownership
|
||||||
cargo run -- tree --package "Signal Desktop (org.signal.Signal)"
|
carto file /usr/bin/bash
|
||||||
|
|
||||||
# Show trees for first 5 packages (demo mode)
|
# List packages
|
||||||
cargo run -- tree
|
carto packages
|
||||||
```
|
carto packages --source dnf
|
||||||
|
carto packages --source flatpak
|
||||||
|
|
||||||
### Package Information
|
# Find files in package
|
||||||
Get detailed information about a specific package:
|
carto find --package bash
|
||||||
|
|
||||||
```bash
|
|
||||||
# Show package details and file tree
|
|
||||||
cargo run -- package bash
|
|
||||||
```
|
|
||||||
|
|
||||||
### File Ownership
|
|
||||||
Find out which package owns a specific file:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Check who owns /usr/bin/bash
|
|
||||||
cargo run -- file /usr/bin/bash
|
|
||||||
```
|
|
||||||
|
|
||||||
### List All Packages
|
|
||||||
Display all installed packages from all managers:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# List all packages from all managers
|
|
||||||
cargo run -- packages
|
|
||||||
|
|
||||||
# List only DNF packages
|
|
||||||
cargo run -- packages --source dnf
|
|
||||||
|
|
||||||
# List only Flatpak packages
|
|
||||||
cargo run -- packages --source flatpak
|
|
||||||
```
|
|
||||||
|
|
||||||
### Find Files
|
|
||||||
Search for files within packages:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Find all files in bash package
|
|
||||||
cargo run -- find --package bash
|
|
||||||
|
|
||||||
# Find all files in Flatpak application
|
|
||||||
cargo run -- find --package "Signal Desktop (org.signal.Signal)"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Example Output
|
## Example Output
|
||||||
|
|
||||||
### Package Tree
|
```bash
|
||||||
```
|
|
||||||
bash 5.2.15-6.fc41
|
bash 5.2.15-6.fc41
|
||||||
├── usr/
|
├── usr/
|
||||||
│ ├── bin/
|
│ ├── bin/
|
||||||
│ │ └── bash
|
│ │ └── bash
|
||||||
│ └── share/
|
│ └── share/
|
||||||
│ ├── doc/
|
|
||||||
│ │ └── bash/
|
|
||||||
│ │ ├── CHANGES
|
|
||||||
│ │ ├── README
|
|
||||||
│ │ └── NEWS
|
|
||||||
│ └── man/
|
│ └── man/
|
||||||
│ └── man1/
|
│ └── man1/
|
||||||
│ └── bash.1.gz
|
│ └── bash.1.gz
|
||||||
|
@ -96,80 +45,13 @@ bash 5.2.15-6.fc41
|
||||||
└── skel/
|
└── skel/
|
||||||
└── .bashrc
|
└── .bashrc
|
||||||
|
|
||||||
Signal Desktop (org.signal.Signal) 7.63.0
|
|
||||||
├── /var/lib/flatpak/app/org.signal.Signal/
|
|
||||||
├── /var/lib/flatpak/exports/share/applications/org.signal.Signal.desktop
|
|
||||||
└── ~/.var/app/org.signal.Signal/
|
|
||||||
```
|
|
||||||
|
|
||||||
### File Ownership
|
|
||||||
```
|
|
||||||
/usr/bin/bash is owned by package: bash
|
/usr/bin/bash is owned by package: bash
|
||||||
~/.var/app/org.signal.Signal is owned by package: org.signal.Signal
|
|
||||||
```
|
|
||||||
|
|
||||||
### Package Listing
|
|
||||||
```
|
|
||||||
DNF packages (1,847 total):
|
DNF packages (1,847 total):
|
||||||
bash 5.2.15-6.fc41
|
bash 5.2.15-6.fc41
|
||||||
glibc 2.37-4.fc38
|
glibc 2.37-4.fc38
|
||||||
kernel 6.8.5-301.fc40
|
|
||||||
|
|
||||||
Flatpak packages (7 total):
|
Flatpak packages (7 total):
|
||||||
Zen (app.zen_browser.zen) 1.14.9b
|
|
||||||
Signal Desktop (org.signal.Signal) 7.63.0
|
Signal Desktop (org.signal.Signal) 7.63.0
|
||||||
GIMP (org.gimp.GIMP) 3.0.4
|
GIMP (org.gimp.GIMP) 3.0.4
|
||||||
```
|
```
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
The tool is designed with a modular architecture supporting multiple package managers:
|
|
||||||
|
|
||||||
- **Package Manager Abstraction** - Common interface for DNF, Flatpak, and future managers
|
|
||||||
- **Universal File Tracking** - Track files across traditional packages and sandboxed applications
|
|
||||||
- **Source-Aware Filtering** - Filter by package manager type (DNF, Flatpak, etc.)
|
|
||||||
- **Binary Database Format** - High-performance storage for file metadata (planned)
|
|
||||||
- **CLI Interface** - Clean command-line interface built with Clap
|
|
||||||
- **Tree Visualization** - Hierarchical display of package contents
|
|
||||||
|
|
||||||
## Supported Package Managers
|
|
||||||
|
|
||||||
- ✅ **DNF/RPM** (Fedora, RHEL, CentOS) - Full support
|
|
||||||
- ✅ **Flatpak** (Universal) - Full support
|
|
||||||
- 🚧 **APT** (Debian, Ubuntu) - Coming Soon
|
|
||||||
- 🚧 **Snap** - Coming Soon
|
|
||||||
- 🚧 **Pacman** (Arch Linux) - Coming Soon
|
|
||||||
|
|
||||||
## Development Philosophy
|
|
||||||
|
|
||||||
This tool is part of the **IntentOS** project, focused on creating systems where every component has clear provenance and purpose. The core principle is "know where everything is" - eliminating the entropy and "shrapnel" that accumulates in traditional Linux installations.
|
|
||||||
|
|
||||||
## Contributing
|
|
||||||
|
|
||||||
Built with Rust 2021 edition. Key dependencies:
|
|
||||||
- `clap` - Command line argument parsing
|
|
||||||
- `memmap2` - Memory-mapped file access
|
|
||||||
- `chrono` - Date/time handling
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
[Add your license here]
|
|
||||||
|
|
||||||
## Roadmap
|
|
||||||
|
|
||||||
- [x] DNF/RPM support with file ownership tracking
|
|
||||||
- [x] Flatpak support with sandboxed application tracking
|
|
||||||
- [x] Universal package listing with source filtering
|
|
||||||
- [x] Package file tree visualization
|
|
||||||
- [ ] Snap support
|
|
||||||
- [ ] APT support for Debian/Ubuntu systems
|
|
||||||
- [ ] Real-time filesystem monitoring
|
|
||||||
- [ ] Binary database format for fast queries
|
|
||||||
- [ ] GUI interface
|
|
||||||
- [ ] Integration with IntentOS package manager
|
|
||||||
- [ ] Package installation profiles
|
|
||||||
- [ ] System entropy analysis tools
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
*WHERE gives you complete visibility into your system's package installations - because you should know where everything comes from.*
|
|
|
@ -3,7 +3,7 @@ pub mod tree;
|
||||||
use clap::{Parser, Subcommand};
|
use clap::{Parser, Subcommand};
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[command(name = "where", about = "Universal system file and package mapper")]
|
#[command(name = "carto", about = "Universal system file and package mapper")]
|
||||||
pub struct Cli {
|
pub struct Cli {
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
pub command: Commands,
|
pub command: Commands,
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::{PackageManager, PackageSource, WhereError};
|
use crate::{PackageManager, PackageSource, CartoError};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
pub fn print_package_tree(manager: &dyn PackageManager, package_name: &str) -> Result<(), WhereError> {
|
pub fn print_package_tree(manager: &dyn PackageManager, package_name: &str) -> Result<(), CartoError> {
|
||||||
// Get package info first
|
// Get package info first
|
||||||
let package_info = match manager.get_package_info(package_name)? {
|
let package_info = match manager.get_package_info(package_name)? {
|
||||||
Some(info) => info,
|
Some(info) => info,
|
||||||
|
|
26
src/main.rs
26
src/main.rs
|
@ -20,7 +20,7 @@ pub struct PackageInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum WhereError {
|
pub enum CartoError {
|
||||||
Io(std::io::Error),
|
Io(std::io::Error),
|
||||||
Utf8(std::string::FromUtf8Error),
|
Utf8(std::string::FromUtf8Error),
|
||||||
CommandFailed(String),
|
CommandFailed(String),
|
||||||
|
@ -30,32 +30,32 @@ pub enum WhereError {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implement From traits for error conversion
|
// Implement From traits for error conversion
|
||||||
impl From<std::io::Error> for WhereError {
|
impl From<std::io::Error> for CartoError {
|
||||||
fn from(err: std::io::Error) -> Self {
|
fn from(err: std::io::Error) -> Self {
|
||||||
WhereError::Io(err)
|
CartoError::Io(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<std::string::FromUtf8Error> for WhereError {
|
impl From<std::string::FromUtf8Error> for CartoError {
|
||||||
fn from(err: std::string::FromUtf8Error) -> Self {
|
fn from(err: std::string::FromUtf8Error) -> Self {
|
||||||
WhereError::Utf8(err)
|
CartoError::Utf8(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for WhereError {
|
impl std::fmt::Display for CartoError {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
WhereError::Io(e) => write!(f, "IO error: {}", e),
|
CartoError::Io(e) => write!(f, "IO error: {}", e),
|
||||||
WhereError::Utf8(e) => write!(f, "UTF-8 error: {}", e),
|
CartoError::Utf8(e) => write!(f, "UTF-8 error: {}", e),
|
||||||
WhereError::CommandFailed(s) => write!(f, "Command failed: {}", s),
|
CartoError::CommandFailed(s) => write!(f, "Command failed: {}", s),
|
||||||
WhereError::PackageNotFound(s) => write!(f, "Package not found: {}", s),
|
CartoError::PackageNotFound(s) => write!(f, "Package not found: {}", s),
|
||||||
WhereError::ManagerNotAvailable(s) => write!(f, "Manager not available: {}", s),
|
CartoError::ManagerNotAvailable(s) => write!(f, "Manager not available: {}", s),
|
||||||
WhereError::InvalidDatabase => write!(f, "Invalid database format"),
|
CartoError::InvalidDatabase => write!(f, "Invalid database format"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::error::Error for WhereError {}
|
impl std::error::Error for CartoError {}
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let cli = Cli::parse();
|
let cli = Cli::parse();
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use crate::{PackageInfo, WhereError};
|
use crate::{PackageInfo, CartoError};
|
||||||
use super::{PackageManager, PackageSource};
|
use super::{PackageManager, PackageSource};
|
||||||
pub struct DnfManager;
|
pub struct DnfManager;
|
||||||
|
|
||||||
|
@ -15,14 +15,14 @@ impl PackageManager for DnfManager {
|
||||||
Path::new("/usr/bin/dnf").exists() || Path::new("/usr/bin/rpm").exists()
|
Path::new("/usr/bin/dnf").exists() || Path::new("/usr/bin/rpm").exists()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_installed_packages(&self) -> Result<Vec<PackageInfo>, WhereError> {
|
fn get_installed_packages(&self) -> Result<Vec<PackageInfo>, CartoError> {
|
||||||
// Use rpm directly for better performance and parsing
|
// Use rpm directly for better performance and parsing
|
||||||
let output = Command::new("rpm")
|
let output = Command::new("rpm")
|
||||||
.args(["-qa", "--queryformat", "%{NAME}\t%{VERSION}-%{RELEASE}\t%{INSTALLTIME}\t%{SIZE}\n"])
|
.args(["-qa", "--queryformat", "%{NAME}\t%{VERSION}-%{RELEASE}\t%{INSTALLTIME}\t%{SIZE}\n"])
|
||||||
.output()?;
|
.output()?;
|
||||||
|
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
return Err(WhereError::CommandFailed("rpm query failed".to_string()));
|
return Err(CartoError::CommandFailed("rpm query failed".to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let stdout = String::from_utf8(output.stdout)?;
|
let stdout = String::from_utf8(output.stdout)?;
|
||||||
|
@ -44,13 +44,13 @@ impl PackageManager for DnfManager {
|
||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_package_files(&self, package: &str) -> Result<Vec<PathBuf>, WhereError> {
|
fn get_package_files(&self, package: &str) -> Result<Vec<PathBuf>, CartoError> {
|
||||||
let output = Command::new("rpm")
|
let output = Command::new("rpm")
|
||||||
.args(["-ql", package])
|
.args(["-ql", package])
|
||||||
.output()?;
|
.output()?;
|
||||||
|
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
return Err(WhereError::PackageNotFound(package.to_string()));
|
return Err(CartoError::PackageNotFound(package.to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let stdout = String::from_utf8(output.stdout)?;
|
let stdout = String::from_utf8(output.stdout)?;
|
||||||
|
@ -61,7 +61,7 @@ impl PackageManager for DnfManager {
|
||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_file_owner(&self, path: &Path) -> Result<Option<String>, WhereError> {
|
fn get_file_owner(&self, path: &Path) -> Result<Option<String>, CartoError> {
|
||||||
let output = Command::new("rpm")
|
let output = Command::new("rpm")
|
||||||
.args(["-qf", &path.to_string_lossy()])
|
.args(["-qf", &path.to_string_lossy()])
|
||||||
.output()?;
|
.output()?;
|
||||||
|
@ -82,12 +82,12 @@ impl PackageManager for DnfManager {
|
||||||
if stderr.contains("is not owned by any package") {
|
if stderr.contains("is not owned by any package") {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
} else {
|
} else {
|
||||||
Err(WhereError::CommandFailed(format!("rpm query failed: {}", stderr)))
|
Err(CartoError::CommandFailed(format!("rpm query failed: {}", stderr)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_package_info(&self, package: &str) -> Result<Option<PackageInfo>, WhereError> {
|
fn get_package_info(&self, package: &str) -> Result<Option<PackageInfo>, CartoError> {
|
||||||
let output = Command::new("rpm")
|
let output = Command::new("rpm")
|
||||||
.args(["-qi", package])
|
.args(["-qi", package])
|
||||||
.output()?;
|
.output()?;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
use crate::{PackageInfo, WhereError};
|
use crate::{PackageInfo, CartoError};
|
||||||
use super::{PackageManager, PackageSource};
|
use super::{PackageManager, PackageSource};
|
||||||
|
|
||||||
pub struct FlatpakManager;
|
pub struct FlatpakManager;
|
||||||
|
@ -17,13 +17,13 @@ impl PackageManager for FlatpakManager {
|
||||||
Command::new("flatpak").arg("--version").output().is_ok()
|
Command::new("flatpak").arg("--version").output().is_ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_installed_packages(&self) -> Result<Vec<PackageInfo>, WhereError> {
|
fn get_installed_packages(&self) -> Result<Vec<PackageInfo>, CartoError> {
|
||||||
let output = Command::new("flatpak")
|
let output = Command::new("flatpak")
|
||||||
.args(["list", "--columns=application,name,version,branch,installation"])
|
.args(["list", "--columns=application,name,version,branch,installation"])
|
||||||
.output()?;
|
.output()?;
|
||||||
|
|
||||||
if !output.status.success() {
|
if !output.status.success() {
|
||||||
return Err(WhereError::CommandFailed("flatpak list failed".to_string()));
|
return Err(CartoError::CommandFailed("flatpak list failed".to_string()));
|
||||||
}
|
}
|
||||||
|
|
||||||
let stdout = String::from_utf8(output.stdout)?;
|
let stdout = String::from_utf8(output.stdout)?;
|
||||||
|
@ -56,7 +56,7 @@ impl PackageManager for FlatpakManager {
|
||||||
Ok(packages)
|
Ok(packages)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_package_files(&self, package: &str) -> Result<Vec<PathBuf>, WhereError> {
|
fn get_package_files(&self, package: &str) -> Result<Vec<PathBuf>, CartoError> {
|
||||||
// Extract app ID from package name (format: "Name (app.id)")
|
// Extract app ID from package name (format: "Name (app.id)")
|
||||||
let app_id = if package.contains('(') && package.contains(')') {
|
let app_id = if package.contains('(') && package.contains(')') {
|
||||||
package.split('(').nth(1)
|
package.split('(').nth(1)
|
||||||
|
@ -89,7 +89,7 @@ impl PackageManager for FlatpakManager {
|
||||||
Ok(files.into_iter().filter(|p| p.exists()).collect())
|
Ok(files.into_iter().filter(|p| p.exists()).collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_file_owner(&self, path: &Path) -> Result<Option<String>, WhereError> {
|
fn get_file_owner(&self, path: &Path) -> Result<Option<String>, CartoError> {
|
||||||
let path_str = path.to_string_lossy();
|
let path_str = path.to_string_lossy();
|
||||||
|
|
||||||
// Check if it's in a Flatpak directory
|
// Check if it's in a Flatpak directory
|
||||||
|
@ -123,7 +123,7 @@ impl PackageManager for FlatpakManager {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_package_info(&self, package: &str) -> Result<Option<PackageInfo>, WhereError> {
|
fn get_package_info(&self, package: &str) -> Result<Option<PackageInfo>, CartoError> {
|
||||||
// Extract app ID from package name
|
// Extract app ID from package name
|
||||||
let app_id = if package.contains('(') && package.contains(')') {
|
let app_id = if package.contains('(') && package.contains(')') {
|
||||||
package.split('(').nth(1)
|
package.split('(').nth(1)
|
||||||
|
@ -176,7 +176,7 @@ impl PackageManager for FlatpakManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FlatpakManager {
|
impl FlatpakManager {
|
||||||
fn get_app_location(&self, app_id: &str) -> Result<Option<PathBuf>, WhereError> {
|
fn get_app_location(&self, app_id: &str) -> Result<Option<PathBuf>, CartoError> {
|
||||||
let output = Command::new("flatpak")
|
let output = Command::new("flatpak")
|
||||||
.args(["info", "--show-location", app_id])
|
.args(["info", "--show-location", app_id])
|
||||||
.output()?;
|
.output()?;
|
||||||
|
|
|
@ -6,8 +6,7 @@ pub mod flatpak;
|
||||||
use self::flatpak::FlatpakManager;
|
use self::flatpak::FlatpakManager;
|
||||||
use self::dnf::DnfManager;
|
use self::dnf::DnfManager;
|
||||||
|
|
||||||
|
use crate::{PackageInfo, CartoError};
|
||||||
use crate::{PackageInfo, WhereError};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
pub enum PackageSource {
|
pub enum PackageSource {
|
||||||
|
@ -62,10 +61,10 @@ pub trait PackageManager {
|
||||||
fn name(&self) -> &'static str;
|
fn name(&self) -> &'static str;
|
||||||
fn source_type(&self) -> PackageSource;
|
fn source_type(&self) -> PackageSource;
|
||||||
fn is_available(&self) -> bool;
|
fn is_available(&self) -> bool;
|
||||||
fn get_installed_packages(&self) -> Result<Vec<PackageInfo>, WhereError>;
|
fn get_installed_packages(&self) -> Result<Vec<PackageInfo>, CartoError>;
|
||||||
fn get_package_files(&self, package: &str) -> Result<Vec<PathBuf>, WhereError>;
|
fn get_package_files(&self, package: &str) -> Result<Vec<PathBuf>, CartoError>;
|
||||||
fn get_file_owner(&self, path: &Path) -> Result<Option<String>, WhereError>;
|
fn get_file_owner(&self, path: &Path) -> Result<Option<String>, CartoError>;
|
||||||
fn get_package_info(&self, package: &str) -> Result<Option<PackageInfo>, WhereError>;
|
fn get_package_info(&self, package: &str) -> Result<Option<PackageInfo>, CartoError>;
|
||||||
|
|
||||||
/// Priority for file ownership resolution (higher = preferred)
|
/// Priority for file ownership resolution (higher = preferred)
|
||||||
fn priority(&self) -> u8 {
|
fn priority(&self) -> u8 {
|
||||||
|
@ -108,7 +107,7 @@ pub fn detect_available_managers() -> Vec<Box<dyn PackageManager>> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find which package owns a file, checking all available managers
|
/// Find which package owns a file, checking all available managers
|
||||||
pub fn find_file_owner(path: &Path) -> Result<Option<(String, PackageSource)>, WhereError> {
|
pub fn find_file_owner(path: &Path) -> Result<Option<(String, PackageSource)>, CartoError> {
|
||||||
let managers = detect_available_managers();
|
let managers = detect_available_managers();
|
||||||
|
|
||||||
for manager in managers {
|
for manager in managers {
|
||||||
|
@ -121,7 +120,7 @@ pub fn find_file_owner(path: &Path) -> Result<Option<(String, PackageSource)>, W
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get all installed packages from all available managers
|
/// Get all installed packages from all available managers
|
||||||
pub fn get_all_packages() -> Result<Vec<PackageInfo>, WhereError> {
|
pub fn get_all_packages() -> Result<Vec<PackageInfo>, CartoError> {
|
||||||
let managers = detect_available_managers();
|
let managers = detect_available_managers();
|
||||||
let mut all_packages = Vec::new();
|
let mut all_packages = Vec::new();
|
||||||
|
|
||||||
|
@ -142,7 +141,7 @@ pub fn get_all_packages() -> Result<Vec<PackageInfo>, WhereError> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get package information by name, searching all managers
|
/// Get package information by name, searching all managers
|
||||||
pub fn find_package(name: &str) -> Result<Option<PackageInfo>, WhereError> {
|
pub fn find_package(name: &str) -> Result<Option<PackageInfo>, CartoError> {
|
||||||
let managers = detect_available_managers();
|
let managers = detect_available_managers();
|
||||||
|
|
||||||
for manager in managers {
|
for manager in managers {
|
||||||
|
@ -155,7 +154,7 @@ pub fn find_package(name: &str) -> Result<Option<PackageInfo>, WhereError> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get packages from a specific source type
|
/// Get packages from a specific source type
|
||||||
pub fn get_packages_by_source(source: PackageSource) -> Result<Vec<PackageInfo>, WhereError> {
|
pub fn get_packages_by_source(source: PackageSource) -> Result<Vec<PackageInfo>, CartoError> {
|
||||||
let managers = detect_available_managers();
|
let managers = detect_available_managers();
|
||||||
|
|
||||||
for manager in managers {
|
for manager in managers {
|
||||||
|
@ -164,7 +163,7 @@ pub fn get_packages_by_source(source: PackageSource) -> Result<Vec<PackageInfo>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(WhereError::ManagerNotAvailable(source.name().to_string()))
|
Err(CartoError::ManagerNotAvailable(source.name().to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Summary of available package managers
|
/// Summary of available package managers
|
||||||
|
|
Loading…
Reference in New Issue