Overall cleanup, refactor and performance tweaks
This commit is contained in:
parent
7d89f51eac
commit
ae9ab02dc4
9 changed files with 241 additions and 183 deletions
206
src/conf.rs
206
src/conf.rs
|
|
@ -1,7 +1,5 @@
|
|||
use std::{
|
||||
collections::HashMap,
|
||||
fs::{self, DirEntry},
|
||||
path::PathBuf,
|
||||
collections::HashMap, fs::{self, DirEntry}, path::PathBuf
|
||||
};
|
||||
|
||||
use crate::{
|
||||
|
|
@ -9,66 +7,71 @@ use crate::{
|
|||
run::Command,
|
||||
};
|
||||
|
||||
pub fn load() -> Configuration {
|
||||
pub fn load() -> Result<Configuration, Error> {
|
||||
log::elog("Loading configuration");
|
||||
|
||||
let mut conf = Configuration {
|
||||
su_command: SuCommand {
|
||||
command: Command::new("su", &["-c", "{% command %}"]),
|
||||
wraps: true,
|
||||
},
|
||||
su_command_wraps: None,
|
||||
};
|
||||
let mut candidate = Configuration::default();
|
||||
|
||||
let root = get_root();
|
||||
let Ok(contents) = fs::read_to_string(root.join("tori.conf")) else {
|
||||
eprintln!("Failed reading configuration file at {root:?}");
|
||||
return conf;
|
||||
};
|
||||
let contents = fs::read_to_string(root.join("tori.conf"))?;
|
||||
|
||||
let lines: Vec<(&str, &str)> = contents
|
||||
let map: HashMap<String, String> = contents
|
||||
.lines()
|
||||
.filter_map(|line| line.split_once('='))
|
||||
.map(|(k, v)| (k.to_owned(), v.to_owned()))
|
||||
.collect();
|
||||
|
||||
let mut map: HashMap<String, String> = HashMap::new();
|
||||
|
||||
for line in &lines {
|
||||
let (key, value) = line;
|
||||
map.insert(key.to_string(), value.to_string());
|
||||
if let Some(su_command) = map.get("su_command") {
|
||||
let wraps = map.get("su_command_wraps").is_some_and(|v| v == "true");
|
||||
candidate.su_command = parse_su_command(su_command, wraps)?;
|
||||
}
|
||||
|
||||
elog(&format!("{lines:#?}"));
|
||||
if let Some(merge_strategy) = map.get("merge_strategy") {
|
||||
candidate.merge_strategy = match merge_strategy.as_str() {
|
||||
"prefer configuration" => MergeStrategy::PreferConfig,
|
||||
"prefer system" => MergeStrategy::PreferSystem,
|
||||
_ => MergeStrategy::default(),
|
||||
|
||||
if let Some(su_command) = map.get("su_command") {
|
||||
let split: Vec<String> = su_command
|
||||
.split(' ')
|
||||
.filter(|s| !s.is_empty())
|
||||
.map(|s| s.to_string())
|
||||
.collect();
|
||||
|
||||
if let Some((base, args)) = split.split_first()
|
||||
&& let Ok(resolved_path) = resolve_from_path(base)
|
||||
{
|
||||
elog(&format!(
|
||||
"Succesfully resolved 'su_command' configuration value \
|
||||
{su_command} through PATH to {resolved_path:?}, with base \
|
||||
{base} and args {args:?}"
|
||||
));
|
||||
conf.su_command = SuCommand {
|
||||
command: Command::new_from_strings(base, args),
|
||||
wraps: map.get("su_command_wraps").is_some_and(|v| v == "true"),
|
||||
}
|
||||
} else {
|
||||
eprintln!("Failed validation of 'su_command' configuration value");
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(su_command_wraps) = map.get("su_command_wraps") {
|
||||
conf.su_command_wraps = Some(su_command_wraps == "true");
|
||||
}
|
||||
Ok(candidate)
|
||||
}
|
||||
|
||||
conf
|
||||
fn parse_su_command(config_value: &str, wraps: bool) -> Result<SuCommand, Error> {
|
||||
let split: Vec<&str> = config_value
|
||||
.split(' ')
|
||||
.filter(|s| !s.is_empty())
|
||||
.collect();
|
||||
|
||||
let Some((base, args)) = split.split_first() else {
|
||||
return Err(Error::new("Configuration line is empty", ErrorKind::MalformedConfigLine))
|
||||
};
|
||||
|
||||
let Ok(resolved_base) = resolve_command(base) else {
|
||||
return Err(Error::new(
|
||||
"su_command does not resolve to a command in PATH",
|
||||
ErrorKind::CommandNotInPath,
|
||||
))
|
||||
};
|
||||
|
||||
let Some(resolved_base_str) = resolved_base.to_str() else {
|
||||
return Err(Error::new(
|
||||
"su_command path contains invalid characters (expected UTF-8)",
|
||||
ErrorKind::UTF8,
|
||||
))
|
||||
};
|
||||
|
||||
elog(&format!(
|
||||
"Successfully resolved 'su_command' configuration value \
|
||||
{config_value} through PATH from base {base} and args {args:?} \
|
||||
to {resolved_base:?}"
|
||||
));
|
||||
|
||||
Ok(SuCommand {
|
||||
command: Command::new(resolved_base_str, args),
|
||||
wraps,
|
||||
})
|
||||
}
|
||||
|
||||
fn get_root() -> PathBuf {
|
||||
|
|
@ -93,22 +96,17 @@ fn get_root() -> PathBuf {
|
|||
}
|
||||
}
|
||||
|
||||
fn resolve_from_path(command: &str) -> Result<PathBuf, String> {
|
||||
fn resolve_command(command: &str) -> Result<PathBuf, Error> {
|
||||
elog(&format!("Solving from PATH for {command}"));
|
||||
|
||||
let paths: Vec<PathBuf> = if let Ok(path) = std::env::var("PATH") {
|
||||
path.split(':')
|
||||
.filter(|p| !p.is_empty() && PathBuf::from(p).is_dir())
|
||||
.map(PathBuf::from)
|
||||
.collect()
|
||||
} else {
|
||||
elog("Error: PATH is not set");
|
||||
return Err(format!(
|
||||
"{command} not found: PATH is not set in the environment"
|
||||
));
|
||||
};
|
||||
let path_var = std::env::var("PATH")?;
|
||||
|
||||
let paths = path_var.split(':')
|
||||
.filter(|p| !p.is_empty() && PathBuf::from(p).is_dir())
|
||||
.map(PathBuf::from);
|
||||
|
||||
elog(&format!("Gathered paths {paths:?}"));
|
||||
|
||||
for path in paths {
|
||||
elog(&format!("On path {path:?}"));
|
||||
let Ok(mut entries) = fs::read_dir(path) else {
|
||||
|
|
@ -136,29 +134,107 @@ fn resolve_from_path(command: &str) -> Result<PathBuf, String> {
|
|||
continue;
|
||||
};
|
||||
}
|
||||
Err(format!(
|
||||
"{command} not found in any of the directories in PATH"
|
||||
))
|
||||
Err(Error {
|
||||
message: format!("{command} not found in any of the directories in PATH"),
|
||||
kind: ErrorKind::CommandNotInPath,
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct Configuration {
|
||||
pub su_command: SuCommand,
|
||||
pub su_command_wraps: Option<bool>,
|
||||
pub merge_strategy: MergeStrategy,
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq)]
|
||||
pub enum MergeStrategy {
|
||||
PreferSystem,
|
||||
PreferConfig,
|
||||
#[default]
|
||||
Interactive,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct SuCommand {
|
||||
command: Command,
|
||||
wraps: bool,
|
||||
}
|
||||
|
||||
impl SuCommand {
|
||||
pub fn command(&self) -> Command {
|
||||
self.command.clone()
|
||||
pub fn command(&self) -> &Command {
|
||||
&self.command
|
||||
}
|
||||
|
||||
pub const fn wraps(&self) -> bool {
|
||||
self.wraps
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SuCommand {
|
||||
fn default() -> SuCommand {
|
||||
SuCommand {
|
||||
command: Command::new("su", &["-c", "{% command %}"]),
|
||||
wraps: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Error {
|
||||
message: String,
|
||||
kind: ErrorKind,
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub fn new(message: &str, kind: ErrorKind) -> Error {
|
||||
Error {
|
||||
message: message.to_owned(),
|
||||
kind,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "{}: {}", self.kind, self.message)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::env::VarError> for Error {
|
||||
fn from(var_error: std::env::VarError) -> Error {
|
||||
Error {
|
||||
message: format!("Environment variable error: {var_error}"),
|
||||
kind: ErrorKind::VarError,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for Error {
|
||||
fn from(io_error: std::io::Error) -> Error {
|
||||
Error {
|
||||
message: format!("{}: {io_error}", io_error.kind()),
|
||||
kind: ErrorKind::IO,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum ErrorKind {
|
||||
CommandNotInPath,
|
||||
VarError,
|
||||
MalformedConfigLine,
|
||||
UTF8,
|
||||
IO,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ErrorKind {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
use ErrorKind::*;
|
||||
let s = match self {
|
||||
VarError => "Environment variable error",
|
||||
CommandNotInPath => "Command not in PATH",
|
||||
MalformedConfigLine => "Malformed configuration line",
|
||||
UTF8 => "Invalid characters could not be decoded (expected UTF-8)",
|
||||
IO => "Input/Output error",
|
||||
};
|
||||
write!(f, "{s}")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
pub fn elog(message: &str) {
|
||||
// DONE MUST be printed only if DEBUG is set in the environment
|
||||
if let Ok(debug) = std::env::var("DEBUG")
|
||||
&& !debug.is_empty()
|
||||
{
|
||||
|
|
|
|||
12
src/main.rs
12
src/main.rs
|
|
@ -2,11 +2,17 @@ use tori::{conf, log, run, state};
|
|||
|
||||
fn main() -> std::process::ExitCode {
|
||||
log::elog(&format!("tori {}", env!("CARGO_PKG_VERSION")));
|
||||
let configuration = conf::load();
|
||||
let configuration = match conf::load() {
|
||||
Ok(c) => c,
|
||||
Err(error) => {
|
||||
eprintln!("Configuration error: {error}");
|
||||
return 1.into()
|
||||
},
|
||||
};
|
||||
log::elog(&format!("Configuration: {configuration:#?}"));
|
||||
let order = run::teller::parse(std::env::args(), &configuration);
|
||||
let order = run::teller::parse(std::env::args());
|
||||
log::elog(&format!("Order: {order:#?}"));
|
||||
let state = state::setup(&configuration, &[order]);
|
||||
let state = state::setup(configuration, &[order]);
|
||||
log::elog(&format!("State: {state:#?}"));
|
||||
let result = run::expeditor::fulfill(&state);
|
||||
log::elog(&format!("Filled Order: {result:#?}"));
|
||||
|
|
|
|||
|
|
@ -11,12 +11,12 @@ pub struct OperatingSystem {
|
|||
}
|
||||
|
||||
impl OperatingSystem {
|
||||
pub fn kind(&self) -> Kind {
|
||||
self.kind.clone()
|
||||
pub fn kind(&self) -> &Kind {
|
||||
&self.kind
|
||||
}
|
||||
|
||||
pub fn packager(&self) -> Packager {
|
||||
self.packager.clone()
|
||||
pub fn packager(&self) -> &Packager {
|
||||
&self.packager
|
||||
}
|
||||
|
||||
pub const fn new(kind: Kind, packager: Packager) -> OperatingSystem {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
use std::fs::read_to_string;
|
||||
use std::{collections::HashSet, fs::read_to_string, iter};
|
||||
|
||||
use crate::{
|
||||
conf::Configuration,
|
||||
|
|
@ -38,59 +38,52 @@ impl Packages for Apt {
|
|||
"dpkg-query",
|
||||
&["--show", "--showformat", "${Package} ${Status}\\n"],
|
||||
))?;
|
||||
let all: Vec<String> = raw_all
|
||||
.lines()
|
||||
.filter_map(|line| {
|
||||
let pair = line.split_once(' ');
|
||||
match pair {
|
||||
Some((pkg, "install ok installed")) => Some(pkg.to_string()),
|
||||
Some(_) => None,
|
||||
None => {
|
||||
elog("Warning: Dropped a None pair when cleaning up package list");
|
||||
None
|
||||
}
|
||||
let all = raw_all.lines().filter_map(|line| {
|
||||
let pair = line.split_once(' ');
|
||||
match pair {
|
||||
Some((pkg, "install ok installed")) => Some(pkg.to_string()),
|
||||
Some(_) => None,
|
||||
None => {
|
||||
elog("Warning: Dropped a None pair when cleaning up package list");
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let auto_packages: Vec<Package> = self.automatic()?;
|
||||
let mut manual_packages: Vec<Package> = vec![];
|
||||
|
||||
for package in all {
|
||||
let auto = Package::new_with_manual(&package, false);
|
||||
if !auto_packages.contains(&auto) {
|
||||
manual_packages.push(Package::new_with_manual(&package, true));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let auto_set: HashSet<String> = self
|
||||
.automatic()?
|
||||
.into_iter()
|
||||
.map(|package| package.name().to_owned())
|
||||
.collect();
|
||||
let manual_packages: Vec<Package> = all
|
||||
.into_iter()
|
||||
.filter(|name| !auto_set.contains(name))
|
||||
.map(|name| Package::new_with_manual(&name, true))
|
||||
.collect();
|
||||
|
||||
Ok(manual_packages)
|
||||
}
|
||||
|
||||
fn automatic(&self) -> Result<Vec<Package>, pkg::Error> {
|
||||
let path = "/var/lib/apt/extended_states";
|
||||
let Ok(extended_states) = read_to_string(path) else {
|
||||
return pkg::Error::send(
|
||||
&format!("Failed reading {path}"),
|
||||
pkg::ErrorKind::MetadataFileRead,
|
||||
);
|
||||
};
|
||||
let lines: Vec<String> = extended_states
|
||||
let extended_states = read_to_string(path)?;
|
||||
|
||||
let lines: Vec<&str> = extended_states
|
||||
.lines()
|
||||
.map(|s| s.to_string())
|
||||
.filter(|line| !line.is_empty())
|
||||
.collect();
|
||||
|
||||
let iterator = lines.chunks_exact(3);
|
||||
let remainder = iterator.remainder();
|
||||
if !remainder.is_empty() {
|
||||
let chunks = lines.chunks_exact(3);
|
||||
if !chunks.remainder().is_empty() {
|
||||
elog(&format!(
|
||||
"Warning: Reading package extended states left a remainder: {remainder:?}"
|
||||
"Warning: Package extended states read left a remainder: {:?}",
|
||||
chunks.remainder()
|
||||
));
|
||||
}
|
||||
|
||||
let mut packages: Vec<Package> = vec![];
|
||||
|
||||
for chunk in iterator {
|
||||
for chunk in chunks {
|
||||
if let Some(name_line) = chunk.first()
|
||||
&& let Some(auto_line) = chunk.get(2)
|
||||
{
|
||||
|
|
@ -143,8 +136,8 @@ impl Packages for Apt {
|
|||
Ok(packages)
|
||||
}
|
||||
|
||||
fn variant(&self) -> Result<PackagerVariant, pkg::Error> {
|
||||
Ok(self.variant.clone())
|
||||
fn variant(&self) -> &PackagerVariant {
|
||||
&self.variant
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -159,11 +152,11 @@ impl Apt {
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
let mut args = vec![subcommand];
|
||||
args.extend_from_slice(&packages.iter().map(|p| p.into()).collect::<Vec<&str>>());
|
||||
let args: Vec<&str> = iter::once(subcommand)
|
||||
.chain(packages.iter().map(|p| p.into()))
|
||||
.collect();
|
||||
|
||||
let command = Command::new("apt", &args).escalate(config)?;
|
||||
|
||||
Ok(crate::run::executor::spawn(&command)?)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ pub trait Packages: Clone + Default + Debug + PartialEq + Eq {
|
|||
fn uninstall(&self, packages: &[Package], config: &Configuration) -> Result<(), Error>;
|
||||
fn manual(&self) -> Result<Vec<Package>, Error>;
|
||||
fn automatic(&self) -> Result<Vec<Package>, Error>;
|
||||
fn variant(&self) -> Result<PackagerVariant, Error>;
|
||||
fn variant(&self) -> &PackagerVariant;
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Debug, PartialEq, Eq)]
|
||||
|
|
@ -51,12 +51,10 @@ impl Packages for Packager {
|
|||
}
|
||||
}
|
||||
|
||||
fn variant(&self) -> Result<PackagerVariant, Error> {
|
||||
fn variant(&self) -> &PackagerVariant {
|
||||
match self {
|
||||
Packager::Apt(p) => p.variant(),
|
||||
Packager::Unknown => Error::unknown_packager(
|
||||
"Can't determine the package manager's variant because it is unknown",
|
||||
),
|
||||
Packager::Unknown => &PackagerVariant::Unknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -76,8 +74,8 @@ pub struct Package {
|
|||
}
|
||||
|
||||
impl Package {
|
||||
pub fn name(&self) -> String {
|
||||
self.name.clone()
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn new_with_manual(name: &str, manual: bool) -> Package {
|
||||
|
|
@ -185,7 +183,7 @@ impl Error {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn send<T>(message: &str, kind: ErrorKind) -> Result<T, Error> {
|
||||
pub fn wrapped<T>(message: &str, kind: ErrorKind) -> Result<T, Error> {
|
||||
Err(Error::new(message, kind))
|
||||
}
|
||||
|
||||
|
|
@ -215,6 +213,15 @@ impl From<run::Error> for Error {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for Error {
|
||||
fn from(io_error: std::io::Error) -> Error {
|
||||
Error {
|
||||
message: format!("{:?}: {}", io_error.kind(), io_error),
|
||||
kind: ErrorKind::IO,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ErrorKind {
|
||||
NotFound,
|
||||
|
|
@ -222,4 +229,5 @@ pub enum ErrorKind {
|
|||
MetadataFileRead,
|
||||
RunError,
|
||||
ExecutorError,
|
||||
IO,
|
||||
}
|
||||
|
|
|
|||
40
src/run.rs
40
src/run.rs
|
|
@ -18,8 +18,8 @@ impl Order {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn tasks(&self) -> Vec<Task> {
|
||||
self.tasks.clone()
|
||||
pub const fn tasks(&self) -> &Vec<Task> {
|
||||
&self.tasks
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -29,43 +29,32 @@ pub struct Task {
|
|||
done: bool,
|
||||
argument: String,
|
||||
parameters: Vec<String>,
|
||||
configuration: Configuration,
|
||||
}
|
||||
|
||||
impl Task {
|
||||
fn new(
|
||||
kind: TaskKind,
|
||||
argument: &str,
|
||||
parameters: Vec<String>,
|
||||
configuration: &Configuration,
|
||||
) -> Task {
|
||||
fn new(kind: TaskKind, argument: &str, parameters: Vec<String>) -> Task {
|
||||
Task {
|
||||
kind,
|
||||
done: false,
|
||||
argument: String::from(argument),
|
||||
parameters,
|
||||
configuration: configuration.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn configuration(&self) -> Configuration {
|
||||
self.configuration.clone()
|
||||
pub fn argument(&self) -> &str {
|
||||
&self.argument
|
||||
}
|
||||
|
||||
pub fn argument(&self) -> String {
|
||||
self.argument.clone()
|
||||
}
|
||||
|
||||
pub fn parameters(&self) -> Vec<String> {
|
||||
self.parameters.clone()
|
||||
pub const fn parameters(&self) -> &Vec<String> {
|
||||
&self.parameters
|
||||
}
|
||||
|
||||
pub const fn done(&self) -> bool {
|
||||
self.done
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> TaskKind {
|
||||
self.kind.clone()
|
||||
pub const fn kind(&self) -> &TaskKind {
|
||||
&self.kind
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -89,7 +78,7 @@ pub struct Command {
|
|||
|
||||
impl Command {
|
||||
pub fn escalate(&self, config: &Configuration) -> Result<Command, Error> {
|
||||
let mut args = config.su_command.command().args;
|
||||
let mut args = config.su_command.command().clone().args;
|
||||
|
||||
if config.su_command.wraps() {
|
||||
let flattened_command = format!("{} {}", self.base, self.args.join(" "));
|
||||
|
|
@ -114,7 +103,7 @@ impl Command {
|
|||
}
|
||||
|
||||
Ok(Command {
|
||||
base: config.su_command.command().base,
|
||||
base: config.su_command.command().clone().base,
|
||||
args,
|
||||
})
|
||||
}
|
||||
|
|
@ -125,13 +114,6 @@ impl Command {
|
|||
args: args.iter().map(|e| e.to_string()).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_from_strings(base: &str, args: &[String]) -> Command {
|
||||
Command {
|
||||
base: base.to_string(),
|
||||
args: args.to_vec(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
|||
|
|
@ -1,11 +1,10 @@
|
|||
use crate::{
|
||||
conf::Configuration,
|
||||
log::elog,
|
||||
run::{Order, Task, TaskKind},
|
||||
};
|
||||
use std::{env, path::PathBuf};
|
||||
|
||||
pub fn parse(mut raw_args: env::Args, configuration: &Configuration) -> Order {
|
||||
pub fn parse(mut raw_args: env::Args) -> Order {
|
||||
let (argument, parameters): (String, Vec<String>) = if let Some(first) = raw_args.next() {
|
||||
if is_executable_path(&first) {
|
||||
elog("First argument is the executable path");
|
||||
|
|
@ -32,25 +31,20 @@ pub fn parse(mut raw_args: env::Args, configuration: &Configuration) -> Order {
|
|||
|
||||
let make_order = |kind: TaskKind| -> Order {
|
||||
Order {
|
||||
tasks: vec![Task::new(kind, &argument, parameters, configuration)],
|
||||
tasks: vec![Task::new(kind, &argument, parameters)],
|
||||
}
|
||||
};
|
||||
|
||||
elog(&format!("Command is {argument}"));
|
||||
if argument == "version" || argument == "-v" || argument == "--version" {
|
||||
make_order(TaskKind::Version)
|
||||
} else if argument == "help" || argument == "-h" || argument == "--help" {
|
||||
make_order(TaskKind::Help)
|
||||
} else if argument == "install" {
|
||||
make_order(TaskKind::PackageInstall)
|
||||
} else if argument == "uninstall" {
|
||||
make_order(TaskKind::PackageUninstall)
|
||||
} else if argument == "auto" {
|
||||
make_order(TaskKind::PackageListAuto)
|
||||
} else if argument == "manual" {
|
||||
make_order(TaskKind::PackageListManual)
|
||||
} else {
|
||||
make_order(TaskKind::Unrecognized)
|
||||
|
||||
match argument.as_str() {
|
||||
"version" | "-v" | "--version" => make_order(TaskKind::Version),
|
||||
"help" | "-h" | "--help" => make_order(TaskKind::Help),
|
||||
"install" => make_order(TaskKind::PackageInstall),
|
||||
"uninstall" => make_order(TaskKind::PackageUninstall),
|
||||
"auto" => make_order(TaskKind::PackageListAuto),
|
||||
"manual" => make_order(TaskKind::PackageListManual),
|
||||
_ => make_order(TaskKind::Unrecognized),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
30
src/state.rs
30
src/state.rs
|
|
@ -15,29 +15,29 @@ pub struct State {
|
|||
}
|
||||
|
||||
impl State {
|
||||
fn new(config: &Configuration, os: &OperatingSystem, orders: &[Order]) -> State {
|
||||
fn new(configuration: Configuration, os: OperatingSystem, orders: &[Order]) -> State {
|
||||
State {
|
||||
configuration: config.clone(),
|
||||
os: os.clone(),
|
||||
orders: orders.to_vec(),
|
||||
configuration,
|
||||
os,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn configuration(&self) -> Configuration {
|
||||
self.configuration.clone()
|
||||
pub const fn configuration(&self) -> &Configuration {
|
||||
&self.configuration
|
||||
}
|
||||
|
||||
pub fn os(&self) -> OperatingSystem {
|
||||
self.os.clone()
|
||||
pub const fn os(&self) -> &OperatingSystem {
|
||||
&self.os
|
||||
}
|
||||
|
||||
pub fn orders(&self) -> Vec<Order> {
|
||||
self.orders.clone()
|
||||
pub const fn orders(&self) -> &Vec<Order> {
|
||||
&self.orders
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup(config: &Configuration, orders: &[Order]) -> State {
|
||||
State::new(config, &detect_os(), orders)
|
||||
pub fn setup(config: Configuration, orders: &[Order]) -> State {
|
||||
State::new(config, detect_os(), orders)
|
||||
}
|
||||
|
||||
fn detect_os() -> OperatingSystem {
|
||||
|
|
@ -46,11 +46,11 @@ fn detect_os() -> OperatingSystem {
|
|||
if let Ok(os_release) = std::fs::read_to_string("/etc/os-release") {
|
||||
elog(&os_release);
|
||||
let mut map: HashMap<String, String> = HashMap::new();
|
||||
let lines: Vec<Option<(&str, &str)>> = os_release
|
||||
let lines = os_release
|
||||
.lines()
|
||||
.map(|line| line.split_once('='))
|
||||
.collect();
|
||||
for line in lines.into_iter().flatten() {
|
||||
.map(|line| line.split_once('='));
|
||||
|
||||
for line in lines.flatten() {
|
||||
let (key, value) = line;
|
||||
map.insert(key.to_string(), strip_quotes(value));
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue