added config
This commit is contained in:
103
src/core/config.rs
Normal file
103
src/core/config.rs
Normal file
@@ -0,0 +1,103 @@
|
||||
use std::{fs, io, path::PathBuf};
|
||||
|
||||
use super::paths;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Config {
|
||||
pub base_url: String,
|
||||
pub access_token: Option<String>,
|
||||
pub user_id: Option<String>,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ConfigError {
|
||||
NotFound(PathBuf),
|
||||
Io(io::Error),
|
||||
Parse(String),
|
||||
}
|
||||
|
||||
impl From<io::Error> for ConfigError {
|
||||
fn from(e: io::Error) -> Self {
|
||||
Self::Io(e)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn config_path() -> Option<PathBuf> {
|
||||
paths::app_config_dir().map(|d| d.join("config.toml"))
|
||||
}
|
||||
|
||||
// Very small TOML-ish parser for now (no deps).
|
||||
// Format:
|
||||
// base_url = "http://..."
|
||||
// access_token = "..."
|
||||
pub fn load_config() -> Result<Config, ConfigError> {
|
||||
let path = config_path().ok_or_else(|| ConfigError::Parse("No HOME/XDG dirs".into()))?;
|
||||
if !path.exists() {
|
||||
return Err(ConfigError::NotFound(path));
|
||||
}
|
||||
|
||||
let s = fs::read_to_string(&path)?;
|
||||
parse_config(&s).map_err(ConfigError::Parse)
|
||||
}
|
||||
|
||||
fn parse_config(s: &str) -> Result<Config, String> {
|
||||
let mut base_url: Option<String> = None;
|
||||
let mut access_token: Option<String> = None;
|
||||
let mut user_id: Option<String> = None;
|
||||
|
||||
for (i, line) in s.lines().enumerate() {
|
||||
let line = line.trim();
|
||||
if line.is_empty() || line.starts_with('#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
let (k, v) = line
|
||||
.split_once('=')
|
||||
.ok_or_else(|| format!("Line {}: expected key = value", i + 1))?;
|
||||
|
||||
let key = k.trim();
|
||||
let mut val = v.trim().to_string();
|
||||
|
||||
// strip quotes if present
|
||||
if val.starts_with('"') && val.ends_with('"') && val.len() >= 2 {
|
||||
val = val[1..val.len() - 1].to_string();
|
||||
}
|
||||
|
||||
match key {
|
||||
"base_url" => base_url = Some(val),
|
||||
"access_token" => access_token = Some(val),
|
||||
"user_id" => user_id = Some(val),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let base_url = base_url.ok_or_else(|| "Missing base_url".to_string())?;
|
||||
|
||||
Ok(Config {
|
||||
base_url,
|
||||
access_token,
|
||||
user_id,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn save_config(cfg: &Config) -> Result<(), ConfigError> {
|
||||
let dir = paths::app_config_dir()
|
||||
.ok_or_else(|| ConfigError::Parse("No HOME/XDG dirs".into()))?;
|
||||
std::fs::create_dir_all(&dir)?;
|
||||
|
||||
let path = dir.join("config.toml");
|
||||
|
||||
let mut out = String::new();
|
||||
out.push_str(&format!("base_url = \"{}\"\n", cfg.base_url));
|
||||
|
||||
if let Some(t) = &cfg.access_token {
|
||||
out.push_str(&format!("access_token = \"{}\"\n", t));
|
||||
}
|
||||
if let Some(u) = &cfg.user_id {
|
||||
out.push_str(&format!("user_id = \"{}\"\n", u));
|
||||
}
|
||||
|
||||
std::fs::write(path, out)?;
|
||||
Ok(())
|
||||
}
|
||||
33
src/core/jellyfin.rs
Normal file
33
src/core/jellyfin.rs
Normal file
@@ -0,0 +1,33 @@
|
||||
use super::config::Config;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct JellyfinClient {
|
||||
pub cfg: Config,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct JfItem {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub item_type: String, // "Movie", "Series", etc.
|
||||
pub image_tag: Option<String>,
|
||||
}
|
||||
|
||||
impl JellyfinClient {
|
||||
pub fn new(cfg: Config) -> Self {
|
||||
Self { cfg }
|
||||
}
|
||||
|
||||
// Placeholder for now — next step will use reqwest and actually call Jellyfin.
|
||||
pub fn test_connection_stub(&self) -> Result<String, String> {
|
||||
Ok(format!("Configured for {}", self.cfg.base_url))
|
||||
}
|
||||
|
||||
// Placeholder list so we can wire UI without network yet
|
||||
pub fn list_movies_stub(&self) -> Vec<JfItem> {
|
||||
vec![
|
||||
JfItem { id: "1".into(), name: "Movie A".into(), item_type: "Movie".into(), image_tag: None },
|
||||
JfItem { id: "2".into(), name: "Movie B".into(), item_type: "Movie".into(), image_tag: None },
|
||||
]
|
||||
}
|
||||
}
|
||||
3
src/core/mod.rs
Normal file
3
src/core/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub mod config;
|
||||
pub mod jellyfin;
|
||||
pub mod paths;
|
||||
35
src/core/paths.rs
Normal file
35
src/core/paths.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
use std::{env, path::PathBuf};
|
||||
|
||||
pub fn home_dir() -> Option<PathBuf> {
|
||||
env::var_os("HOME").map(PathBuf::from)
|
||||
}
|
||||
|
||||
pub fn config_dir() -> Option<PathBuf> {
|
||||
// XDG first, fallback to ~/.config
|
||||
if let Some(p) = env::var_os("XDG_CONFIG_HOME").map(PathBuf::from) {
|
||||
Some(p)
|
||||
} else {
|
||||
home_dir().map(|h| h.join(".config"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cache_dir() -> Option<PathBuf> {
|
||||
// XDG first, fallback to ~/.cache
|
||||
if let Some(p) = env::var_os("XDG_CACHE_HOME").map(PathBuf::from) {
|
||||
Some(p)
|
||||
} else {
|
||||
home_dir().map(|h| h.join(".cache"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn app_config_dir() -> Option<PathBuf> {
|
||||
config_dir().map(|d| d.join("cj-jftui"))
|
||||
}
|
||||
|
||||
pub fn app_cache_dir() -> Option<PathBuf> {
|
||||
cache_dir().map(|d| d.join("cj-jftui"))
|
||||
}
|
||||
|
||||
pub fn posters_cache_dir() -> Option<PathBuf> {
|
||||
app_cache_dir().map(|d| d.join("posters"))
|
||||
}
|
||||
Reference in New Issue
Block a user