Skip to content

Commit bb82692

Browse files
committed
WIP
1 parent 3d84d58 commit bb82692

8 files changed

Lines changed: 419 additions & 125 deletions

File tree

src/cli.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@ pub trait ExecutableCommand<'input> {
1010
type I;
1111
type R;
1212

13-
fn execute(&self, input: &'input Self::I) -> impl Future<Output = Self::R> + Send;
13+
fn execute(&self, input: &'input Self::I) -> impl Future<Output = Self::R>;
1414
}

src/cli/command.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::{
1010
};
1111

1212
#[derive(Debug, ClapSubcommand)]
13-
#[command(version, about, long_about = None, propagate_version = true)]
13+
#[command(version, about, long_about = None)]
1414
pub enum Subcommand<'a> {
1515
Auto(auto::Command<'a>),
1616
Get(get::Command<'a>),

src/cli/get.rs

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::{
77
Config,
88
cli::ExecutableCommand,
99
config::provider::Provider as ProviderConfig,
10-
provider::{Provider, nitrado::NitradoProvider},
10+
provider::{GetAllRecordsInput, GetRecordsInput, Provider, nitrado::NitradoProvider},
1111
};
1212

1313
#[derive(Debug)]
@@ -20,21 +20,24 @@ pub struct Input<'config> {
2020
pub enum Error {
2121
#[error("The given provider is not configured: {0}")]
2222
ProviderNotConfigured(String),
23+
24+
#[error("Provider error: {0}")]
25+
ProviderError(#[from] anyhow::Error),
2326
}
2427

2528
//TODO: Fix order of usage message (provider should come first)
2629
#[derive(Debug, Args)]
2730
#[group(required = true, multiple = false)]
28-
pub struct DomainArgs {
29-
/// Domains to get records for
30-
domains: Vec<String>,
31+
pub struct SubdomainArgs {
32+
/// Subdomains to get records for
33+
subdomains: Vec<String>,
3134

3235
/// Get all records
3336
#[clap(short, long, default_value = "false")]
3437
pub all: bool,
3538
}
3639

37-
/// Update providers as defined in the configuration file
40+
/// Get one or more DNS records from a provider
3841
#[derive(Debug, Parser)]
3942
#[command(version, about, long_about = None, propagate_version = true)]
4043
pub struct Command<'command> {
@@ -44,10 +47,14 @@ pub struct Command<'command> {
4447
/// Name of the provider to get records from
4548
provider: String,
4649

50+
/// Domain to get records for
51+
domain: String,
52+
4753
#[command(flatten)]
48-
domain_args: DomainArgs,
54+
subdomain_args: SubdomainArgs,
4955
}
5056

57+
//TODO: Move
5158
fn get_provider<'config>(
5259
name: &str,
5360
config: &'config Config,
@@ -73,13 +80,42 @@ impl<'command> ExecutableCommand<'command> for Command<'command> {
7380
let config = input.config;
7481
let provider_name = self.provider.as_str();
7582

76-
let _provider = match get_provider(provider_name, config) {
83+
let provider = match get_provider(provider_name, config) {
7784
Some(p) => p,
7885
None => return Err(Error::ProviderNotConfigured(provider_name.to_string())),
7986
};
8087

81-
let _reqwest = reqwest::Client::new();
88+
let reqwest = reqwest::Client::new();
89+
90+
let results = if self.subdomain_args.all {
91+
let input = GetAllRecordsInput {
92+
domain: self.domain.as_str(),
93+
};
94+
95+
provider.get_all_records(reqwest, &input).await
96+
} else {
97+
let input = GetRecordsInput {
98+
domain: self.domain.as_str(),
99+
subdomains: self
100+
.subdomain_args
101+
.subdomains
102+
.iter()
103+
.map(|s| s.as_str())
104+
.collect(),
105+
};
106+
107+
provider.get_records(reqwest, &input).await
108+
};
109+
110+
let records = match results {
111+
Err(e) => {
112+
eprintln!("Error: {}", e);
113+
return Err(e.into());
114+
}
115+
Ok(records) => records,
116+
};
82117

118+
println!("Records: {:#?}", records);
83119
Ok(())
84120
}
85121
}

src/provider.rs

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,36 @@ pub mod nitrado;
77

88
#[derive(Debug, Clone, PartialEq, Eq)]
99
pub enum Feature {
10-
GetRecord,
1110
GetRecords,
11+
GetAllRecords,
1212
AddRecord,
1313
UpdateRecord,
1414
DeleteRecord,
1515
}
1616

17-
pub struct GetRecordInput {}
18-
pub struct GetRecordsInput {
19-
pub domain: String,
17+
pub struct GetRecordsInput<'input> {
18+
pub domain: &'input str,
19+
pub subdomains: Vec<&'input str>,
20+
}
21+
22+
pub struct GetAllRecordsInput<'input> {
23+
pub domain: &'input str,
24+
}
25+
26+
impl<'input> From<GetRecordsInput<'input>> for GetAllRecordsInput<'input> {
27+
fn from(input: GetRecordsInput<'input>) -> Self {
28+
GetAllRecordsInput {
29+
domain: input.domain,
30+
}
31+
}
32+
}
33+
34+
impl<'input> From<&'input GetRecordsInput<'input>> for GetAllRecordsInput<'input> {
35+
fn from(input: &'input GetRecordsInput<'input>) -> Self {
36+
GetAllRecordsInput {
37+
domain: input.domain,
38+
}
39+
}
2040
}
2141

2242
#[async_trait]
@@ -32,7 +52,13 @@ pub trait Provider {
3252
reqwest: reqwest::Client,
3353
input: &GetRecordsInput,
3454
) -> Result<Vec<Record>>;
35-
async fn get_all_records(&self, reqwest: reqwest::Client) -> Result<Vec<Record>>;
55+
56+
async fn get_all_records(
57+
&self,
58+
reqwest: reqwest::Client,
59+
input: &GetAllRecordsInput,
60+
) -> Result<Vec<Record>>;
61+
3662
async fn add_record(&self, reqwest: reqwest::Client, record: &Record) -> Result<()>;
3763
async fn update_record(&self, reqwest: reqwest::Client, record: &Record) -> Result<()>;
3864
async fn delete_record(&self, reqwest: reqwest::Client, record: &Record) -> Result<()>;

src/provider/nitrado.rs

Lines changed: 33 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -1,110 +1,19 @@
1-
use std::{
2-
net::{Ipv4Addr, Ipv6Addr},
3-
str::FromStr,
4-
};
5-
61
use anyhow::Result;
72
use async_trait::async_trait;
8-
use lum_libs::{
9-
serde::{Deserialize, Serialize},
10-
serde_json,
11-
};
3+
use lum_libs::serde_json;
124
use reqwest::header::HeaderMap;
135
use thiserror::Error;
146

157
use crate::{
16-
config::dns::{AutomaticRecordConfig, RecordConfig, ResolveType},
17-
provider::{self, Feature, Provider},
18-
types::dns::{self, MxRecord, Record, RecordValue},
8+
provider::{Feature, GetAllRecordsInput, GetRecordsInput, Provider},
9+
types::dns::{self},
1910
};
2011

21-
#[derive(Debug, Clone, Serialize, Deserialize)]
22-
#[serde(crate = "lum_libs::serde")]
23-
pub struct Config {
24-
pub name: String,
25-
pub api_key: String,
26-
pub api_base_url: String,
27-
}
12+
pub mod config;
13+
pub mod model;
2814

29-
impl Default for Config {
30-
fn default() -> Self {
31-
Config {
32-
name: "Nitrado1".to_string(),
33-
api_key: "your_api_key".to_string(),
34-
api_base_url: "https://api.nitrado.net".to_string(),
35-
}
36-
}
37-
}
38-
39-
#[derive(Debug, Clone, Serialize, Deserialize)]
40-
#[serde(crate = "lum_libs::serde")]
41-
pub struct DomainConfig {
42-
pub domain: String,
43-
pub records: Vec<RecordConfig>,
44-
}
45-
46-
#[derive(Debug, Clone, Serialize, Deserialize)]
47-
#[serde(crate = "lum_libs::serde")]
48-
pub struct DnsConfig {
49-
pub provider_name: String,
50-
pub domains: Vec<DomainConfig>,
51-
}
52-
53-
impl Default for DnsConfig {
54-
fn default() -> Self {
55-
DnsConfig {
56-
provider_name: "Nitrado1".to_string(),
57-
domains: vec![DomainConfig {
58-
domain: "example.com".to_string(),
59-
records: vec![
60-
RecordConfig::Manual(dns::Record {
61-
domain: "ipv4".to_string(),
62-
value: RecordValue::A(Ipv4Addr::from_str("127.0.0.1").unwrap()),
63-
ttl: Some(3600),
64-
}),
65-
RecordConfig::Manual(dns::Record {
66-
domain: "ipv6".to_string(),
67-
value: RecordValue::AAAA(Ipv6Addr::from_str("::1").unwrap()),
68-
ttl: Some(3600),
69-
}),
70-
RecordConfig::Manual(dns::Record {
71-
domain: "forward".to_string(),
72-
value: RecordValue::CNAME("example.com".to_string()),
73-
ttl: Some(3600),
74-
}),
75-
RecordConfig::Manual(dns::Record {
76-
domain: "@".to_string(),
77-
value: RecordValue::MX(MxRecord {
78-
priority: 10,
79-
target: "mail.example.com".to_string(),
80-
}),
81-
ttl: Some(3600),
82-
}),
83-
RecordConfig::Manual(dns::Record {
84-
domain: "@".to_string(),
85-
value: RecordValue::TXT("v=spf1 include:example.com ~all".to_string()),
86-
ttl: Some(3600),
87-
}),
88-
RecordConfig::Manual(dns::Record {
89-
domain: "@".to_string(),
90-
value: RecordValue::Custom("RecordType".to_string(), "Value".to_string()),
91-
ttl: Some(3600),
92-
}),
93-
RecordConfig::Automatic(AutomaticRecordConfig {
94-
domain: "auto-ipv4".to_string(),
95-
ttl: Some(300),
96-
resolve_type: ResolveType::IPv4,
97-
}),
98-
RecordConfig::Automatic(AutomaticRecordConfig {
99-
domain: "auto-ipv6".to_string(),
100-
ttl: Some(300),
101-
resolve_type: ResolveType::IPv6,
102-
}),
103-
],
104-
}],
105-
}
106-
}
107-
}
15+
pub use config::{Config, DnsConfig, DomainConfig};
16+
pub use model::{GetRecordsResponse, Record, RecordMode, TryFromRecordError};
10817

10918
pub struct NitradoProvider<'provider_config> {
11019
pub provider_config: &'provider_config Config,
@@ -136,8 +45,8 @@ impl Provider for NitradoProvider<'_> {
13645

13746
fn get_supported_features(&self) -> Vec<Feature> {
13847
vec![
139-
Feature::GetRecord,
14048
Feature::GetRecords,
49+
Feature::GetAllRecords,
14150
Feature::AddRecord,
14251
Feature::UpdateRecord,
14352
Feature::DeleteRecord,
@@ -146,8 +55,25 @@ impl Provider for NitradoProvider<'_> {
14655
async fn get_records(
14756
&self,
14857
reqwest: reqwest::Client,
149-
input: &provider::GetRecordsInput,
150-
) -> Result<Vec<Record>> {
58+
input: &GetRecordsInput,
59+
) -> Result<Vec<dns::Record>> {
60+
let get_all_records_input = GetAllRecordsInput::from(input);
61+
let records = self
62+
.get_all_records(reqwest, &get_all_records_input)
63+
.await?;
64+
let records = records
65+
.into_iter()
66+
.filter(|record| input.subdomains.contains(&record.domain.as_str()))
67+
.collect();
68+
69+
Ok(records)
70+
}
71+
72+
async fn get_all_records(
73+
&self,
74+
reqwest: reqwest::Client,
75+
input: &GetAllRecordsInput,
76+
) -> Result<Vec<dns::Record>> {
15177
let mut headers = HeaderMap::new();
15278
headers.insert(
15379
"Authorization",
@@ -168,23 +94,21 @@ impl Provider for NitradoProvider<'_> {
16894
}
16995

17096
let text = response.text().await?;
171-
let json = serde_json::from_str(&text)?;
172-
Ok(json)
173-
}
97+
let response: GetRecordsResponse = serde_json::from_str(&text)?;
98+
let records: Vec<dns::Record> = response.try_into()?;
17499

175-
async fn get_all_records(&self, _reqwest: reqwest::Client) -> Result<Vec<Record>> {
176-
unimplemented!()
100+
Ok(records)
177101
}
178102

179-
async fn add_record(&self, _reqwest: reqwest::Client, _input: &Record) -> Result<()> {
103+
async fn add_record(&self, _reqwest: reqwest::Client, _input: &dns::Record) -> Result<()> {
180104
unimplemented!()
181105
}
182106

183-
async fn update_record(&self, _reqwest: reqwest::Client, _input: &Record) -> Result<()> {
107+
async fn update_record(&self, _reqwest: reqwest::Client, _input: &dns::Record) -> Result<()> {
184108
unimplemented!()
185109
}
186110

187-
async fn delete_record(&self, _reqwest: reqwest::Client, _input: &Record) -> Result<()> {
111+
async fn delete_record(&self, _reqwest: reqwest::Client, _input: &dns::Record) -> Result<()> {
188112
unimplemented!()
189113
}
190114
}

0 commit comments

Comments
 (0)