1use std::str::FromStr;
4
5use strum::{Display, EnumString, VariantNames};
6
7use crate::NtripClientError;
8
9#[derive(Clone, PartialEq, Debug)]
11#[cfg_attr(feature = "clap", derive(clap::Parser))]
12#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
13pub struct NtripConfig {
14 #[cfg_attr(
16 feature = "clap",
17 clap(long = "ntrip-host", env = "NTRIP_HOST", default_value = "rtk2go.com")
18 )]
19 pub host: String,
20
21 #[cfg_attr(
23 feature = "clap",
24 clap(long = "ntrip-port", env = "NTRIP_PORT", default_value_t = 2101)
25 )]
26 pub port: u16,
27
28 #[cfg_attr(
30 feature = "clap",
31 clap(long = "ntrip-use-tls", env = "NTRIP_USE_TLS", default_value_t = false)
32 )]
33 pub use_tls: bool,
34}
35
36impl Default for NtripConfig {
37 fn default() -> Self {
40 Self::from_provider(RtcmProvider::Centipede)
41 }
42}
43
44impl NtripConfig {
45 pub fn to_url(&self) -> String {
47 format!("{}:{}", self.host, self.port)
48 }
49
50 pub fn from_provider(network: RtcmProvider) -> Self {
52 Self {
53 host: network.host().to_string(),
54 port: network.port(),
55 use_tls: network.uses_tls(),
56 }
57 }
58
59 pub fn with_host(&self, address: &str) -> Self {
61 let mut s = self.clone();
62 s.host = address.to_string();
63 s
64 }
65
66 pub fn with_port(&self, port: u16) -> Self {
68 let mut s = self.clone();
69 s.port = port;
70 s
71 }
72
73 pub fn with_tls(&self) -> Self {
75 let mut s = self.clone();
76 s.use_tls = true;
77 s
78 }
79
80 pub fn without_tls(&self) -> Self {
82 let mut s = self.clone();
83 s.use_tls = false;
84 s
85 }
86}
87
88#[derive(Clone, Default, PartialEq, Debug)]
90#[cfg_attr(feature = "clap", derive(clap::Parser))]
91pub struct NtripCredentials {
92 #[cfg_attr(feature = "clap", clap(long = "ntrip-user", env = "NTRIP_USER"))]
94 pub user: String,
95
96 #[cfg_attr(
98 feature = "clap",
99 clap(long = "ntrip-pass", env = "NTRIP_PASS", default_value = "")
100 )]
101 pub pass: String,
102}
103
104impl NtripCredentials {
105 pub fn with_username(&self, username: &str) -> Self {
107 let mut s = self.clone();
108 s.user = username.to_string();
109 s
110 }
111
112 pub fn with_password(&self, password: &str) -> Self {
114 let mut s = self.clone();
115 s.pass = password.to_string();
116 s
117 }
118}
119
120#[derive(Clone, PartialEq, Debug, EnumString, Display, VariantNames)]
122pub enum RtcmProvider {
123 #[strum(serialize = "linz")]
127 Linz,
128 #[strum(serialize = "rtk2go")]
130 Rtk2Go,
131 #[strum(serialize = "posau")]
135 PosAu,
136 #[strum(serialize = "centipede")]
138 Centipede,
139}
140
141impl RtcmProvider {
142 pub fn host(&self) -> &str {
144 match self {
145 RtcmProvider::Linz => "positionz-rt.linz.govt.nz",
146 RtcmProvider::Rtk2Go => "rtk2go.com",
147 RtcmProvider::PosAu => "ntrip.data.gnss.ga.gov.au",
148 RtcmProvider::Centipede => "caster.centipede.fr",
149 }
150 }
151
152 pub fn port(&self) -> u16 {
154 match self {
155 RtcmProvider::Linz => 2101,
156 RtcmProvider::Rtk2Go => 2101,
157 RtcmProvider::PosAu => 443,
158 RtcmProvider::Centipede => 2101,
159 }
160 }
161
162 pub fn uses_tls(&self) -> bool {
164 match self {
165 RtcmProvider::Linz => false,
166 RtcmProvider::Rtk2Go => false,
167 RtcmProvider::PosAu => true,
168 RtcmProvider::Centipede => false,
169 }
170 }
171}
172
173impl FromStr for NtripConfig {
197 type Err = NtripClientError;
198
199 fn from_str(s: &str) -> Result<Self, Self::Err> {
201 if let Ok(provider) = RtcmProvider::from_str(s) {
203 return Ok(NtripConfig::from_provider(provider));
204 }
205
206 let proto = if s.starts_with("http://") {
208 "http"
209 } else if s.starts_with("https://") {
210 "https"
211 } else if s.starts_with("ntrip://") {
212 "ntrip"
213 } else {
214 "unknown"
215 };
216 let s = s.trim_start_matches(&format!("{proto}://"));
217
218 let parts: Vec<&str> = s.split(':').collect();
220 if parts.is_empty() {
221 return Err(NtripClientError::InvalidUrl);
222 }
223 let host = parts[0].to_string();
224
225 let port = if parts.len() > 1 {
227 parts[1]
228 .parse::<u16>()
229 .map_err(|_| NtripClientError::InvalidPort)?
230 } else if proto == "https" {
231 443
232 } else {
233 2101
234 };
235 Ok(NtripConfig {
236 host,
237 port,
238 use_tls: port == 443,
239 })
240 }
241}