Skip to content

Commit

Permalink
Have a config file for gng_db
Browse files Browse the repository at this point in the history
  • Loading branch information
hunger committed May 23, 2021
1 parent d2c30b7 commit 6da8908
Show file tree
Hide file tree
Showing 3 changed files with 201 additions and 12 deletions.
17 changes: 5 additions & 12 deletions gng-build/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,8 @@ struct Args {
db_dir: PathBuf,

/// the repository configuration directory
#[clap(
long,
parse(from_os_str),
env = "GNG_REPO_CONFIG_DIR",
value_name = "DIR",
default_value = "/etc/gng/repositories"
)]
repository_config_dir: PathBuf,
#[clap(long, parse(from_os_str), env = "GNG_CONFIG_FILE", value_name = "FILE")]
config_file: Option<PathBuf>,

/// the repository to use
#[clap(long, value_name = "REPO")]
Expand Down Expand Up @@ -110,10 +104,9 @@ fn main() -> Result<()> {
.wrap_err("Failed to get current work directory.")?
.join(args.pkgsrc_dir);

let repo_db = gng_db::RepositoryDb::open(&args.repository_config_dir).wrap_err(format!(
"Failed to read repositories in {}.",
&args.repository_config_dir.display()
))?;
let config = gng_db::Config::new(&args.config_file)?;

let repo_db = config.repository_db()?;

let repo = match &args.repository {
Some(rin) => repo_db.resolve_repository(rin),
Expand Down
189 changes: 189 additions & 0 deletions gng_db/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
// SPDX-License-Identifier: GPL-3.0-or-later
// Copyright (C) 2020 Tobias Hunger <tobias.hunger@gmail.com>

//! A object representing a `Repository`

use crate::{Error, Result};

// ----------------------------------------------------------------------
// - Constants:
// ----------------------------------------------------------------------

const REPOSITORY_DIR_KEY: &str = "repository_dir";
const REMOTES_DIR_KEY: &str = "remotes_dir";
const LOCALS_DIR_KEY: &str = "locals_dir";
const PACKET_DB_DIR_KEY: &str = "packet_db_dir";

// ----------------------------------------------------------------------
// - Helpers:
// ----------------------------------------------------------------------

fn env_path(env_var: &str, default_prefix: &str, fallback: &str) -> std::path::PathBuf {
std::env::var(env_var).map_or_else(
|_| {
std::env::var("HOME").map_or_else(
|_| std::path::PathBuf::from(fallback),
|p| std::path::PathBuf::from(p).join(default_prefix),
)
},
std::path::PathBuf::from,
)
}

fn config_path() -> std::path::PathBuf {
env_path("XDG_CONFIG_HOME", ".config", "/etc")
}

fn cache_path() -> std::path::PathBuf {
env_path("XDG_CACHE_HOME", ".cache", "/var/cache")
}

fn data_path() -> std::path::PathBuf {
env_path("XDG_DATA_HOME", ".local/share", "/var/lib")
}

// Fill in default config file path if possible
fn default_config_file_path(
config_file: &Option<std::path::PathBuf>,
) -> Option<std::path::PathBuf> {
if config_file.is_some() {
config_file.clone()
} else {
let config_file_path = config_path().join("gng/config");
if config_file_path.exists() {
Some(config_file_path)
} else {
None
}
}
}

fn create_config_file_parser(
config_file: &Option<std::path::PathBuf>,
) -> Result<justconfig::Config> {
let mut conf = justconfig::Config::default();

// Open the configuration file
if let Some(config_file) = &config_file {
let file = std::fs::File::open(config_file)?;
conf.add_source(
justconfig::sources::text::ConfigText::new(
file,
config_file.to_string_lossy().as_ref(),
)
.map_err(|e| Error::Config(e.to_string()))?,
);
};

// Allow some environment variables to override configuration values read
// from the configuration file.
let config_env = justconfig::sources::env::Env::new(&[
(
justconfig::ConfPath::from(&[REPOSITORY_DIR_KEY]),
std::ffi::OsStr::new("GNG_REPOSITORY_DIR"),
),
(
justconfig::ConfPath::from(&[REMOTES_DIR_KEY]),
std::ffi::OsStr::new("GNG_REMOTES_DIR"),
),
(
justconfig::ConfPath::from(&[LOCALS_DIR_KEY]),
std::ffi::OsStr::new("GNG_LOCALS_DIR"),
),
(
justconfig::ConfPath::from(&[PACKET_DB_DIR_KEY]),
std::ffi::OsStr::new("GNG_PACKET_DB_DIR"),
),
]);
conf.add_source(config_env);

// Defaults:
let mut defaults = justconfig::sources::defaults::Defaults::default();
defaults.set(
conf.root().push_all(&[REPOSITORY_DIR_KEY]),
config_path()
.join("gng/repositories")
.to_string_lossy()
.as_ref(),
"default",
);
defaults.set(
conf.root().push_all(&[REMOTES_DIR_KEY]),
cache_path().join("gng/remotes").to_string_lossy().as_ref(),
"default",
);
defaults.set(
conf.root().push_all(&[LOCALS_DIR_KEY]),
data_path().join("gng/locals").to_string_lossy().as_ref(),
"default",
);
defaults.set(
conf.root().push_all(&[PACKET_DB_DIR_KEY]),
cache_path().join("gng/packets").to_string_lossy().as_ref(),
"default",
);
conf.add_source(defaults);

Ok(conf)
}

// ----------------------------------------------------------------------
// - Config:
// ----------------------------------------------------------------------

/// A Configuration object that can be read from a configuration file.
#[derive(Debug)]
pub struct Config {
/// The directory holding repository definition files
pub repository_dir: std::path::PathBuf,
/// The directory holding cached data from remote repositories
pub remotes_dir: std::path::PathBuf,
/// The directory containing all data from local repositories
pub locals_dir: std::path::PathBuf,
/// The directory holding the packet DB
pub packet_db_directory: std::path::PathBuf,
}

impl Config {
/// Parse a config file and create a `Config` object from it.
///
/// # Errors
/// May return an `Error::Config` if opening the `config_file` or parsing the necessary values fails.
#[tracing::instrument(level = "debug")]
pub fn new(config_file: &Option<std::path::PathBuf>) -> Result<Self> {
use justconfig::item::ValueExtractor;

// Fill in default config file
let config_file = default_config_file_path(config_file);

let conf = create_config_file_parser(&config_file)?;

Ok(Self {
repository_dir: conf
.get(conf.root().push(REPOSITORY_DIR_KEY))
.value()
.map_err(|e| Error::Config(e.to_string()))?,
remotes_dir: conf
.get(conf.root().push(REMOTES_DIR_KEY))
.value()
.map_err(|e| Error::Config(e.to_string()))?,
locals_dir: conf
.get(conf.root().push(LOCALS_DIR_KEY))
.value()
.map_err(|e| Error::Config(e.to_string()))?,
packet_db_directory: conf
.get(conf.root().push(PACKET_DB_DIR_KEY))
.value()
.map_err(|e| Error::Config(e.to_string()))?,
})
}

/// Open the `RepositoryDB` pointed to by this `Config`
///
/// # Errors
/// May return a `Error::Repository` if opening the Repository DB failed.
#[tracing::instrument(level = "debug")]
pub fn repository_db(&self) -> Result<crate::repository_db::RepositoryDb> {
crate::repository_db::RepositoryDb::open(&self.repository_dir)
}
}
7 changes: 7 additions & 0 deletions gng_db/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@
/// Repository related Errors
#[derive(thiserror::Error, Debug)]
pub enum Error {
/// A generic `Error` about the `RepositoryDb`.
#[error("Configuration error: {}", .0)]
Config(String),

/// A generic `Error` about the `RepositoryDb`.
#[error("General repository DB error: {}", .0)]
Db(String),
Expand Down Expand Up @@ -52,6 +56,7 @@ pub type Result<T> = std::result::Result<T, Error>;
// - Modules:
// ----------------------------------------------------------------------

pub mod config;
pub mod packet_db;
pub mod repository_db;

Expand All @@ -61,6 +66,8 @@ pub mod repository_db;

pub use uuid::Uuid; // Reexport Uuid from uuid crate!

pub use config::Config;
// FIXME: Are the following needed?
pub use packet_db::PacketDb;
pub use repository_db::RepositoryDb;

Expand Down

0 comments on commit 6da8908

Please sign in to comment.