Initial commit.
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
package configuration
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var errCfg = errors.New("configuration")
|
||||
|
||||
// Config is a configuration controlling structure.
|
||||
type Config struct {
|
||||
RSS *RSS `json:"rss"`
|
||||
Outputs *Outputs `json:"outputs"`
|
||||
}
|
||||
|
||||
// New creates new configuration controller.
|
||||
func New(path string) (*Config, error) {
|
||||
cfg := &Config{}
|
||||
|
||||
// Get configuration file real path.
|
||||
userHomeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: parse configuration file: get real path: get user home dir: %w", errCfg, err)
|
||||
}
|
||||
|
||||
path = strings.Replace(path, "~", userHomeDir, 1)
|
||||
|
||||
absPath, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: parse configuration file: get real path: get absolute path: %w", errCfg, err)
|
||||
}
|
||||
|
||||
cfgFileData, err := os.ReadFile(absPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: parse configuration file: read file: %w", errCfg, err)
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(cfgFileData, cfg); err != nil {
|
||||
return nil, fmt.Errorf("%w: parse configuration file: %w", errCfg, err)
|
||||
}
|
||||
|
||||
// Validate configuration.
|
||||
if err := cfg.Validate(); err != nil {
|
||||
return nil, fmt.Errorf("%w: %w", errCfg, err)
|
||||
}
|
||||
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
// Validate validates app configuration.
|
||||
func (c *Config) Validate() error {
|
||||
if err := c.RSS.Validate(); err != nil {
|
||||
return fmt.Errorf("validate configuration: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package configuration
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
var (
|
||||
errDestIsEmpty = errors.New("dest is empty")
|
||||
errFeedValidate = errors.New("feed configuration validation")
|
||||
errNameIsEmpty = errors.New("name is empty")
|
||||
errOutputIsEmpty = errors.New("output is empty")
|
||||
errURLIsEmpty = errors.New("URL is empty")
|
||||
)
|
||||
|
||||
// Feed is a single RSS feed configuration.
|
||||
type Feed struct {
|
||||
Name string `json:"name"`
|
||||
URL string `json:"url"`
|
||||
Output string `json:"output"`
|
||||
Destination string `json:"dest"`
|
||||
}
|
||||
|
||||
// Validate validates RSS feed configuration.
|
||||
func (f *Feed) Validate() error {
|
||||
if f.Name == "" {
|
||||
return fmt.Errorf("%w: %w", errFeedValidate, errNameIsEmpty)
|
||||
}
|
||||
|
||||
if f.Output == "" {
|
||||
return fmt.Errorf("%w: %w", errFeedValidate, errOutputIsEmpty)
|
||||
}
|
||||
|
||||
if f.Destination == "" {
|
||||
return fmt.Errorf("%w: %w", errFeedValidate, errDestIsEmpty)
|
||||
}
|
||||
|
||||
if f.URL == "" {
|
||||
return fmt.Errorf("%w: %w", errFeedValidate, errURLIsEmpty)
|
||||
}
|
||||
|
||||
if _, err := url.Parse(f.URL); err != nil {
|
||||
return fmt.Errorf("%w: parse feed URL: %w", errFeedValidate, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package configuration
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var errRequestTimeoutTooLow = errors.New("request timeout is too low")
|
||||
|
||||
// HTTPClient is an HTTP client configuration.
|
||||
type HTTPClient struct {
|
||||
RequestTimeoutSeconds int64 `json:"request_timeout_seconds"`
|
||||
}
|
||||
|
||||
// Validate validates configuration for HTTP clients.
|
||||
func (h *HTTPClient) Validate() error {
|
||||
if h.RequestTimeoutSeconds < 1 {
|
||||
return fmt.Errorf("validate HTTP client configuration: %w", errRequestTimeoutTooLow)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package configuration
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
)
|
||||
|
||||
var (
|
||||
errNNTPAddressIsEmpty = errors.New("address is empty")
|
||||
errNNTPInvalidAddress = errors.New("address is invalid")
|
||||
errNNTPInvalidConnTimeout = errors.New("connect timeout is invalid")
|
||||
errNNTPUserOrPassIsEmpty = errors.New("username or password is empty")
|
||||
)
|
||||
|
||||
// NNTP is an NNTP output configuration.
|
||||
type NNTP struct {
|
||||
Address string `json:"address"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
ConnectTimeout int64 `json:"connect_timeout"`
|
||||
}
|
||||
|
||||
// Validate validates configuration.
|
||||
func (n *NNTP) Validate() error {
|
||||
if n.Address == "" {
|
||||
return fmt.Errorf("validate NNTP output configuration: %w", errNNTPAddressIsEmpty)
|
||||
}
|
||||
|
||||
_, _, err := net.SplitHostPort(n.Address)
|
||||
if err != nil {
|
||||
return fmt.Errorf("validate NNTP output configuration: %w: %w", errNNTPInvalidAddress, err)
|
||||
}
|
||||
|
||||
if (n.Username != "" && n.Password == "") || (n.Username == "" && n.Password != "") {
|
||||
return fmt.Errorf("validate NNTP output configuration: %w", errNNTPUserOrPassIsEmpty)
|
||||
}
|
||||
|
||||
if n.ConnectTimeout < 1 {
|
||||
return fmt.Errorf("validate NNTP output configuration: %w", errNNTPInvalidConnTimeout)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package configuration
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Outputs is an outputs configuration.
|
||||
type Outputs struct {
|
||||
NNTP *NNTP `json:"nntp"`
|
||||
}
|
||||
|
||||
// Validate validates configuration.
|
||||
func (o *Outputs) Validate() error {
|
||||
if err := o.NNTP.Validate(); err != nil {
|
||||
return fmt.Errorf("validate outputs configuration: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package configuration
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var errRSSValidate = errors.New("validate RSS configuration")
|
||||
|
||||
// RSS is an RSS parser configuration.
|
||||
type RSS struct {
|
||||
HTTPClient *HTTPClient `json:"http_client"`
|
||||
Feeds []*Feed `json:"feeds"`
|
||||
}
|
||||
|
||||
// Validate validates RSS parser configuration.
|
||||
func (r *RSS) Validate() error {
|
||||
for idx, feed := range r.Feeds {
|
||||
if err := feed.Validate(); err != nil {
|
||||
return fmt.Errorf("%w: feed %d: %w", errRSSValidate, idx, err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := r.HTTPClient.Validate(); err != nil {
|
||||
return fmt.Errorf("%w: %w", errRSSValidate, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user