From f35967c6e3021f7bd564e4a15f092f6984d4b3b9 Mon Sep 17 00:00:00 2001 From: neo_1993 Date: Wed, 9 Sep 2026 18:59:57 +0200 Subject: [PATCH] config reader start --- examples/config_reader.rs | 21 +++ libportage/config/mod.rs | 139 ++++++++++++++++ libportage/config/portage.rs | 314 +++++++++++++++++++++++++++++++++++ libportage/config/ruport.rs | 61 +++++++ libportage/features.rs | 230 +++++++++++++++++++++++++ libportage/lib.rs | 2 + libportage/util/logger.rs | 5 + src/emerge.rs | 108 ++++++++++++ src/main.rs | 47 +++++- 9 files changed, 925 insertions(+), 2 deletions(-) create mode 100644 examples/config_reader.rs create mode 100644 libportage/config/mod.rs create mode 100644 libportage/config/portage.rs create mode 100644 libportage/config/ruport.rs create mode 100644 libportage/features.rs create mode 100644 src/emerge.rs diff --git a/examples/config_reader.rs b/examples/config_reader.rs new file mode 100644 index 0000000..f627a80 --- /dev/null +++ b/examples/config_reader.rs @@ -0,0 +1,21 @@ +use std::io::Result; +use std::process::exit; + +use portage::util::logger::logger_init; +use portage::config::PortageConfig; + + +fn main() -> Result<()> { + match logger_init(log::LevelFilter::Debug) { + Ok(_) => {}, + Err(e) => { + eprintln!("{e}"); + exit(-1); + } + } + + let cfg = PortageConfig::new()?; + cfg.display_config(); + + Ok(()) +} \ No newline at end of file diff --git a/libportage/config/mod.rs b/libportage/config/mod.rs new file mode 100644 index 0000000..2fac6b3 --- /dev/null +++ b/libportage/config/mod.rs @@ -0,0 +1,139 @@ +use std::{fmt, path::{Path, PathBuf}}; + +mod ruport; +mod portage; + +use ruport::RuportConfigProvider; +use portage::PortageConfigProvider; + +use crate::features::Features; + +trait ConfigProvider { + fn new() -> Result where Self: Sized; + fn parse_global(&mut self) -> Result<(), std::io::Error>; + + // getter + fn get_elog(&self) -> ELog; + fn get_features(&self) -> Vec; + + // display functions + fn display_cflags(&self); +} + +#[derive(Debug)] +enum ConfigError { + PortageConfigNotFound, + RuportConfigNotFound, +} +impl fmt::Display for ConfigError { + fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { + todo!() + } +} + +// portage paths +pub(crate) struct PortagePaths { + distdir: PathBuf, + pkgdir: PathBuf, + rpmdir: PathBuf, + tmpdir: PathBuf, +} + +// elog classes +#[derive(Debug,Clone)] +enum ELogClasses { + Log, + Warn, + Error, +} +impl ELogClasses { + pub fn from(line: String) -> Vec { + let mut ret = Vec::new(); + + let line = line.trim_matches('"') + .split_whitespace() + .collect::>(); + + for v in line { + match v { + "log" => ret.push(ELogClasses::Log), + "warn" => ret.push(ELogClasses::Warn), + "error" => ret.push(ELogClasses::Error), + + _ => eprintln!("unknown ELog Class: {v}"), + } + } + + ret + } +} + +#[derive(Debug,Clone)] +pub(crate) struct ELog { + classes: Vec, + system: String, + mail_uri: String, + mail_subject: String, + mail_from: String, +} +impl ELog { + pub fn default() -> Self { + Self { + classes: vec![ELogClasses::Log, ELogClasses::Warn, ELogClasses::Error], + system: "save_summary:log,warn,error,qa echo".to_string(), + + mail_uri: "root".to_string(), + mail_subject: "[portage] ebuild log for {pkg} on {host}".to_string(), + mail_from: "portage@localhost".to_string(), + } + } +} + +/// This struct holds the current configuration. +pub struct PortageConfig { + provider: Box, + + elog: ELog, + features: Vec, +} +impl PortageConfig { + pub fn new() -> Result { + let mut p: Box = match PortageConfigProvider::new() { + Ok(prov) => Box::new(prov), + Err(_) => { + match RuportConfigProvider::new() { + Ok(prov) => Box::new(prov), + Err(_) => Box::new(RuportConfigProvider::default()), + } + } + }; + + match p.parse_global() { + Ok(r) => {}, + Err(e) => {}, + }; + + let elog = p.get_elog(); + let features = p.get_features(); + + Ok(Self { + provider: p, + elog: elog, + features: features, + }) + } + + pub fn display_config(&self) { + self.provider.display_cflags(); + + // display active features + println!("Active Features: {:?}", self.features); + + // print elog information + println!("ELOG: "); + println!("\tClasses: {:?}", self.elog.classes); + println!("\tSystem: {}", self.elog.system); + println!("\tMail Subject: {}", self.elog.mail_subject); + println!("\tMail From: ({}) {}", self.elog.mail_uri, self.elog.mail_from); + } +} \ No newline at end of file diff --git a/libportage/config/portage.rs b/libportage/config/portage.rs new file mode 100644 index 0000000..9b5ca13 --- /dev/null +++ b/libportage/config/portage.rs @@ -0,0 +1,314 @@ + +use std::{collections::HashMap, fs::File, io::{BufRead, BufReader, Error, ErrorKind}, path::{Path, PathBuf}}; + +use log::debug; + +use crate::features::Features; + +use super::ConfigProvider; +use super::ConfigError; +use super::PortagePaths; +use super::{ELog,ELogClasses}; + +/// The Portage config provider. +/// +/// It is the default configuration in "normal" gentoo system. Per default, +/// Ruport try to read this configuration first. +pub(crate) struct PortageConfigProvider{ + global_path: PathBuf, + compile_flags: HashMap, + features: Vec, + mirrors: Vec, + paths: PortagePaths, + elog: ELog, +} +impl PortageConfigProvider { + fn validate_data(&mut self, data: Vec<(String,String)>) -> std::io::Result<()> { + for (key,value) in data { + match key.as_str() { + "CFLAGS" => self.compile_flags.insert("CFLAGS".to_string(), value), + "CXXFLAGS" => self.compile_flags.insert("CXXFLAGS".to_string(), value), + "FFLAGS" => self.compile_flags.insert("FFLAGS".to_string(), value), + "FCFLAGS" => self.compile_flags.insert("FCFLAGS".to_string(), value), + "LDFLAGS" => self.compile_flags.insert("LDFLAGS".to_string(), value), + + "GENTOO_MIRRORS" => { + let mirrors: Vec<&str> = value.split_whitespace().collect(); + for mirror in mirrors { + self.mirrors.push(mirror.to_string()); + } + }, + + "FEATURES" => { + let features: Vec<&str> = value.split_whitespace().collect(); + for feature in features { + let f = Features::from(feature); + if f.is_some(){ + self.features.push(f.unwrap()); + } + } + }, + "DISTDIR" => { + let dir = value.trim_matches('"'); + self.paths.distdir = PathBuf::from(dir); + }, + "PKGDIR" => { + let dir = value.trim_matches('"'); + self.paths.pkgdir = PathBuf::from(dir); + }, + "RPMDIR" => { + let dir = value.trim_matches('"'); + self.paths.rpmdir = PathBuf::from(dir); + }, + "PORTAGE_TMPDIR" => { + let dir = value.trim_matches('"'); + self.paths.tmpdir = PathBuf::from(dir); + }, + + // ELog configuration + "PORTAGE_ELOG_CLASSES" => { + let classes = value.trim_matches('"') + .split_whitespace() + .collect::>(); + for class in classes { + let c = match class { + "log" => ELogClasses::Log, + "warn" => ELogClasses::Warn, + "error" => ELogClasses::Error, + }; + self.elog.classes.push(c); + } + }, + "PORTAGE_ELOG_SYSTEM" => self.elog.system = value, + "PORTAGE_ELOG_MAILURI" => self.elog.mail_uri = value, + "PORTAGE_ELOG_MAILSUBJECT" => { + let subject = value + .replace("\\${PACKAGE", "{pkg}") + .replace("\\${HOST}", "{host}"); + self.elog.mail_subject = subject; + }, + "PORTAGE_ELOG_MAILFROM" => self.elog.mail_from = value, + + &_ => todo!(), + }; + } + + Ok(()) + } +} + +impl ConfigProvider for PortageConfigProvider { + fn new() -> Result { + let global_path = Path::new("/usr/share/portage/config/make.globals"); + + if !global_path.exists() { + return Err(ConfigError::PortageConfigNotFound); + } + + log::debug!("using portage config"); + + Ok(Self{ + global_path: global_path.to_path_buf(), + compile_flags: HashMap::new(), + features: Vec::new(), + mirrors: Vec::new(), + paths: PortagePaths { + distdir: PathBuf::new(), + pkgdir: PathBuf::new(), + rpmdir: PathBuf::new(), + tmpdir: PathBuf::new(), + }, + elog: ELog::default(), + }) + } + + fn parse_global(&mut self) -> Result<(), std::io::Error> { + let file = File::open(self.global_path.clone())?; + let reader = BufReader::new(file); + + let mut data: Vec<(String, String)> = Vec::new(); + + let mut current_key: Option = None; + let mut current_value = String::new(); + + for line in reader.lines() { + let line = line?; + let line = line.trim(); + + // ignore empty line or comments + if line.is_empty() || line.starts_with('#') { + continue; + } + + // multiple line + if current_key.is_some() { + current_value.push(' '); + current_value.push_str(&line.to_string()); + } + + if let Some(index) = line.find('=') { + let key = line[..index].trim().to_string(); + let value = line[index+1..].trim(); + + if value.starts_with('"') && !value.ends_with('"') { + current_key = Some(key); + current_value = value.to_string(); + } else { + data.push((key, current_value.to_string())); + current_value.clear(); + } + } + } + + self.validate_data(data)?; + + Ok(()) + } + + // fn eparse_global(&mut self) -> std::io::Result<()> { + // let file = File::open(self.global_path.clone())?; + // let reader = BufReader::new(file); + + // let mut data: HashMap = HashMap::new(); + + // let mut current_key: Option = None; + // let mut current_value = String::new(); + + // for line in reader.lines() { + // let line = line.unwrap(); + // let line = line.trim(); + + // // ignore empty line or comments + // if line.is_empty() || line.starts_with('#') { + // continue; + // } + + // // multiline option + // if current_key.is_some() { + // current_value.push(' '); + // current_value.push_str(line); + + // if line.ends_with('"') { + // data.insert( + // current_key.take().unwrap(), + // current_value.clone(), + // ); + // current_value.clear(); + // } + + // continue; + // } + + // if let Some(index) = line.find('=') { + // let key = line[..index].trim().to_string(); + // let value = line[index+1..].trim(); + + // if value.starts_with('"') && ! value.ends_with('"') { + // current_key = Some(key); + // current_value = value.to_string(); + // } else { + // if current_value.contains("${*}") { + + // } + // data.insert(key, value.to_string()); + // } + + // // data.insert( + // // key.to_string(), + // // value.to_string() + // // ); + // } + // } + + // // now get the values from the buffer + // let cflags = data.get("CFLAGS"); + // if cflags.is_some() { + // self.compile_flags.insert("CFLAGS".to_string(), cflags.unwrap().to_string()); + // } + // let cxxflags = data.get("CXXFLAGS"); + // if cxxflags.is_some() { + // self.compile_flags.insert("CXXFLAGS".to_string(), cxxflags.unwrap().to_string()); + // } + // let ldflags = data.get("LDFLAGS"); + // if ldflags.is_some() { + // self.compile_flags.insert("LDFLAGS".to_string(), ldflags.unwrap().to_string()); + // } + // let fflags = data.get("FFLAGS"); + // if fflags.is_some() { + // self.compile_flags.insert("FFLAGS".to_string(), fflags.unwrap().to_string()); + // } + // let fcflags = data.get("FCFLAGS"); + // if fcflags.is_some() { + // self.compile_flags.insert("FCFLAGS".to_string(), fcflags.unwrap().to_string()); + // } + // let mirrors = data.get("GENTOO_MIRRORS"); + // let mirrors: Vec<&str> = mirrors.unwrap().split_whitespace().collect(); + // for mirror in mirrors { + // self.mirrors.push(mirror.to_string()); + // } + + // let features = data.get("FEATURES"); + // if features.is_some() { + // self.features = Features::from_line(features.unwrap().to_string()) + // } + + // let distdir = data.get("DISTDIR"); + // let pkgdir = data.get("PKGDIR"); + // let rpmdir = data.get("RPMDIR"); + // let tmpdir = data.get("PORTAGE_TMPDIR"); + // if distdir.is_some() { + // self.paths.distdir = PathBuf::from(distdir.unwrap().to_string()); + // } + // if pkgdir.is_some() { + // self.paths.pkgdir = PathBuf::from(pkgdir.unwrap().to_string()); + // } + // if rpmdir.is_some() { + // self.paths.rpmdir = PathBuf::from(rpmdir.unwrap().to_string()); + // } + // if tmpdir.is_some() { + // self.paths.tmpdir = PathBuf::from(tmpdir.unwrap().to_string()) + // } + + // // elog settings + // let elog_classes = data.get("PORTAGE_ELOG_CLASSES"); + // let elog_system = data.get("PORTAGE_ELOG_SYSTEM"); + // let elog_mailuri = data.get("PORTAGE_ELOG_MAILURI"); + // let elog_mailsubject = data.get("PORTAGE_ELOG_MAILSUBJECT"); + // let elog_mailfrom = data.get("PORTAGE_ELOG_MAILFROM"); + // if elog_classes.is_some() { + // self.elog.classes = ELogClasses::from(elog_classes.unwrap().to_string()); + // } + // if elog_system.is_some() { + // self.elog.system = elog_system.unwrap().to_string(); + // } + // if elog_mailuri.is_some() { + // self.elog.mail_uri = elog_mailuri.unwrap().to_string(); + // } + // if elog_mailsubject.is_some() { + // let subject = elog_mailsubject.unwrap().to_string(); + // let _ = subject.replace("\\${PACKAGE}", "{pkg}") + // .replace("\\${HOST}", "{host}"); + // self.elog.mail_subject = subject; + // } + // if elog_mailfrom.is_some() { + // self.elog.mail_from = elog_mailfrom.unwrap().to_string(); + // } + + // Ok(()) + // } + + fn get_elog(&self) -> ELog { + self.elog.clone() + } + fn get_features(&self) -> Vec { + self.features.clone() + } + + fn display_cflags(&self) { + println!("Compiler Flags:"); + for (k,v) in self.compile_flags.clone() { + let v = v.trim_matches('"'); + println!("\t{k} = {v}"); + } + } +} \ No newline at end of file diff --git a/libportage/config/ruport.rs b/libportage/config/ruport.rs new file mode 100644 index 0000000..d79de07 --- /dev/null +++ b/libportage/config/ruport.rs @@ -0,0 +1,61 @@ + +use std::path::Path; +use std::path::PathBuf; + +use crate::features::Features; + +use super::ConfigProvider; +use super::ConfigError; +use super::{ELog,ELogClasses}; + +/// The Ruport config provider. +/// +/// It is used, then there is no current portage configuration available. +/// It is also capable of, creating default configs, useful for embedded +/// systems. +pub(crate) struct RuportConfigProvider{ + global_path: PathBuf, + elog: ELog, + features: Vec, +} + +impl RuportConfigProvider { + pub fn default() -> Self { + let elog = ELog::default(); + + Self { + global_path: PathBuf::from("/usr/share/ruport/config.ini"), + elog, + features: Vec::new(), + } + } +} +impl ConfigProvider for RuportConfigProvider { + fn new() -> Result { + let global_path = Path::new("/usr/share/ruport/config.ini"); + if !global_path.exists() { + return Err(ConfigError::RuportConfigNotFound); + } + + log::debug!("using ruport config."); + Ok(Self{ + global_path: global_path.to_path_buf(), + elog: ELog::default(), + features: Vec::new(), + }) + } + fn parse_global(&mut self) -> std::io::Result<()> { + Ok(()) + } + + fn get_elog(&self) -> ELog { + self.elog.clone() + } + fn get_features(&self) -> Vec { + self.features.clone() + } + + fn display_cflags(&self) { + + } +} \ No newline at end of file diff --git a/libportage/features.rs b/libportage/features.rs new file mode 100644 index 0000000..5791a7c --- /dev/null +++ b/libportage/features.rs @@ -0,0 +1,230 @@ + +#[derive(Debug,Clone,Copy)] +pub enum Features { + AssumeDigests, + BinPkgDoCompress, + BinPkgDoStrip, + BinPkgIgnoreSignature, + BinPkgLogs, + BinPkgMultiInstance, + BinPkgRequestSignature, + BinPkgSigning, + BuildPkg, + BuildPkgLive, + BuildPkgProactive, + BuildSysPkg, + Candy, + CaseInsensistiveFs, + Ccache, + Chflags, + CleanLogs, + CollisionProtect, + CompressBuildLogs, + CompressDebug, + CompressIndex, + ConfigProtectIfModified, + DeDupDebug, + Digest, + DistCC, + DistLocks, + DowngradeBackup, + EbuildLocks, + FailClean, + FakeRoot, + FixLaFiles, + ForceMirror, + GetBinPkg, + GpgKeepAlive, + HomeDirTemplateCopy, + IceCream, + InstallSources, + IpcSandbox, + JobServerToken, + KeepTemp, + KeepWork, + LMirror, + MergeSync, + MergeWait, + MetadataTransfer, + Mirror, + MountSandbox, + MultilibStrict, + NetworkSandbox, + NetworkSandboxProxy, + News, + NoAuto, + NoClean, + NoDoc, + NoInfo, + NoMan, + NoStrip, + NoTitles, + PackDebug, + ParallelFetch, + ParallelInstall, + PidSandbox, + PkgdirIndexTrusted, + PrelinkChecksums, + PreserveLibs, + ProtectOwned, + PythonTrace, + QAUnresolvedSonameDeps, + Sandbox, + SeLinux, + SeSandbox, + SFPerms, + Sign, + SkipRoCheck, + SplitDebug, + SplitElog, + SplitLog, + Strict, + StrictKeepDir, + Stricter, + Suidctl, + Test, + TestFailContinue, + UnknownFeaturesFilter, + UnknownFeaturesWarn, + UnmergeBackup, + UnmergeLogs, + UnmergeOrphans, + Unprivileged, + UserFetch, + UserPriv, + UserSandbox, + UserSync, + WarnOnLargeEnv, + WebRsyncGpg, + XATTR, +} + +impl Features { + pub fn from(val: &str) -> Option { + log::debug!("features val line: {val}"); + + match val { + "assume-digests" => Some(Self::AssumeDigests), + + "binpkg-docompress" => Some(Self::BinPkgDoCompress), + "binpkg-dostrip" => Some(Self::BinPkgDoStrip), + "binpkg-ignore-signature" => Some(Self::BinPkgIgnoreSignature), + "binpkg-logs" => Some(Self::BinPkgLogs), + "binpkg-multi-instance" => Some(Self::BinPkgMultiInstance), + "binpkg-request-signature" => Some(Self::BinPkgRequestSignature), + "binpkg-signing" => Some(Self::BinPkgSigning), + + "buildpkg" => Some(Self::BuildPkg), + "buildpkg-live" => Some(Self::BuildPkgLive), + "buildpkg-proactive" => Some(Self::BuildPkgProactive), + "buildsyspkg" => Some(Self::BuildSysPkg), + + "candy" => Some(Self::Candy), + "case-insensistive-fs" => Some(Self::CaseInsensistiveFs), + "ccache" => Some(Self::Ccache), + "chflags" => Some(Self::Chflags), + "clean-logs" => Some(Self::CleanLogs), + "collision-protect" => Some(Self::CollisionProtect), + "compress-build-logs" => Some(Self::CompressBuildLogs), + "compressdebug" => Some(Self::CompressDebug), + "compress-index" => Some(Self::CompressIndex), + "config-protect-if-modified" => Some(Self::ConfigProtectIfModified), + "dedupdebug" => Some(Self::DeDupDebug), + "digest" => Some(Self::Digest), + "distcc" => Some(Self::DistCC), + "distlocks" => Some(Self::DistLocks), + "downgrade-backup" => Some(Self::DowngradeBackup), + "ebuild-locks" => Some(Self::EbuildLocks), + "fail-clean" => Some(Self::FailClean), + "fakeroot" => Some(Self::FakeRoot), + "fixlafiles" => Some(Self::FixLaFiles), + "force-mirror" => Some(Self::ForceMirror), + "getbinpkg" => Some(Self::GetBinPkg), + "gpg-keepalive" => Some(Self::GpgKeepAlive), + "home-dir-template-copy" => Some(Self::HomeDirTemplateCopy), + "icecream" => Some(Self::IceCream), + "installsources" => Some(Self::InstallSources), + "ipc-sandbox" => Some(Self::IpcSandbox), + "jobserver-token" => Some(Self::JobServerToken), + "keeptemp" => Some(Self::KeepTemp), + "keepwork" => Some(Self::KeepWork), + "lmirror" => Some(Self::LMirror), + "merge-sync" => Some(Self::MergeSync), + "merge-wait" => Some(Self::MergeWait), + "metadata-transfer" => Some(Self::MetadataTransfer), + "mirror" => Some(Self::Mirror), + "mount-sandbox" => Some(Self::MountSandbox), + "multilib-strict" => Some(Self::MultilibStrict), + "network-sandbox" => Some(Self::NetworkSandbox), + "network-sandbox-proxy" => Some(Self::NetworkSandboxProxy), + "news" => Some(Self::News), + + "noauto" => Some(Self::NoAuto), + "noclean" => Some(Self::NoClean), + "nodoc" => Some(Self::NoDoc), + "noinfo" => Some(Self::NoInfo), + "noman" => Some(Self::NoMan), + "nostrip" => Some(Self::NoStrip), + "notitles" => Some(Self::NoTitles), + + "packdebug" => Some(Self::PackDebug), + "parallel-fetch" => Some(Self::ParallelFetch), + "parallel-install" => Some(Self::ParallelInstall), + "pid-sandbox" => Some(Self::PidSandbox), + "pkgdir-index-trusted" => Some(Self::PkgdirIndexTrusted), + "prelink-checksums" => Some(Self::PrelinkChecksums), + "preserve-libs" => Some(Self::PreserveLibs), + "protect-owned" => Some(Self::ProtectOwned), + "python-trace" => Some(Self::PythonTrace), + "qa-unresolved-soname-deps" => Some(Self::QAUnresolvedSonameDeps), + "sandbox" => Some(Self::Sandbox), + "selinux" => Some(Self::SeLinux), + "sesandbox" => Some(Self::SeSandbox), + "sfperms" => Some(Self::SFPerms), + "sign" => Some(Self::Sign), + "skiprocheck" => Some(Self::SkipRoCheck), + "splitdebug" => Some(Self::SplitDebug), + "split-elog" => Some(Self::SplitElog), + "split-log" => Some(Self::SplitLog), + + "strict" => Some(Self::Strict), + "strict-keepdir" => Some(Self::StrictKeepDir), + "stricter" => Some(Self::Stricter), + "suidctl" => Some(Self::Suidctl), + + "test" => Some(Self::Test), + "test-fail-continue" => Some(Self::TestFailContinue), + + "unknown-features-filter" => Some(Self::UnknownFeaturesFilter), + "unknown-features-warn" => Some(Self::UnknownFeaturesWarn), + + "unmerge-backup" => Some(Self::UnmergeBackup), + "unmerge-logs" => Some(Self::UnmergeLogs), + "unmerge-orphans" => Some(Self::UnmergeOrphans), + + "unprivileged" => Some(Self::Unprivileged), + + "userfetch" => Some(Self::UserFetch), + "userpriv" => Some(Self::UserPriv), + "usersandbox" => Some(Self::UserSandbox), + "usersync" => Some(Self::UserSync), + + "warn-on-large-env" => Some(Self::WarnOnLargeEnv), + "webrsync-gpg" => Some(Self::WebRsyncGpg), + "xattr" => Some(Self::XATTR), + + _ => None, + } + } + pub fn from_line(line: String) -> Vec { + let mut ret = Vec::new(); + for v in line.split_whitespace() { + let f = Self::from(v); + if f.is_some() { + ret.push(f.unwrap()) + } + } + + ret + } +} \ No newline at end of file diff --git a/libportage/lib.rs b/libportage/lib.rs index 487ae74..6acaa52 100644 --- a/libportage/lib.rs +++ b/libportage/lib.rs @@ -6,6 +6,8 @@ use crate::repository::RepoConfig; pub mod config; pub mod util; pub mod repository; +pub(crate) mod features; + #[derive(Debug,Clone,ValueEnum)] pub enum Action { diff --git a/libportage/util/logger.rs b/libportage/util/logger.rs index 3475493..ebad8de 100644 --- a/libportage/util/logger.rs +++ b/libportage/util/logger.rs @@ -3,6 +3,11 @@ use log::{Record, Level, Metadata, SetLoggerError, LevelFilter}; struct SimpleLogger; impl log::Log for SimpleLogger { + #[cfg(debug_assertions)] + fn enabled(&self, metadata: &Metadata) -> bool { + metadata.level() <= Level::Debug + } + #[cfg(not(debug_assertions))] fn enabled(&self, metadata: &Metadata) -> bool { metadata.level() <= Level::Info } diff --git a/src/emerge.rs b/src/emerge.rs new file mode 100644 index 0000000..799f6a4 --- /dev/null +++ b/src/emerge.rs @@ -0,0 +1,108 @@ +use clap::{CommandFactory, Parser}; + +use signal_hook::{consts::SIGINT, iterator::Signals}; +use std::{collections::HashMap, error::Error, path::PathBuf, thread}; + +use portage::Action; + +#[derive(Parser)] +#[command( + name = "emerge", + about = "command-line interface to the Portage system" +)] +struct Cli { + /// Pretend mode + #[arg(short = 'p', long)] + pub pretend: bool, + + /// Ask before executing + #[arg(short = 'a', long)] + pub ask: bool, + + /// Update package + #[arg(short = 'u',long,action)] + pub update: bool, + + /// Clean unused packages + #[arg(short='c',long,action)] + pub depclean: bool, + + /// Synchronize repositories + #[arg(long,action)] + pub sync: bool, + + /// search for matches of the supplied string in the ebuild repository. + #[arg(short = 's',long,action)] + pub search: bool, + + /// Number of jobs + #[arg(short = 'j', long)] + pub jobs: Option, + + /// Set the PORTAGE_CONFIGROOT environment variable + #[arg(long,default_value="/")] + pub config_root: PathBuf, + + /// Set the ROOT environment variable + #[arg(long)] + pub root: PathBuf, + + /// Set the SYSROOT environment variable + #[arg(long)] + pub sysroot: PathBuf, + + /// Set the EPREFIX environment variable + #[arg(long)] + pub prefix: PathBuf, + + /// Package or sets + #[arg()] + pub targets: Vec, +} + +impl Cli { + pub fn get_action(&self) -> Action { + if self.sync { + Action::Sync + } else if self.depclean { + Action::Depclean + } else if self.update { + Action::Update + } else if self.search { + Action::Search(self.targets.clone()) + } else { + Action::Install(self.targets.clone()) + } + } + + pub fn get_paths(&self) -> HashMap<&str, PathBuf> { + let mut paths = HashMap::new(); + + paths.insert("PORTAGE_CONFIGROOT", self.config_root.clone()); + paths.insert("SYSROOT", self.sysroot.clone()); + paths.insert("ROOT", self.root.clone()); + paths.insert("EPREFIX", self.prefix.clone()); + + paths + } +} + +pub fn main() -> Result<(), Box> { + if std::env::args_os().len() == 1 { + Cli::command().print_help()?; + return Ok(()); + } + + let cli = Cli::parse(); + let mut signals = Signals::new([SIGINT])?; + + thread::spawn(move || { + for sig in signals.forever() { + println!("Received signal {:?}", sig); + } + }); + + portage::perform_action(cli.get_action(), cli.get_paths())?; + + Ok(()) +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index e7a11a9..7fbd0af 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,46 @@ -fn main() { - println!("Hello, world!"); +use std::io::Result; +use clap::{Args, CommandFactory, Parser}; +use portage::{Action, PortageContext}; +use portage::util::logger::logger_init; + +#[derive(Parser)] +struct Cli { + /// ask before doing anything + #[arg(short='a', long)] + pub ask: bool, + /// be verbose on the output + #[arg(short='v', long)] + pub verbose: bool, + /// show debug messages + #[arg(short='d', long, default_value="false")] + pub debug: bool, + + /// action to perform + #[arg(value_enum)] + pub action: Action, + /// package atoms/names to perform actions against + #[arg()] + pub targets: Vec, +} + + +fn main() -> std::io::Result<()>{ + if std::env::args_os().len() == 1 { + Cli::command().print_help().unwrap(); + println!(); + return Ok(()); + } + let _cli = Cli::parse(); + + if _cli.debug { + logger_init(log::LevelFilter::Debug) + .expect("unable to setup logger"); + } else { + logger_init(log::LevelFilter::Info) + .expect("unable to setup logger"); + } + + let _ctx = PortageContext::new()?; + + Ok(()) }