feat(config): add configuration management for Yarr TUI app
This commit is contained in:
165
README.md
Normal file
165
README.md
Normal file
@@ -0,0 +1,165 @@
|
||||
# Yarr
|
||||
|
||||
A Terminal User Interface (TUI) for managing Sonarr.
|
||||
|
||||
## Features
|
||||
|
||||
- View system status and health
|
||||
- Browse series and episodes
|
||||
- Monitor download queue
|
||||
- View download history
|
||||
- Interactive TUI interface
|
||||
- Configurable via config files, environment variables, or CLI arguments
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
cargo install --path .
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Yarr supports multiple configuration methods with the following priority order (highest to lowest):
|
||||
|
||||
1. Command line arguments
|
||||
2. Environment variables
|
||||
3. Configuration file
|
||||
4. Default values
|
||||
|
||||
### Configuration File
|
||||
|
||||
Create a configuration file in one of these locations:
|
||||
|
||||
- `./yarr.toml` (current directory)
|
||||
- `~/.config/yarr/config.toml` (user config directory)
|
||||
|
||||
Example configuration:
|
||||
|
||||
```toml
|
||||
[sonarr]
|
||||
url = "http://localhost:8989"
|
||||
api_key = "your-api-key-here"
|
||||
```
|
||||
|
||||
### Environment Variables
|
||||
|
||||
Set these environment variables:
|
||||
|
||||
```bash
|
||||
export YARR_SONARR_URL="http://localhost:8989"
|
||||
export YARR_SONARR_API_KEY="your-api-key-here"
|
||||
```
|
||||
|
||||
### Command Line Arguments
|
||||
|
||||
```bash
|
||||
yarr --sonarr-url="http://localhost:8989" --sonarr-api-key="your-api-key"
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### TUI Mode (Default)
|
||||
|
||||
Launch the interactive TUI:
|
||||
|
||||
```bash
|
||||
yarr
|
||||
# or explicitly
|
||||
yarr tui
|
||||
```
|
||||
|
||||
### Command Line Mode
|
||||
|
||||
List all series:
|
||||
|
||||
```bash
|
||||
yarr list
|
||||
```
|
||||
|
||||
List only monitored series:
|
||||
|
||||
```bash
|
||||
yarr list --monitored
|
||||
```
|
||||
|
||||
Add a new series:
|
||||
|
||||
```bash
|
||||
yarr add --name "Series Name"
|
||||
```
|
||||
|
||||
### Configuration Management
|
||||
|
||||
Create a sample config file:
|
||||
|
||||
```bash
|
||||
yarr config init
|
||||
```
|
||||
|
||||
Create config file at specific location:
|
||||
|
||||
```bash
|
||||
yarr config init --path /path/to/config.toml
|
||||
```
|
||||
|
||||
Show current configuration:
|
||||
|
||||
```bash
|
||||
yarr config show
|
||||
```
|
||||
|
||||
Show configuration file search paths:
|
||||
|
||||
```bash
|
||||
yarr config paths
|
||||
```
|
||||
|
||||
### Shell Completions
|
||||
|
||||
Generate shell completions:
|
||||
|
||||
```bash
|
||||
# Bash
|
||||
yarr completions bash > /etc/bash_completion.d/yarr
|
||||
|
||||
# Zsh
|
||||
yarr completions zsh > ~/.zfunc/_yarr
|
||||
|
||||
# Fish
|
||||
yarr completions fish > ~/.config/fish/completions/yarr.fish
|
||||
|
||||
# PowerShell
|
||||
yarr completions powershell > yarr.ps1
|
||||
```
|
||||
|
||||
## TUI Controls
|
||||
|
||||
- `q` - Quit
|
||||
- `↑/↓` or `j/k` - Navigate up/down
|
||||
- `Enter` - Select/expand
|
||||
- `Tab` - Switch between panels
|
||||
- `r` - Refresh data
|
||||
|
||||
## Getting Started
|
||||
|
||||
1. Install yarr
|
||||
2. Create a configuration file:
|
||||
```bash
|
||||
yarr config init
|
||||
```
|
||||
3. Edit the configuration file to set your Sonarr URL and API key
|
||||
4. Launch the TUI:
|
||||
```bash
|
||||
yarr
|
||||
```
|
||||
|
||||
## Finding Your Sonarr API Key
|
||||
|
||||
1. Open your Sonarr web interface
|
||||
2. Go to Settings > General
|
||||
3. Find the "Security" section
|
||||
4. Copy the "API Key" value
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
186
src/config.rs
Normal file
186
src/config.rs
Normal file
@@ -0,0 +1,186 @@
|
||||
use config::{Config, ConfigError, Environment, File};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AppConfig {
|
||||
pub sonarr: SonarrConfig,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct SonarrConfig {
|
||||
pub url: String,
|
||||
pub api_key: String,
|
||||
}
|
||||
|
||||
impl Default for AppConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
sonarr: SonarrConfig {
|
||||
url: "http://localhost:8989".to_string(),
|
||||
api_key: String::new(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AppConfig {
|
||||
/// Load configuration from various sources with the following priority:
|
||||
/// 1. Command line arguments (highest priority)
|
||||
/// 2. Environment variables
|
||||
/// 3. Config file
|
||||
/// 4. Default values (lowest priority)
|
||||
pub fn load(
|
||||
config_path: Option<PathBuf>,
|
||||
sonarr_url: Option<String>,
|
||||
sonarr_api_key: Option<String>,
|
||||
) -> Result<Self, ConfigError> {
|
||||
let mut builder = Config::builder();
|
||||
|
||||
// Start with default config
|
||||
builder = builder.add_source(Config::try_from(&AppConfig::default())?);
|
||||
|
||||
// Add config file if it exists
|
||||
if let Some(path) = config_path {
|
||||
if path.exists() {
|
||||
builder = builder.add_source(File::from(path));
|
||||
}
|
||||
} else {
|
||||
// Try to load from default locations
|
||||
if let Some(config_dir) = dirs::config_dir() {
|
||||
let yarr_config = config_dir.join("yarr").join("config.toml");
|
||||
if yarr_config.exists() {
|
||||
builder = builder.add_source(File::from(yarr_config));
|
||||
}
|
||||
}
|
||||
|
||||
// Also try current directory
|
||||
let local_config = std::env::current_dir()
|
||||
.map(|dir| dir.join("yarr.toml"))
|
||||
.unwrap_or_else(|_| PathBuf::from("yarr.toml"));
|
||||
|
||||
if local_config.exists() {
|
||||
builder = builder.add_source(File::from(local_config));
|
||||
}
|
||||
}
|
||||
|
||||
// Add environment variables with YARR_ prefix
|
||||
builder = builder.add_source(
|
||||
Environment::with_prefix("YARR")
|
||||
.try_parsing(true)
|
||||
.separator("_"),
|
||||
);
|
||||
|
||||
// Override with command line arguments if provided
|
||||
let mut config = builder.build()?.try_deserialize::<AppConfig>()?;
|
||||
|
||||
if let Some(url) = sonarr_url {
|
||||
config.sonarr.url = url;
|
||||
}
|
||||
|
||||
if let Some(api_key) = sonarr_api_key {
|
||||
config.sonarr.api_key = api_key;
|
||||
}
|
||||
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
/// Create a sample config file
|
||||
pub fn create_sample_config(path: &PathBuf) -> Result<(), Box<dyn std::error::Error>> {
|
||||
let sample_config = AppConfig {
|
||||
sonarr: SonarrConfig {
|
||||
url: "http://localhost:8989".to_string(),
|
||||
api_key: "your-api-key-here".to_string(),
|
||||
},
|
||||
};
|
||||
|
||||
let toml_content = toml::to_string_pretty(&sample_config)?;
|
||||
|
||||
// Create directory if it doesn't exist
|
||||
if let Some(parent) = path.parent() {
|
||||
std::fs::create_dir_all(parent)?;
|
||||
}
|
||||
|
||||
std::fs::write(path, toml_content)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validate the configuration
|
||||
pub fn validate(&self) -> Result<(), String> {
|
||||
if self.sonarr.url.is_empty() {
|
||||
return Err("Sonarr URL is required".to_string());
|
||||
}
|
||||
|
||||
if self.sonarr.api_key.is_empty() {
|
||||
return Err("Sonarr API key is required".to_string());
|
||||
}
|
||||
|
||||
// Validate URL format
|
||||
if !self.sonarr.url.starts_with("http://") && !self.sonarr.url.starts_with("https://") {
|
||||
return Err("Sonarr URL must start with http:// or https://".to_string());
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get the default config file paths
|
||||
pub fn get_default_config_paths() -> Vec<PathBuf> {
|
||||
let mut paths = Vec::new();
|
||||
|
||||
// Current directory
|
||||
if let Ok(current_dir) = std::env::current_dir() {
|
||||
paths.push(current_dir.join("yarr.toml"));
|
||||
}
|
||||
|
||||
// User config directory
|
||||
if let Some(config_dir) = dirs::config_dir() {
|
||||
paths.push(config_dir.join("yarr").join("config.toml"));
|
||||
}
|
||||
|
||||
paths
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::env;
|
||||
|
||||
#[test]
|
||||
fn test_default_config() {
|
||||
let config = AppConfig::default();
|
||||
assert_eq!(config.sonarr.url, "http://localhost:8989");
|
||||
assert_eq!(config.sonarr.api_key, "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_validation() {
|
||||
let mut config = AppConfig::default();
|
||||
|
||||
// Should fail validation with empty API key
|
||||
assert!(config.validate().is_err());
|
||||
|
||||
// Should fail with invalid URL
|
||||
config.sonarr.api_key = "test-key".to_string();
|
||||
config.sonarr.url = "invalid-url".to_string();
|
||||
assert!(config.validate().is_err());
|
||||
|
||||
// Should pass with valid config
|
||||
config.sonarr.url = "https://example.com".to_string();
|
||||
assert!(config.validate().is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_config_override() {
|
||||
// Test that CLI args override config
|
||||
let config = AppConfig::load(
|
||||
None,
|
||||
Some("https://cli-url.com".to_string()),
|
||||
Some("cli-api-key".to_string()),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(config.sonarr.url, "https://cli-url.com");
|
||||
assert_eq!(config.sonarr.api_key, "cli-api-key");
|
||||
}
|
||||
}
|
||||
20
yarr.toml.example
Normal file
20
yarr.toml.example
Normal file
@@ -0,0 +1,20 @@
|
||||
# Yarr Configuration File
|
||||
# Copy this file to one of the following locations:
|
||||
# - ./yarr.toml (current directory)
|
||||
# - ~/.config/yarr/config.toml (user config directory)
|
||||
|
||||
[sonarr]
|
||||
# Sonarr server URL (required)
|
||||
# Example: "http://localhost:8989" or "https://sonarr.example.com"
|
||||
url = "http://localhost:8989"
|
||||
|
||||
# Sonarr API key (required)
|
||||
# You can find this in Sonarr under Settings > General > Security > API Key
|
||||
api_key = "your-api-key-here"
|
||||
|
||||
# Environment variables can also be used:
|
||||
# YARR_SONARR_URL="http://localhost:8989"
|
||||
# YARR_SONARR_API_KEY="your-api-key-here"
|
||||
#
|
||||
# Command line arguments take highest priority:
|
||||
# yarr --sonarr-url="http://localhost:8989" --sonarr-api-key="your-key"
|
||||
Reference in New Issue
Block a user