11use std:: fmt;
22use std:: path:: Path ;
3- use std:: time:: Duration ;
43
54use confique:: Config ;
5+ use trogon_nats:: jetstream:: StreamMaxAge ;
66use trogon_nats:: { NatsAuth , NatsToken } ;
77use trogon_source_discord:: config:: DiscordBotToken ;
88use trogon_source_github:: config:: GitHubWebhookSecret ;
99use trogon_source_gitlab:: config:: GitLabWebhookSecret ;
1010use trogon_source_linear:: config:: LinearWebhookSecret ;
1111use trogon_source_slack:: config:: SlackSigningSecret ;
1212use trogon_source_telegram:: config:: TelegramWebhookSecret ;
13+ use trogon_std:: NonZeroDuration ;
1314
1415#[ derive( Debug ) ]
1516pub enum ConfigError {
@@ -304,24 +305,97 @@ fn resolve_github(
304305 }
305306 } ;
306307
308+ let nats_ack_timeout = match NonZeroDuration :: from_secs ( section. nats_ack_timeout_secs ) {
309+ Ok ( d) => d,
310+ Err ( _) => {
311+ errors. push ( "github: nats_ack_timeout_secs must not be zero" . to_string ( ) ) ;
312+ return None ;
313+ }
314+ } ;
315+
316+ let stream_max_age = match StreamMaxAge :: from_secs ( section. stream_max_age_secs ) {
317+ Ok ( age) => age,
318+ Err ( _) => {
319+ errors. push ( "github: stream_max_age_secs must not be zero" . to_string ( ) ) ;
320+ return None ;
321+ }
322+ } ;
323+
307324 Some ( trogon_source_github:: GithubConfig {
308325 webhook_secret,
309326 subject_prefix,
310327 stream_name,
311- stream_max_age : Duration :: from_secs ( section . stream_max_age_secs ) ,
312- nats_ack_timeout : Duration :: from_secs ( section . nats_ack_timeout_secs ) ,
328+ stream_max_age,
329+ nats_ack_timeout,
313330 } )
314331}
315332
316333fn resolve_discord (
317334 section : DiscordConfig ,
318335 errors : & mut Vec < String > ,
319336) -> Option < trogon_source_discord:: DiscordConfig > {
320- let mode_str = section. mode . filter ( |s| !s. is_empty ( ) ) ?;
337+ let mode_str = section. mode . as_deref ( ) . filter ( |s| !s. is_empty ( ) ) ?;
338+
339+ let mode = resolve_discord_mode ( & section, mode_str, errors) ?;
321340
322- let mode = match mode_str. to_ascii_lowercase ( ) . as_str ( ) {
341+ let subject_prefix = match NatsToken :: new ( section. subject_prefix ) {
342+ Ok ( t) => t,
343+ Err ( e) => {
344+ errors. push ( format ! ( "discord: invalid subject_prefix: {e:?}" ) ) ;
345+ return None ;
346+ }
347+ } ;
348+
349+ let stream_name = match NatsToken :: new ( section. stream_name ) {
350+ Ok ( t) => t,
351+ Err ( e) => {
352+ errors. push ( format ! ( "discord: invalid stream_name: {e:?}" ) ) ;
353+ return None ;
354+ }
355+ } ;
356+
357+ let nats_ack_timeout = match NonZeroDuration :: from_secs ( section. nats_ack_timeout_secs ) {
358+ Ok ( d) => d,
359+ Err ( _) => {
360+ errors. push ( "discord: nats_ack_timeout_secs must not be zero" . to_string ( ) ) ;
361+ return None ;
362+ }
363+ } ;
364+
365+ let nats_request_timeout = match NonZeroDuration :: from_secs ( section. nats_request_timeout_secs ) {
366+ Ok ( d) => d,
367+ Err ( _) => {
368+ errors. push ( "discord: nats_request_timeout_secs must not be zero" . to_string ( ) ) ;
369+ return None ;
370+ }
371+ } ;
372+
373+ let stream_max_age = match StreamMaxAge :: from_secs ( section. stream_max_age_secs ) {
374+ Ok ( age) => age,
375+ Err ( _) => {
376+ errors. push ( "discord: stream_max_age_secs must not be zero" . to_string ( ) ) ;
377+ return None ;
378+ }
379+ } ;
380+
381+ Some ( trogon_source_discord:: DiscordConfig {
382+ mode,
383+ subject_prefix,
384+ stream_name,
385+ stream_max_age,
386+ nats_ack_timeout,
387+ nats_request_timeout,
388+ } )
389+ }
390+
391+ fn resolve_discord_mode (
392+ section : & DiscordConfig ,
393+ mode_str : & str ,
394+ errors : & mut Vec < String > ,
395+ ) -> Option < trogon_source_discord:: config:: SourceMode > {
396+ match mode_str. to_ascii_lowercase ( ) . as_str ( ) {
323397 "gateway" => {
324- let Some ( token_str) = section. bot_token . filter ( |s| !s. is_empty ( ) ) else {
398+ let Some ( token_str) = section. bot_token . as_deref ( ) . filter ( |s| !s. is_empty ( ) ) else {
325399 errors. push ( "discord: bot_token is required when mode=gateway" . to_string ( ) ) ;
326400 return None ;
327401 } ;
@@ -346,57 +420,33 @@ fn resolve_discord(
346420 trogon_source_discord:: config:: default_intents ( )
347421 } ;
348422
349- trogon_source_discord:: config:: SourceMode :: Gateway { bot_token, intents }
423+ Some ( trogon_source_discord:: config:: SourceMode :: Gateway { bot_token, intents } )
350424 }
351425 "webhook" => {
352- let Some ( public_key_hex) = section. public_key . filter ( |s| !s. is_empty ( ) ) else {
426+ let Some ( public_key_hex) = section. public_key . as_deref ( ) . filter ( |s| !s. is_empty ( ) )
427+ else {
353428 errors. push ( "discord: public_key is required when mode=webhook" . to_string ( ) ) ;
354429 return None ;
355430 } ;
356431
357432 let public_key =
358- match trogon_source_discord:: signature:: parse_public_key ( & public_key_hex) {
433+ match trogon_source_discord:: signature:: parse_public_key ( public_key_hex) {
359434 Ok ( pk) => pk,
360435 Err ( e) => {
361436 errors. push ( format ! ( "discord: invalid public_key: {e}" ) ) ;
362437 return None ;
363438 }
364439 } ;
365440
366- trogon_source_discord:: config:: SourceMode :: Webhook { public_key }
441+ Some ( trogon_source_discord:: config:: SourceMode :: Webhook { public_key } )
367442 }
368443 other => {
369444 errors. push ( format ! (
370445 "discord: mode must be 'gateway' or 'webhook', got '{other}'"
371446 ) ) ;
372- return None ;
373- }
374- } ;
375-
376- let subject_prefix = match NatsToken :: new ( section. subject_prefix ) {
377- Ok ( t) => t,
378- Err ( e) => {
379- errors. push ( format ! ( "discord: invalid subject_prefix: {e:?}" ) ) ;
380- return None ;
381- }
382- } ;
383-
384- let stream_name = match NatsToken :: new ( section. stream_name ) {
385- Ok ( t) => t,
386- Err ( e) => {
387- errors. push ( format ! ( "discord: invalid stream_name: {e:?}" ) ) ;
388- return None ;
447+ None
389448 }
390- } ;
391-
392- Some ( trogon_source_discord:: DiscordConfig {
393- mode,
394- subject_prefix,
395- stream_name,
396- stream_max_age : Duration :: from_secs ( section. stream_max_age_secs ) ,
397- nats_ack_timeout : Duration :: from_secs ( section. nats_ack_timeout_secs ) ,
398- nats_request_timeout : Duration :: from_secs ( section. nats_request_timeout_secs ) ,
399- } )
449+ }
400450}
401451
402452fn resolve_slack (
@@ -429,13 +479,37 @@ fn resolve_slack(
429479 }
430480 } ;
431481
482+ let nats_ack_timeout = match NonZeroDuration :: from_secs ( section. nats_ack_timeout_secs ) {
483+ Ok ( d) => d,
484+ Err ( _) => {
485+ errors. push ( "slack: nats_ack_timeout_secs must not be zero" . to_string ( ) ) ;
486+ return None ;
487+ }
488+ } ;
489+
490+ let timestamp_max_drift = match NonZeroDuration :: from_secs ( section. timestamp_max_drift_secs ) {
491+ Ok ( d) => d,
492+ Err ( _) => {
493+ errors. push ( "slack: timestamp_max_drift_secs must not be zero" . to_string ( ) ) ;
494+ return None ;
495+ }
496+ } ;
497+
498+ let stream_max_age = match StreamMaxAge :: from_secs ( section. stream_max_age_secs ) {
499+ Ok ( age) => age,
500+ Err ( _) => {
501+ errors. push ( "slack: stream_max_age_secs must not be zero" . to_string ( ) ) ;
502+ return None ;
503+ }
504+ } ;
505+
432506 Some ( trogon_source_slack:: SlackConfig {
433507 signing_secret,
434508 subject_prefix,
435509 stream_name,
436- stream_max_age : Duration :: from_secs ( section . stream_max_age_secs ) ,
437- nats_ack_timeout : Duration :: from_secs ( section . nats_ack_timeout_secs ) ,
438- timestamp_max_drift : Duration :: from_secs ( section . timestamp_max_drift_secs ) ,
510+ stream_max_age,
511+ nats_ack_timeout,
512+ timestamp_max_drift,
439513 } )
440514}
441515
@@ -469,12 +543,28 @@ fn resolve_telegram(
469543 }
470544 } ;
471545
546+ let nats_ack_timeout = match NonZeroDuration :: from_secs ( section. nats_ack_timeout_secs ) {
547+ Ok ( d) => d,
548+ Err ( _) => {
549+ errors. push ( "telegram: nats_ack_timeout_secs must not be zero" . to_string ( ) ) ;
550+ return None ;
551+ }
552+ } ;
553+
554+ let stream_max_age = match StreamMaxAge :: from_secs ( section. stream_max_age_secs ) {
555+ Ok ( age) => age,
556+ Err ( _) => {
557+ errors. push ( "telegram: stream_max_age_secs must not be zero" . to_string ( ) ) ;
558+ return None ;
559+ }
560+ } ;
561+
472562 Some ( trogon_source_telegram:: TelegramSourceConfig {
473563 webhook_secret,
474564 subject_prefix,
475565 stream_name,
476- stream_max_age : Duration :: from_secs ( section . stream_max_age_secs ) ,
477- nats_ack_timeout : Duration :: from_secs ( section . nats_ack_timeout_secs ) ,
566+ stream_max_age,
567+ nats_ack_timeout,
478568 } )
479569}
480570
@@ -508,12 +598,28 @@ fn resolve_gitlab(
508598 }
509599 } ;
510600
601+ let nats_ack_timeout = match NonZeroDuration :: from_secs ( section. nats_ack_timeout_secs ) {
602+ Ok ( d) => d,
603+ Err ( _) => {
604+ errors. push ( "gitlab: nats_ack_timeout_secs must not be zero" . to_string ( ) ) ;
605+ return None ;
606+ }
607+ } ;
608+
609+ let stream_max_age = match StreamMaxAge :: from_secs ( section. stream_max_age_secs ) {
610+ Ok ( age) => age,
611+ Err ( _) => {
612+ errors. push ( "gitlab: stream_max_age_secs must not be zero" . to_string ( ) ) ;
613+ return None ;
614+ }
615+ } ;
616+
511617 Some ( trogon_source_gitlab:: GitlabConfig {
512618 webhook_secret,
513619 subject_prefix,
514620 stream_name,
515- stream_max_age : Duration :: from_secs ( section . stream_max_age_secs ) ,
516- nats_ack_timeout : Duration :: from_secs ( section . nats_ack_timeout_secs ) ,
621+ stream_max_age,
622+ nats_ack_timeout,
517623 } )
518624}
519625
@@ -547,13 +653,28 @@ fn resolve_linear(
547653 }
548654 } ;
549655
656+ let nats_ack_timeout = match NonZeroDuration :: from_secs ( section. nats_ack_timeout_secs ) {
657+ Ok ( d) => d,
658+ Err ( _) => {
659+ errors. push ( "linear: nats_ack_timeout_secs must not be zero" . to_string ( ) ) ;
660+ return None ;
661+ }
662+ } ;
663+
664+ let stream_max_age = match StreamMaxAge :: from_secs ( section. stream_max_age_secs ) {
665+ Ok ( age) => age,
666+ Err ( _) => {
667+ errors. push ( "linear: stream_max_age_secs must not be zero" . to_string ( ) ) ;
668+ return None ;
669+ }
670+ } ;
671+
550672 Some ( trogon_source_linear:: LinearConfig {
551673 webhook_secret,
552674 subject_prefix,
553675 stream_name,
554- stream_max_age : Duration :: from_secs ( section. stream_max_age_secs ) ,
555- timestamp_tolerance : ( section. timestamp_tolerance_secs > 0 )
556- . then ( || Duration :: from_secs ( section. timestamp_tolerance_secs ) ) ,
557- nats_ack_timeout : Duration :: from_secs ( section. nats_ack_timeout_secs ) ,
676+ stream_max_age,
677+ timestamp_tolerance : NonZeroDuration :: from_secs ( section. timestamp_tolerance_secs ) . ok ( ) ,
678+ nats_ack_timeout,
558679 } )
559680}
0 commit comments