use std::path::{Path, PathBuf};
use clap::Parser;
use serde::{Deserialize, Serialize};
use tower::Service;
use url::Url;
use crate::{
server::{self, EndpointConfig, ServerConfig},
util::{ConfigError, Result},
};
#[derive(Parser)]
pub struct Cli {
#[clap(subcommand)]
subcmd: SubCommand,
#[clap(long, short)]
config: PathBuf,
}
#[derive(Parser)]
enum SubCommand {
Server(ServerConfig),
Test(TestConfig),
}
#[derive(Parser)]
struct TestConfig {
endpoint: String,
#[clap(long, short)]
source: Option<Url>,
#[clap(long, short)]
limit_filters: Option<usize>,
#[clap(long, short('n'))]
limit_posts: Option<usize>,
#[clap(long, short)]
compact_output: bool,
#[clap(long, short)]
quiet: bool,
}
impl TestConfig {
fn to_endpoint_param(&self) -> server::EndpointParam {
server::EndpointParam::new(
self.source.clone(),
self.limit_filters,
self.limit_posts,
!self.compact_output,
)
}
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct FeedDefinition {
pub endpoints: Vec<EndpointConfig>,
}
impl FeedDefinition {
pub fn load_from_file(path: &Path) -> Result<Self> {
let f = std::fs::File::open(path)?;
let feed_definition =
serde_yaml::from_reader(f).map_err(ConfigError::from)?;
Ok(feed_definition)
}
fn get_endpoint(&self, endpoint: &str) -> Option<EndpointConfig> {
self.endpoints.iter().find(|e| e.path == endpoint).cloned()
}
fn endpoints(&self) -> impl Iterator<Item = &EndpointConfig> {
self.endpoints.iter()
}
}
impl Cli {
pub async fn run(self) -> Result<()> {
match self.subcmd {
SubCommand::Server(server_config) => {
server_config.run(&self.config).await
}
SubCommand::Test(test_config) => {
let feed_defn = FeedDefinition::load_from_file(&self.config)?;
test_endpoint(feed_defn, &test_config).await;
Ok(())
}
}
}
}
async fn test_endpoint(feed_defn: FeedDefinition, test_config: &TestConfig) {
let Some(endpoint_conf) = feed_defn.get_endpoint(&test_config.endpoint)
else {
let endpoints: Vec<_> =
feed_defn.endpoints().map(|e| e.path.clone()).collect();
eprintln!(
"endpoint {} not found (available endpoints: {:?})",
&test_config.endpoint, endpoints
);
return;
};
let mut endpoint_service = endpoint_conf
.into_service()
.await
.expect("failed to build endpoint service");
let endpoint_param = test_config.to_endpoint_param();
let outcome = endpoint_service
.call(endpoint_param)
.await
.expect("failed to call endpoint service");
if !test_config.quiet {
println!("{}", outcome.feed_xml());
}
}