summaryrefslogtreecommitdiff
path: root/plugin
diff options
context:
space:
mode:
Diffstat (limited to 'plugin')
-rw-r--r--plugin/maxmind/common_in.go90
-rw-r--r--plugin/maxmind/common_out.go121
-rw-r--r--plugin/maxmind/dbip_country_mmdb_in.go4
-rw-r--r--plugin/maxmind/dbip_country_mmdb_out.go4
-rw-r--r--plugin/maxmind/ipinfo_country_mmdb_in.go4
-rw-r--r--plugin/maxmind/ipinfo_country_mmdb_out.go4
-rw-r--r--plugin/maxmind/maxmind_asn_csv_in.go153
-rw-r--r--plugin/maxmind/maxmind_country_csv_in.go119
-rw-r--r--plugin/maxmind/maxmind_country_mmdb_in.go16
-rw-r--r--plugin/maxmind/maxmind_country_mmdb_out.go20
-rw-r--r--plugin/mihomo/mrs_in.go139
-rw-r--r--plugin/mihomo/mrs_out.go107
-rw-r--r--plugin/plaintext/clash_in.go8
-rw-r--r--plugin/plaintext/clash_out.go8
-rw-r--r--plugin/plaintext/common_in.go18
-rw-r--r--plugin/plaintext/common_out.go136
-rw-r--r--plugin/plaintext/json_in.go4
-rw-r--r--plugin/plaintext/surge_in.go4
-rw-r--r--plugin/plaintext/surge_out.go4
-rw-r--r--plugin/plaintext/text_in.go182
-rw-r--r--plugin/plaintext/text_out.go14
-rw-r--r--plugin/special/cutter.go94
-rw-r--r--plugin/special/lookup.go81
-rw-r--r--plugin/special/private.go60
-rw-r--r--plugin/special/stdin.go81
-rw-r--r--plugin/special/stdout.go84
-rw-r--r--plugin/special/test.go20
-rw-r--r--plugin/v2ray/dat_in.go111
-rw-r--r--plugin/v2ray/dat_out.go136
29 files changed, 1220 insertions, 606 deletions
diff --git a/plugin/maxmind/common_in.go b/plugin/maxmind/common_in.go
index 3d97388c..a937ef98 100644
--- a/plugin/maxmind/common_in.go
+++ b/plugin/maxmind/common_in.go
@@ -14,46 +14,78 @@ var (
defaultIPInfoCountryMMDBFile = filepath.Join("./", "ipinfo", "country.mmdb")
)
-func newGeoLite2CountryMMDBIn(iType string, iDesc string, action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
- var tmp struct {
- URI string `json:"uri"`
- Want []string `json:"wantedList"`
- OnlyIPType lib.IPType `json:"onlyIPType"`
+func NewGeoLite2CountryMMDBIn(iType string, iDesc string, action lib.Action, opts ...lib.InputOption) lib.InputConverter {
+ g := &geoLite2CountryMMDBIn{
+ Type: iType,
+ Action: action,
+ Description: iDesc,
}
- if len(data) > 0 {
- if err := json.Unmarshal(data, &tmp); err != nil {
- return nil, err
+ for _, opt := range opts {
+ if opt != nil {
+ opt(g)
}
}
- if tmp.URI == "" {
- switch iType {
- case TypeGeoLite2CountryMMDBIn:
- tmp.URI = defaultGeoLite2CountryMMDBFile
+ return g
+}
+
+func WithGeoLite2CountryMMDBInURI(iType, uri string) lib.InputOption {
+ return func(s lib.InputConverter) {
+ uri = strings.TrimSpace(uri)
+ if uri == "" {
+ switch iType {
+ case TypeGeoLite2CountryMMDBIn:
+ uri = defaultGeoLite2CountryMMDBFile
+ case TypeDBIPCountryMMDBIn:
+ uri = defaultDBIPCountryMMDBFile
+ case TypeIPInfoCountryMMDBIn:
+ uri = defaultIPInfoCountryMMDBFile
+ }
+ }
- case TypeDBIPCountryMMDBIn:
- tmp.URI = defaultDBIPCountryMMDBFile
+ s.(*geoLite2CountryMMDBIn).URI = uri
+ }
+}
- case TypeIPInfoCountryMMDBIn:
- tmp.URI = defaultIPInfoCountryMMDBFile
+func WithGeoLite2CountryMMDBInWantedList(lists []string) lib.InputOption {
+ return func(s lib.InputConverter) {
+ wantList := make(map[string]bool)
+ for _, want := range lists {
+ if want = strings.ToUpper(strings.TrimSpace(want)); want != "" {
+ wantList[want] = true
+ }
}
+
+ s.(*geoLite2CountryMMDBIn).Want = wantList
+ }
+}
+
+func WithGeoLite2CountryMMDBInOnlyIPType(onlyIPType lib.IPType) lib.InputOption {
+ return func(s lib.InputConverter) {
+ s.(*geoLite2CountryMMDBIn).OnlyIPType = onlyIPType
+ }
+}
+
+func NewGeoLite2CountryMMDBInFromBytes(iType string, iDesc string, action lib.Action, data []byte) (lib.InputConverter, error) {
+ var tmp struct {
+ URI string `json:"uri"`
+ Want []string `json:"wantedList"`
+ OnlyIPType lib.IPType `json:"onlyIPType"`
}
- // Filter want list
- wantList := make(map[string]bool)
- for _, want := range tmp.Want {
- if want = strings.ToUpper(strings.TrimSpace(want)); want != "" {
- wantList[want] = true
+ if len(data) > 0 {
+ if err := json.Unmarshal(data, &tmp); err != nil {
+ return nil, err
}
}
- return &GeoLite2CountryMMDBIn{
- Type: iType,
- Action: action,
- Description: iDesc,
- URI: tmp.URI,
- Want: wantList,
- OnlyIPType: tmp.OnlyIPType,
- }, nil
+ return NewGeoLite2CountryMMDBIn(
+ iType,
+ iDesc,
+ action,
+ WithGeoLite2CountryMMDBInURI(iType, tmp.URI),
+ WithGeoLite2CountryMMDBInWantedList(tmp.Want),
+ WithGeoLite2CountryMMDBInOnlyIPType(tmp.OnlyIPType),
+ ), nil
}
diff --git a/plugin/maxmind/common_out.go b/plugin/maxmind/common_out.go
index 21063faa..9b809f74 100644
--- a/plugin/maxmind/common_out.go
+++ b/plugin/maxmind/common_out.go
@@ -100,7 +100,82 @@ func (d dbipCountry) HasData() bool {
return d != zeroDBIPCountry
}
-func newGeoLite2CountryMMDBOut(iType string, iDesc string, action lib.Action, data json.RawMessage) (lib.OutputConverter, error) {
+func NewGeoLite2CountryMMDBOut(iType string, iDesc string, action lib.Action, opts ...lib.OutputOption) lib.OutputConverter {
+ g := &geoLite2CountryMMDBOut{
+ Type: iType,
+ Action: action,
+ Description: iDesc,
+ }
+
+ for _, opt := range opts {
+ if opt != nil {
+ opt(g)
+ }
+ }
+
+ return g
+}
+
+func WithGeoLite2CountryMMDBOutOutputName(name string) lib.OutputOption {
+ return func(s lib.OutputConverter) {
+ name = strings.TrimSpace(name)
+ if name == "" {
+ name = defaultGeoLite2CountryMMDBOutputName
+ }
+
+ s.(*geoLite2CountryMMDBOut).OutputName = name
+ }
+}
+
+func WithGeoLite2CountryMMDBOutOutputDir(iType, dir string) lib.OutputOption {
+ return func(s lib.OutputConverter) {
+ dir = strings.TrimSpace(dir)
+ if dir == "" {
+ switch iType {
+ case TypeGeoLite2CountryMMDBOut:
+ dir = defaultMaxmindOutputDir
+ case TypeDBIPCountryMMDBOut:
+ dir = defaultDBIPOutputDir
+ case TypeIPInfoCountryMMDBOut:
+ dir = defaultIPInfoOutputDir
+ }
+ }
+
+ s.(*geoLite2CountryMMDBOut).OutputDir = dir
+ }
+}
+
+func WithGeoLite2CountryMMDBOutWantedList(lists []string) lib.OutputOption {
+ return func(s lib.OutputConverter) {
+ s.(*geoLite2CountryMMDBOut).Want = lists
+ }
+}
+
+func WithGeoLite2CountryMMDBOutOverwriteList(lists []string) lib.OutputOption {
+ return func(s lib.OutputConverter) {
+ s.(*geoLite2CountryMMDBOut).Overwrite = lists
+ }
+}
+
+func WithGeoLite2CountryMMDBOutExcludedList(lists []string) lib.OutputOption {
+ return func(s lib.OutputConverter) {
+ s.(*geoLite2CountryMMDBOut).Exclude = lists
+ }
+}
+
+func WithGeoLite2CountryMMDBOutOnlyIPType(onlyIPType lib.IPType) lib.OutputOption {
+ return func(s lib.OutputConverter) {
+ s.(*geoLite2CountryMMDBOut).OnlyIPType = onlyIPType
+ }
+}
+
+func WithGeoLite2CountryMMDBOutSourceMMDBURI(uri string) lib.OutputOption {
+ return func(s lib.OutputConverter) {
+ s.(*geoLite2CountryMMDBOut).SourceMMDBURI = uri
+ }
+}
+
+func NewGeoLite2CountryMMDBOutFromBytes(iType string, iDesc string, action lib.Action, data []byte) (lib.OutputConverter, error) {
var tmp struct {
OutputName string `json:"outputName"`
OutputDir string `json:"outputDir"`
@@ -118,39 +193,21 @@ func newGeoLite2CountryMMDBOut(iType string, iDesc string, action lib.Action, da
}
}
- if tmp.OutputName == "" {
- tmp.OutputName = defaultGeoLite2CountryMMDBOutputName
- }
-
- if tmp.OutputDir == "" {
- switch iType {
- case TypeGeoLite2CountryMMDBOut:
- tmp.OutputDir = defaultMaxmindOutputDir
-
- case TypeDBIPCountryMMDBOut:
- tmp.OutputDir = defaultDBIPOutputDir
-
- case TypeIPInfoCountryMMDBOut:
- tmp.OutputDir = defaultIPInfoOutputDir
- }
- }
-
- return &GeoLite2CountryMMDBOut{
- Type: iType,
- Action: action,
- Description: iDesc,
- OutputName: tmp.OutputName,
- OutputDir: tmp.OutputDir,
- Want: tmp.Want,
- Overwrite: tmp.Overwrite,
- Exclude: tmp.Exclude,
- OnlyIPType: tmp.OnlyIPType,
-
- SourceMMDBURI: tmp.SourceMMDBURI,
- }, nil
+ return NewGeoLite2CountryMMDBOut(
+ iType,
+ iDesc,
+ action,
+ WithGeoLite2CountryMMDBOutOutputName(tmp.OutputName),
+ WithGeoLite2CountryMMDBOutOutputDir(iType, tmp.OutputDir),
+ WithGeoLite2CountryMMDBOutWantedList(tmp.Want),
+ WithGeoLite2CountryMMDBOutOverwriteList(tmp.Overwrite),
+ WithGeoLite2CountryMMDBOutExcludedList(tmp.Exclude),
+ WithGeoLite2CountryMMDBOutOnlyIPType(tmp.OnlyIPType),
+ WithGeoLite2CountryMMDBOutSourceMMDBURI(tmp.SourceMMDBURI),
+ ), nil
}
-func (g *GeoLite2CountryMMDBOut) GetExtraInfo() (map[string]any, error) {
+func (g *geoLite2CountryMMDBOut) GetExtraInfo() (map[string]any, error) {
if strings.TrimSpace(g.SourceMMDBURI) == "" {
return nil, nil
}
diff --git a/plugin/maxmind/dbip_country_mmdb_in.go b/plugin/maxmind/dbip_country_mmdb_in.go
index 304f29e5..713b9626 100644
--- a/plugin/maxmind/dbip_country_mmdb_in.go
+++ b/plugin/maxmind/dbip_country_mmdb_in.go
@@ -18,9 +18,9 @@ const (
func init() {
lib.RegisterInputConfigCreator(TypeDBIPCountryMMDBIn, func(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
- return newGeoLite2CountryMMDBIn(TypeDBIPCountryMMDBIn, DescDBIPCountryMMDBIn, action, data)
+ return NewGeoLite2CountryMMDBInFromBytes(TypeDBIPCountryMMDBIn, DescDBIPCountryMMDBIn, action, data)
})
- lib.RegisterInputConverter(TypeDBIPCountryMMDBIn, &GeoLite2CountryMMDBIn{
+ lib.RegisterInputConverter(TypeDBIPCountryMMDBIn, &geoLite2CountryMMDBIn{
Description: DescDBIPCountryMMDBIn,
})
}
diff --git a/plugin/maxmind/dbip_country_mmdb_out.go b/plugin/maxmind/dbip_country_mmdb_out.go
index 77857d95..73d8c5a8 100644
--- a/plugin/maxmind/dbip_country_mmdb_out.go
+++ b/plugin/maxmind/dbip_country_mmdb_out.go
@@ -18,9 +18,9 @@ const (
func init() {
lib.RegisterOutputConfigCreator(TypeDBIPCountryMMDBOut, func(action lib.Action, data json.RawMessage) (lib.OutputConverter, error) {
- return newGeoLite2CountryMMDBOut(TypeDBIPCountryMMDBOut, DescDBIPCountryMMDBOut, action, data)
+ return NewGeoLite2CountryMMDBOutFromBytes(TypeDBIPCountryMMDBOut, DescDBIPCountryMMDBOut, action, data)
})
- lib.RegisterOutputConverter(TypeDBIPCountryMMDBOut, &GeoLite2CountryMMDBOut{
+ lib.RegisterOutputConverter(TypeDBIPCountryMMDBOut, &geoLite2CountryMMDBOut{
Description: DescDBIPCountryMMDBOut,
})
}
diff --git a/plugin/maxmind/ipinfo_country_mmdb_in.go b/plugin/maxmind/ipinfo_country_mmdb_in.go
index 8551e453..c27bafa3 100644
--- a/plugin/maxmind/ipinfo_country_mmdb_in.go
+++ b/plugin/maxmind/ipinfo_country_mmdb_in.go
@@ -18,9 +18,9 @@ const (
func init() {
lib.RegisterInputConfigCreator(TypeIPInfoCountryMMDBIn, func(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
- return newGeoLite2CountryMMDBIn(TypeIPInfoCountryMMDBIn, DescIPInfoCountryMMDBIn, action, data)
+ return NewGeoLite2CountryMMDBInFromBytes(TypeIPInfoCountryMMDBIn, DescIPInfoCountryMMDBIn, action, data)
})
- lib.RegisterInputConverter(TypeIPInfoCountryMMDBIn, &GeoLite2CountryMMDBIn{
+ lib.RegisterInputConverter(TypeIPInfoCountryMMDBIn, &geoLite2CountryMMDBIn{
Description: DescIPInfoCountryMMDBIn,
})
}
diff --git a/plugin/maxmind/ipinfo_country_mmdb_out.go b/plugin/maxmind/ipinfo_country_mmdb_out.go
index bed18af7..e10b4ca6 100644
--- a/plugin/maxmind/ipinfo_country_mmdb_out.go
+++ b/plugin/maxmind/ipinfo_country_mmdb_out.go
@@ -18,9 +18,9 @@ const (
func init() {
lib.RegisterOutputConfigCreator(TypeIPInfoCountryMMDBOut, func(action lib.Action, data json.RawMessage) (lib.OutputConverter, error) {
- return newGeoLite2CountryMMDBOut(TypeIPInfoCountryMMDBOut, DescIPInfoCountryMMDBOut, action, data)
+ return NewGeoLite2CountryMMDBOutFromBytes(TypeIPInfoCountryMMDBOut, DescIPInfoCountryMMDBOut, action, data)
})
- lib.RegisterOutputConverter(TypeIPInfoCountryMMDBOut, &GeoLite2CountryMMDBOut{
+ lib.RegisterOutputConverter(TypeIPInfoCountryMMDBOut, &geoLite2CountryMMDBOut{
Description: DescIPInfoCountryMMDBOut,
})
}
diff --git a/plugin/maxmind/maxmind_asn_csv_in.go b/plugin/maxmind/maxmind_asn_csv_in.go
index d924e3f9..8444277a 100644
--- a/plugin/maxmind/maxmind_asn_csv_in.go
+++ b/plugin/maxmind/maxmind_asn_csv_in.go
@@ -24,101 +24,138 @@ var (
func init() {
lib.RegisterInputConfigCreator(TypeGeoLite2ASNCSVIn, func(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
- return newGeoLite2ASNCSVIn(action, data)
+ return NewGeoLite2ASNCSVInFromBytes(action, data)
})
- lib.RegisterInputConverter(TypeGeoLite2ASNCSVIn, &GeoLite2ASNCSVIn{
+ lib.RegisterInputConverter(TypeGeoLite2ASNCSVIn, &geoLite2ASNCSVIn{
Description: DescGeoLite2ASNCSVIn,
})
}
-func newGeoLite2ASNCSVIn(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
- var tmp struct {
- IPv4File string `json:"ipv4"`
- IPv6File string `json:"ipv6"`
- Want lib.WantedListExtended `json:"wantedList"`
- OnlyIPType lib.IPType `json:"onlyIPType"`
+type geoLite2ASNCSVIn struct {
+ Type string
+ Action lib.Action
+ Description string
+ IPv4File string
+ IPv6File string
+ Want map[string][]string
+ OnlyIPType lib.IPType
+}
+
+func NewGeoLite2ASNCSVIn(action lib.Action, opts ...lib.InputOption) lib.InputConverter {
+ g := &geoLite2ASNCSVIn{
+ Type: TypeGeoLite2ASNCSVIn,
+ Action: action,
+ Description: DescGeoLite2ASNCSVIn,
}
- if len(data) > 0 {
- if err := json.Unmarshal(data, &tmp); err != nil {
- return nil, err
+ for _, opt := range opts {
+ if opt != nil {
+ opt(g)
}
}
- // When both of IP files are not specified,
- // it means user wants to use the default ones
- if tmp.IPv4File == "" && tmp.IPv6File == "" {
- tmp.IPv4File = defaultGeoLite2ASNCSVIPv4File
- tmp.IPv6File = defaultGeoLite2ASNCSVIPv6File
+ return g
+}
+
+func WithGeoLite2ASNCSVInIPv4File(file string) lib.InputOption {
+ return func(s lib.InputConverter) {
+ s.(*geoLite2ASNCSVIn).IPv4File = file
}
+}
+
+func WithGeoLite2ASNCSVInIPv6File(file string) lib.InputOption {
+ return func(s lib.InputConverter) {
+ s.(*geoLite2ASNCSVIn).IPv6File = file
+ }
+}
+
+func WithGeoLite2ASNCSVInWantedList(want lib.WantedListExtended) lib.InputOption {
+ return func(s lib.InputConverter) {
+ wantList := make(map[string][]string)
- // Filter want list
- wantList := make(map[string][]string) // map[asn][]listname or map[asn][]asn
+ for list, asnList := range want.TypeMap {
+ list = strings.ToUpper(strings.TrimSpace(list))
+ if list == "" {
+ continue
+ }
+
+ for _, asn := range asnList {
+ asn = strings.TrimPrefix(strings.ToLower(strings.TrimSpace(asn)), "as")
+ if asn == "" {
+ continue
+ }
- for list, asnList := range tmp.Want.TypeMap {
- list = strings.ToUpper(strings.TrimSpace(list))
- if list == "" {
- continue
+ if listArr, found := wantList[asn]; found {
+ listArr = append(listArr, list)
+ wantList[asn] = listArr
+ } else {
+ wantList[asn] = []string{list}
+ }
+ }
}
- for _, asn := range asnList {
+ for _, asn := range want.TypeSlice {
asn = strings.TrimPrefix(strings.ToLower(strings.TrimSpace(asn)), "as")
if asn == "" {
continue
}
- if listArr, found := wantList[asn]; found {
- listArr = append(listArr, list)
- wantList[asn] = listArr
- } else {
- wantList[asn] = []string{list}
- }
+ wantList[asn] = []string{"AS" + asn}
}
+
+ s.(*geoLite2ASNCSVIn).Want = wantList
}
+}
- for _, asn := range tmp.Want.TypeSlice {
- asn = strings.TrimPrefix(strings.ToLower(strings.TrimSpace(asn)), "as")
- if asn == "" {
- continue
- }
+func WithGeoLite2ASNCSVInOnlyIPType(onlyIPType lib.IPType) lib.InputOption {
+ return func(s lib.InputConverter) {
+ s.(*geoLite2ASNCSVIn).OnlyIPType = onlyIPType
+ }
+}
- wantList[asn] = []string{"AS" + asn}
+func NewGeoLite2ASNCSVInFromBytes(action lib.Action, data []byte) (lib.InputConverter, error) {
+ var tmp struct {
+ IPv4File string `json:"ipv4"`
+ IPv6File string `json:"ipv6"`
+ Want lib.WantedListExtended `json:"wantedList"`
+ OnlyIPType lib.IPType `json:"onlyIPType"`
}
- return &GeoLite2ASNCSVIn{
- Type: TypeGeoLite2ASNCSVIn,
- Action: action,
- Description: DescGeoLite2ASNCSVIn,
- IPv4File: tmp.IPv4File,
- IPv6File: tmp.IPv6File,
- Want: wantList,
- OnlyIPType: tmp.OnlyIPType,
- }, nil
-}
+ if len(data) > 0 {
+ if err := json.Unmarshal(data, &tmp); err != nil {
+ return nil, err
+ }
+ }
-type GeoLite2ASNCSVIn struct {
- Type string
- Action lib.Action
- Description string
- IPv4File string
- IPv6File string
- Want map[string][]string
- OnlyIPType lib.IPType
+ // When both of IP files are not specified,
+ // it means user wants to use the default ones
+ if tmp.IPv4File == "" && tmp.IPv6File == "" {
+ tmp.IPv4File = defaultGeoLite2ASNCSVIPv4File
+ tmp.IPv6File = defaultGeoLite2ASNCSVIPv6File
+ }
+
+ return NewGeoLite2ASNCSVIn(
+ action,
+ WithGeoLite2ASNCSVInIPv4File(tmp.IPv4File),
+ WithGeoLite2ASNCSVInIPv6File(tmp.IPv6File),
+ WithGeoLite2ASNCSVInWantedList(tmp.Want),
+ WithGeoLite2ASNCSVInOnlyIPType(tmp.OnlyIPType),
+ ), nil
}
-func (g *GeoLite2ASNCSVIn) GetType() string {
+func (g *geoLite2ASNCSVIn) GetType() string {
return g.Type
}
-func (g *GeoLite2ASNCSVIn) GetAction() lib.Action {
+func (g *geoLite2ASNCSVIn) GetAction() lib.Action {
return g.Action
}
-func (g *GeoLite2ASNCSVIn) GetDescription() string {
+func (g *geoLite2ASNCSVIn) GetDescription() string {
return g.Description
}
-func (g *GeoLite2ASNCSVIn) Input(container lib.Container) (lib.Container, error) {
+func (g *geoLite2ASNCSVIn) Input(container lib.Container) (lib.Container, error) {
entries := make(map[string]*lib.Entry)
if g.IPv4File != "" {
@@ -157,7 +194,7 @@ func (g *GeoLite2ASNCSVIn) Input(container lib.Container) (lib.Container, error)
return container, nil
}
-func (g *GeoLite2ASNCSVIn) process(file string, entries map[string]*lib.Entry) error {
+func (g *geoLite2ASNCSVIn) process(file string, entries map[string]*lib.Entry) error {
if entries == nil {
entries = make(map[string]*lib.Entry)
}
diff --git a/plugin/maxmind/maxmind_country_csv_in.go b/plugin/maxmind/maxmind_country_csv_in.go
index 93d38c37..f344fc77 100644
--- a/plugin/maxmind/maxmind_country_csv_in.go
+++ b/plugin/maxmind/maxmind_country_csv_in.go
@@ -25,14 +25,78 @@ var (
func init() {
lib.RegisterInputConfigCreator(TypeGeoLite2CountryCSVIn, func(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
- return newGeoLite2CountryCSVIn(action, data)
+ return NewGeoLite2CountryCSVInFromBytes(action, data)
})
- lib.RegisterInputConverter(TypeGeoLite2CountryCSVIn, &GeoLite2CountryCSVIn{
+ lib.RegisterInputConverter(TypeGeoLite2CountryCSVIn, &geoLite2CountryCSVIn{
Description: DescGeoLite2CountryCSVIn,
})
}
-func newGeoLite2CountryCSVIn(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
+type geoLite2CountryCSVIn struct {
+ Type string
+ Action lib.Action
+ Description string
+ CountryCodeFile string
+ IPv4File string
+ IPv6File string
+ Want map[string]bool
+ OnlyIPType lib.IPType
+}
+
+func NewGeoLite2CountryCSVIn(action lib.Action, opts ...lib.InputOption) lib.InputConverter {
+ g := &geoLite2CountryCSVIn{
+ Type: TypeGeoLite2CountryCSVIn,
+ Action: action,
+ Description: DescGeoLite2CountryCSVIn,
+ }
+
+ for _, opt := range opts {
+ if opt != nil {
+ opt(g)
+ }
+ }
+
+ return g
+}
+
+func WithGeoLite2CountryCSVInCountryCodeFile(file string) lib.InputOption {
+ return func(s lib.InputConverter) {
+ s.(*geoLite2CountryCSVIn).CountryCodeFile = file
+ }
+}
+
+func WithGeoLite2CountryCSVInIPv4File(file string) lib.InputOption {
+ return func(s lib.InputConverter) {
+ s.(*geoLite2CountryCSVIn).IPv4File = file
+ }
+}
+
+func WithGeoLite2CountryCSVInIPv6File(file string) lib.InputOption {
+ return func(s lib.InputConverter) {
+ s.(*geoLite2CountryCSVIn).IPv6File = file
+ }
+}
+
+func WithGeoLite2CountryCSVInWantedList(lists []string) lib.InputOption {
+ return func(s lib.InputConverter) {
+ wantList := make(map[string]bool)
+ for _, want := range lists {
+ if want = strings.ToUpper(strings.TrimSpace(want)); want != "" {
+ wantList[want] = true
+ }
+ }
+
+ s.(*geoLite2CountryCSVIn).Want = wantList
+ }
+}
+
+func WithGeoLite2CountryCSVInOnlyIPType(onlyIPType lib.IPType) lib.InputOption {
+ return func(s lib.InputConverter) {
+ s.(*geoLite2CountryCSVIn).OnlyIPType = onlyIPType
+ }
+}
+
+func NewGeoLite2CountryCSVInFromBytes(action lib.Action, data []byte) (lib.InputConverter, error) {
var tmp struct {
CountryCodeFile string `json:"country"`
IPv4File string `json:"ipv4"`
@@ -58,50 +122,29 @@ func newGeoLite2CountryCSVIn(action lib.Action, data json.RawMessage) (lib.Input
tmp.IPv6File = defaultGeoLite2CountryIPv6File
}
- // Filter want list
- wantList := make(map[string]bool)
- for _, want := range tmp.Want {
- if want = strings.ToUpper(strings.TrimSpace(want)); want != "" {
- wantList[want] = true
- }
- }
-
- return &GeoLite2CountryCSVIn{
- Type: TypeGeoLite2CountryCSVIn,
- Action: action,
- Description: DescGeoLite2CountryCSVIn,
- CountryCodeFile: tmp.CountryCodeFile,
- IPv4File: tmp.IPv4File,
- IPv6File: tmp.IPv6File,
- Want: wantList,
- OnlyIPType: tmp.OnlyIPType,
- }, nil
-}
-
-type GeoLite2CountryCSVIn struct {
- Type string
- Action lib.Action
- Description string
- CountryCodeFile string
- IPv4File string
- IPv6File string
- Want map[string]bool
- OnlyIPType lib.IPType
+ return NewGeoLite2CountryCSVIn(
+ action,
+ WithGeoLite2CountryCSVInCountryCodeFile(tmp.CountryCodeFile),
+ WithGeoLite2CountryCSVInIPv4File(tmp.IPv4File),
+ WithGeoLite2CountryCSVInIPv6File(tmp.IPv6File),
+ WithGeoLite2CountryCSVInWantedList(tmp.Want),
+ WithGeoLite2CountryCSVInOnlyIPType(tmp.OnlyIPType),
+ ), nil
}
-func (g *GeoLite2CountryCSVIn) GetType() string {
+func (g *geoLite2CountryCSVIn) GetType() string {
return g.Type
}
-func (g *GeoLite2CountryCSVIn) GetAction() lib.Action {
+func (g *geoLite2CountryCSVIn) GetAction() lib.Action {
return g.Action
}
-func (g *GeoLite2CountryCSVIn) GetDescription() string {
+func (g *geoLite2CountryCSVIn) GetDescription() string {
return g.Description
}
-func (g *GeoLite2CountryCSVIn) Input(container lib.Container) (lib.Container, error) {
+func (g *geoLite2CountryCSVIn) Input(container lib.Container) (lib.Container, error) {
ccMap, err := g.getCountryCode()
if err != nil {
return nil, err
@@ -145,7 +188,7 @@ func (g *GeoLite2CountryCSVIn) Input(container lib.Container) (lib.Container, er
return container, nil
}
-func (g *GeoLite2CountryCSVIn) getCountryCode() (map[string]string, error) {
+func (g *geoLite2CountryCSVIn) getCountryCode() (map[string]string, error) {
var f io.ReadCloser
var err error
switch {
@@ -192,7 +235,7 @@ func (g *GeoLite2CountryCSVIn) getCountryCode() (map[string]string, error) {
return ccMap, nil
}
-func (g *GeoLite2CountryCSVIn) process(file string, ccMap map[string]string, entries map[string]*lib.Entry) error {
+func (g *geoLite2CountryCSVIn) process(file string, ccMap map[string]string, entries map[string]*lib.Entry) error {
if len(ccMap) == 0 {
return fmt.Errorf("❌ [type %s | action %s] invalid country code data", g.Type, g.Action)
}
diff --git a/plugin/maxmind/maxmind_country_mmdb_in.go b/plugin/maxmind/maxmind_country_mmdb_in.go
index 1f6d5386..d9015e34 100644
--- a/plugin/maxmind/maxmind_country_mmdb_in.go
+++ b/plugin/maxmind/maxmind_country_mmdb_in.go
@@ -18,14 +18,14 @@ const (
func init() {
lib.RegisterInputConfigCreator(TypeGeoLite2CountryMMDBIn, func(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
- return newGeoLite2CountryMMDBIn(TypeGeoLite2CountryMMDBIn, DescGeoLite2CountryMMDBIn, action, data)
+ return NewGeoLite2CountryMMDBInFromBytes(TypeGeoLite2CountryMMDBIn, DescGeoLite2CountryMMDBIn, action, data)
})
- lib.RegisterInputConverter(TypeGeoLite2CountryMMDBIn, &GeoLite2CountryMMDBIn{
+ lib.RegisterInputConverter(TypeGeoLite2CountryMMDBIn, &geoLite2CountryMMDBIn{
Description: DescGeoLite2CountryMMDBIn,
})
}
-type GeoLite2CountryMMDBIn struct {
+type geoLite2CountryMMDBIn struct {
Type string
Action lib.Action
Description string
@@ -34,19 +34,19 @@ type GeoLite2CountryMMDBIn struct {
OnlyIPType lib.IPType
}
-func (g *GeoLite2CountryMMDBIn) GetType() string {
+func (g *geoLite2CountryMMDBIn) GetType() string {
return g.Type
}
-func (g *GeoLite2CountryMMDBIn) GetAction() lib.Action {
+func (g *geoLite2CountryMMDBIn) GetAction() lib.Action {
return g.Action
}
-func (g *GeoLite2CountryMMDBIn) GetDescription() string {
+func (g *geoLite2CountryMMDBIn) GetDescription() string {
return g.Description
}
-func (g *GeoLite2CountryMMDBIn) Input(container lib.Container) (lib.Container, error) {
+func (g *geoLite2CountryMMDBIn) Input(container lib.Container) (lib.Container, error) {
var content []byte
var err error
switch {
@@ -89,7 +89,7 @@ func (g *GeoLite2CountryMMDBIn) Input(container lib.Container) (lib.Container, e
return container, nil
}
-func (g *GeoLite2CountryMMDBIn) generateEntries(content []byte, entries map[string]*lib.Entry) error {
+func (g *geoLite2CountryMMDBIn) generateEntries(content []byte, entries map[string]*lib.Entry) error {
db, err := maxminddb.OpenBytes(content)
if err != nil {
return err
diff --git a/plugin/maxmind/maxmind_country_mmdb_out.go b/plugin/maxmind/maxmind_country_mmdb_out.go
index fc72e83c..1bbaad6e 100644
--- a/plugin/maxmind/maxmind_country_mmdb_out.go
+++ b/plugin/maxmind/maxmind_country_mmdb_out.go
@@ -22,14 +22,14 @@ const (
func init() {
lib.RegisterOutputConfigCreator(TypeGeoLite2CountryMMDBOut, func(action lib.Action, data json.RawMessage) (lib.OutputConverter, error) {
- return newGeoLite2CountryMMDBOut(TypeGeoLite2CountryMMDBOut, DescGeoLite2CountryMMDBOut, action, data)
+ return NewGeoLite2CountryMMDBOutFromBytes(TypeGeoLite2CountryMMDBOut, DescGeoLite2CountryMMDBOut, action, data)
})
- lib.RegisterOutputConverter(TypeGeoLite2CountryMMDBOut, &GeoLite2CountryMMDBOut{
+ lib.RegisterOutputConverter(TypeGeoLite2CountryMMDBOut, &geoLite2CountryMMDBOut{
Description: DescGeoLite2CountryMMDBOut,
})
}
-type GeoLite2CountryMMDBOut struct {
+type geoLite2CountryMMDBOut struct {
Type string
Action lib.Action
Description string
@@ -43,19 +43,19 @@ type GeoLite2CountryMMDBOut struct {
SourceMMDBURI string
}
-func (g *GeoLite2CountryMMDBOut) GetType() string {
+func (g *geoLite2CountryMMDBOut) GetType() string {
return g.Type
}
-func (g *GeoLite2CountryMMDBOut) GetAction() lib.Action {
+func (g *geoLite2CountryMMDBOut) GetAction() lib.Action {
return g.Action
}
-func (g *GeoLite2CountryMMDBOut) GetDescription() string {
+func (g *geoLite2CountryMMDBOut) GetDescription() string {
return g.Description
}
-func (g *GeoLite2CountryMMDBOut) Output(container lib.Container) error {
+func (g *geoLite2CountryMMDBOut) Output(container lib.Container) error {
dbName := ""
dbDesc := ""
dbLanguages := []string{"en"}
@@ -119,7 +119,7 @@ func (g *GeoLite2CountryMMDBOut) Output(container lib.Container) error {
return nil
}
-func (g *GeoLite2CountryMMDBOut) filterAndSortList(container lib.Container) []string {
+func (g *geoLite2CountryMMDBOut) filterAndSortList(container lib.Container) []string {
/*
Note: The IPs and/or CIDRs of the latter list will overwrite those of the former one
when duplicated data found due to MaxMind mmdb file format constraint.
@@ -175,7 +175,7 @@ func (g *GeoLite2CountryMMDBOut) filterAndSortList(container lib.Container) []st
return list
}
-func (g *GeoLite2CountryMMDBOut) marshalData(writer *mmdbwriter.Tree, entry *lib.Entry, extraInfo map[string]any) error {
+func (g *geoLite2CountryMMDBOut) marshalData(writer *mmdbwriter.Tree, entry *lib.Entry, extraInfo map[string]any) error {
entryCidr, err := entry.MarshalText(lib.GetIgnoreIPType(g.OnlyIPType))
if err != nil {
return err
@@ -371,7 +371,7 @@ func (g *GeoLite2CountryMMDBOut) marshalData(writer *mmdbwriter.Tree, entry *lib
return nil
}
-func (g *GeoLite2CountryMMDBOut) writeFile(filename string, writer *mmdbwriter.Tree) error {
+func (g *geoLite2CountryMMDBOut) writeFile(filename string, writer *mmdbwriter.Tree) error {
if err := os.MkdirAll(g.OutputDir, 0755); err != nil {
return err
}
diff --git a/plugin/mihomo/mrs_in.go b/plugin/mihomo/mrs_in.go
index cc80f245..e1fb5564 100644
--- a/plugin/mihomo/mrs_in.go
+++ b/plugin/mihomo/mrs_in.go
@@ -6,6 +6,7 @@ import (
"encoding/json"
"fmt"
"io"
+ "log"
"net/http"
"net/netip"
"os"
@@ -27,80 +28,120 @@ const (
func init() {
lib.RegisterInputConfigCreator(TypeMRSIn, func(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
- return newMRSIn(action, data)
+ return NewMRSInFromBytes(action, data)
})
- lib.RegisterInputConverter(TypeMRSIn, &MRSIn{
+ lib.RegisterInputConverter(TypeMRSIn, &mrs_in{
Description: DescMRSIn,
})
}
-func newMRSIn(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
- var tmp struct {
- Name string `json:"name"`
- URI string `json:"uri"`
- InputDir string `json:"inputDir"`
- Want []string `json:"wantedList"`
- OnlyIPType lib.IPType `json:"onlyIPType"`
+type mrs_in struct {
+ Type string
+ Action lib.Action
+ Description string
+ Name string
+ URI string
+ InputDir string
+ Want map[string]bool
+ OnlyIPType lib.IPType
+}
+
+func NewMRSIn(action lib.Action, opts ...lib.InputOption) lib.InputConverter {
+ m := &mrs_in{
+ Type: TypeMRSIn,
+ Action: action,
+ Description: DescMRSIn,
}
- if len(data) > 0 {
- if err := json.Unmarshal(data, &tmp); err != nil {
- return nil, err
+ for _, opt := range opts {
+ if opt != nil {
+ opt(m)
}
}
- if tmp.Name == "" && tmp.URI == "" && tmp.InputDir == "" {
- return nil, fmt.Errorf("❌ [type %s | action %s] missing inputDir or name or uri", TypeMRSIn, action)
+ return m
+}
+
+func WithMRSInNameAndURI(name, uri string) lib.InputOption {
+ return func(s lib.InputConverter) {
+ name = strings.TrimSpace(name)
+ uri = strings.TrimSpace(uri)
+ if (name == "" || uri == "") && strings.TrimSpace(s.(*mrs_in).InputDir) == "" {
+ log.Fatalf("❌ [type %s | action %s] missing name or uri or inputDir", TypeMRSIn, s.(*mrs_in).Action)
+ }
+
+ s.(*mrs_in).Name = name
+ s.(*mrs_in).URI = uri
}
+}
- if (tmp.Name != "" && tmp.URI == "") || (tmp.Name == "" && tmp.URI != "") {
- return nil, fmt.Errorf("❌ [type %s | action %s] name & uri must be specified together", TypeMRSIn, action)
+func WithMRSInInputDir(dir string) lib.InputOption {
+ return func(s lib.InputConverter) {
+ dir = strings.TrimSpace(dir)
+ if dir == "" && (strings.TrimSpace(s.(*mrs_in).Name) == "" || strings.TrimSpace(s.(*mrs_in).URI) == "") {
+ log.Fatalf("❌ [type %s | action %s] missing name or uri or inputDir", TypeMRSIn, s.(*mrs_in).Action)
+ }
+
+ s.(*mrs_in).InputDir = dir
}
+}
- // Filter want list
- wantList := make(map[string]bool)
- for _, want := range tmp.Want {
- if want = strings.ToUpper(strings.TrimSpace(want)); want != "" {
- wantList[want] = true
+func WithMRSInWantedList(lists []string) lib.InputOption {
+ return func(s lib.InputConverter) {
+ wantList := make(map[string]bool)
+ for _, want := range lists {
+ if want = strings.ToUpper(strings.TrimSpace(want)); want != "" {
+ wantList[want] = true
+ }
}
+
+ s.(*mrs_in).Want = wantList
}
+}
- return &MRSIn{
- Type: TypeMRSIn,
- Action: action,
- Description: DescMRSIn,
- Name: tmp.Name,
- URI: tmp.URI,
- InputDir: tmp.InputDir,
- Want: wantList,
- OnlyIPType: tmp.OnlyIPType,
- }, nil
+func WithMRSInOnlyIPType(onlyIPType lib.IPType) lib.InputOption {
+ return func(s lib.InputConverter) {
+ s.(*mrs_in).OnlyIPType = onlyIPType
+ }
}
-type MRSIn struct {
- Type string
- Action lib.Action
- Description string
- Name string
- URI string
- InputDir string
- Want map[string]bool
- OnlyIPType lib.IPType
+func NewMRSInFromBytes(action lib.Action, data []byte) (lib.InputConverter, error) {
+ var tmp struct {
+ Name string `json:"name"`
+ URI string `json:"uri"`
+ InputDir string `json:"inputDir"`
+ Want []string `json:"wantedList"`
+ OnlyIPType lib.IPType `json:"onlyIPType"`
+ }
+
+ if len(data) > 0 {
+ if err := json.Unmarshal(data, &tmp); err != nil {
+ return nil, err
+ }
+ }
+
+ return NewMRSIn(
+ action,
+ WithMRSInNameAndURI(tmp.Name, tmp.URI),
+ WithMRSInInputDir(tmp.InputDir),
+ WithMRSInWantedList(tmp.Want),
+ WithMRSInOnlyIPType(tmp.OnlyIPType),
+ ), nil
}
-func (m *MRSIn) GetType() string {
+func (m *mrs_in) GetType() string {
return m.Type
}
-func (m *MRSIn) GetAction() lib.Action {
+func (m *mrs_in) GetAction() lib.Action {
return m.Action
}
-func (m *MRSIn) GetDescription() string {
+func (m *mrs_in) GetDescription() string {
return m.Description
}
-func (m *MRSIn) Input(container lib.Container) (lib.Container, error) {
+func (m *mrs_in) Input(container lib.Container) (lib.Container, error) {
entries := make(map[string]*lib.Entry)
var err error
@@ -146,7 +187,7 @@ func (m *MRSIn) Input(container lib.Container) (lib.Container, error) {
return container, nil
}
-func (m *MRSIn) walkDir(dir string, entries map[string]*lib.Entry) error {
+func (m *mrs_in) walkDir(dir string, entries map[string]*lib.Entry) error {
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
@@ -165,7 +206,7 @@ func (m *MRSIn) walkDir(dir string, entries map[string]*lib.Entry) error {
return err
}
-func (m *MRSIn) walkLocalFile(path, name string, entries map[string]*lib.Entry) error {
+func (m *mrs_in) walkLocalFile(path, name string, entries map[string]*lib.Entry) error {
entryName := ""
name = strings.TrimSpace(name)
if name != "" {
@@ -203,7 +244,7 @@ func (m *MRSIn) walkLocalFile(path, name string, entries map[string]*lib.Entry)
return nil
}
-func (m *MRSIn) walkRemoteFile(url, name string, entries map[string]*lib.Entry) error {
+func (m *mrs_in) walkRemoteFile(url, name string, entries map[string]*lib.Entry) error {
resp, err := http.Get(url)
if err != nil {
return err
@@ -221,7 +262,7 @@ func (m *MRSIn) walkRemoteFile(url, name string, entries map[string]*lib.Entry)
return nil
}
-func (m *MRSIn) generateEntries(name string, reader io.Reader, entries map[string]*lib.Entry) error {
+func (m *mrs_in) generateEntries(name string, reader io.Reader, entries map[string]*lib.Entry) error {
name = strings.ToUpper(name)
if len(m.Want) > 0 && !m.Want[name] {
@@ -247,7 +288,7 @@ func (m *MRSIn) generateEntries(name string, reader io.Reader, entries map[strin
return nil
}
-func (m *MRSIn) parseMRS(data []byte, entry *lib.Entry) error {
+func (m *mrs_in) parseMRS(data []byte, entry *lib.Entry) error {
reader, err := zstd.NewReader(bytes.NewReader(data))
if err != nil {
return err
diff --git a/plugin/mihomo/mrs_out.go b/plugin/mihomo/mrs_out.go
index e8d65e9f..ab425cec 100644
--- a/plugin/mihomo/mrs_out.go
+++ b/plugin/mihomo/mrs_out.go
@@ -27,14 +27,69 @@ var (
func init() {
lib.RegisterOutputConfigCreator(TypeMRSOut, func(action lib.Action, data json.RawMessage) (lib.OutputConverter, error) {
- return newMRSOut(action, data)
+ return NewMRSOutFromBytes(action, data)
})
- lib.RegisterOutputConverter(TypeMRSOut, &MRSOut{
+ lib.RegisterOutputConverter(TypeMRSOut, &mrs_out{
Description: DescMRSOut,
})
}
-func newMRSOut(action lib.Action, data json.RawMessage) (lib.OutputConverter, error) {
+type mrs_out struct {
+ Type string
+ Action lib.Action
+ Description string
+ OutputDir string
+ Want []string
+ Exclude []string
+ OnlyIPType lib.IPType
+}
+
+func NewMRSOut(action lib.Action, opts ...lib.OutputOption) lib.OutputConverter {
+ m := &mrs_out{
+ Type: TypeMRSOut,
+ Action: action,
+ Description: DescMRSOut,
+ }
+
+ for _, opt := range opts {
+ if opt != nil {
+ opt(m)
+ }
+ }
+
+ return m
+}
+
+func WithMRSOutOutputDir(dir string) lib.OutputOption {
+ return func(s lib.OutputConverter) {
+ dir = strings.TrimSpace(dir)
+ if dir == "" {
+ dir = defaultOutputDir
+ }
+
+ s.(*mrs_out).OutputDir = dir
+ }
+}
+
+func WithMRSOutWantedList(lists []string) lib.OutputOption {
+ return func(s lib.OutputConverter) {
+ s.(*mrs_out).Want = lists
+ }
+}
+
+func WithMRSOutExcludedList(lists []string) lib.OutputOption {
+ return func(s lib.OutputConverter) {
+ s.(*mrs_out).Exclude = lists
+ }
+}
+
+func WithMRSOutOnlyIPType(onlyIPType lib.IPType) lib.OutputOption {
+ return func(s lib.OutputConverter) {
+ s.(*mrs_out).OnlyIPType = onlyIPType
+ }
+}
+
+func NewMRSOutFromBytes(action lib.Action, data []byte) (lib.OutputConverter, error) {
var tmp struct {
OutputDir string `json:"outputDir"`
Want []string `json:"wantedList"`
@@ -48,44 +103,28 @@ func newMRSOut(action lib.Action, data json.RawMessage) (lib.OutputConverter, er
}
}
- if tmp.OutputDir == "" {
- tmp.OutputDir = defaultOutputDir
- }
-
- return &MRSOut{
- Type: TypeMRSOut,
- Action: action,
- Description: DescMRSOut,
- OutputDir: tmp.OutputDir,
- Want: tmp.Want,
- Exclude: tmp.Exclude,
- OnlyIPType: tmp.OnlyIPType,
- }, nil
-}
-
-type MRSOut struct {
- Type string
- Action lib.Action
- Description string
- OutputDir string
- Want []string
- Exclude []string
- OnlyIPType lib.IPType
+ return NewMRSOut(
+ action,
+ WithMRSOutOutputDir(tmp.OutputDir),
+ WithMRSOutWantedList(tmp.Want),
+ WithMRSOutExcludedList(tmp.Exclude),
+ WithMRSOutOnlyIPType(tmp.OnlyIPType),
+ ), nil
}
-func (m *MRSOut) GetType() string {
+func (m *mrs_out) GetType() string {
return m.Type
}
-func (m *MRSOut) GetAction() lib.Action {
+func (m *mrs_out) GetAction() lib.Action {
return m.Action
}
-func (m *MRSOut) GetDescription() string {
+func (m *mrs_out) GetDescription() string {
return m.Description
}
-func (m *MRSOut) Output(container lib.Container) error {
+func (m *mrs_out) Output(container lib.Container) error {
for _, name := range m.filterAndSortList(container) {
entry, found := container.GetEntry(name)
if !found {
@@ -101,7 +140,7 @@ func (m *MRSOut) Output(container lib.Container) error {
return nil
}
-func (m *MRSOut) filterAndSortList(container lib.Container) []string {
+func (m *mrs_out) filterAndSortList(container lib.Container) []string {
excludeMap := make(map[string]bool)
for _, exclude := range m.Exclude {
if exclude = strings.ToUpper(strings.TrimSpace(exclude)); exclude != "" {
@@ -137,7 +176,7 @@ func (m *MRSOut) filterAndSortList(container lib.Container) []string {
return list
}
-func (m *MRSOut) generate(entry *lib.Entry) error {
+func (m *mrs_out) generate(entry *lib.Entry) error {
ipRanges, err := entry.MarshalIPRange(lib.GetIgnoreIPType(m.OnlyIPType))
if err != nil {
return err
@@ -155,7 +194,7 @@ func (m *MRSOut) generate(entry *lib.Entry) error {
return nil
}
-func (m *MRSOut) writeFile(filename string, ipRanges []netipx.IPRange) error {
+func (m *mrs_out) writeFile(filename string, ipRanges []netipx.IPRange) error {
if err := os.MkdirAll(m.OutputDir, 0755); err != nil {
return err
}
@@ -176,7 +215,7 @@ func (m *MRSOut) writeFile(filename string, ipRanges []netipx.IPRange) error {
return nil
}
-func (m *MRSOut) convertToMrs(ipRanges []netipx.IPRange, w io.Writer) (err error) {
+func (m *mrs_out) convertToMrs(ipRanges []netipx.IPRange, w io.Writer) (err error) {
encoder, err := zstd.NewWriter(w)
if err != nil {
return err
diff --git a/plugin/plaintext/clash_in.go b/plugin/plaintext/clash_in.go
index be77cc62..e22156ee 100644
--- a/plugin/plaintext/clash_in.go
+++ b/plugin/plaintext/clash_in.go
@@ -21,16 +21,16 @@ const (
func init() {
lib.RegisterInputConfigCreator(TypeClashRuleSetClassicalIn, func(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
- return newTextIn(TypeClashRuleSetClassicalIn, DescClashRuleSetClassicalIn, action, data)
+ return NewTextInFromBytes(TypeClashRuleSetClassicalIn, DescClashRuleSetClassicalIn, action, data)
})
- lib.RegisterInputConverter(TypeClashRuleSetClassicalIn, &TextIn{
+ lib.RegisterInputConverter(TypeClashRuleSetClassicalIn, &textIn{
Description: DescClashRuleSetClassicalIn,
})
lib.RegisterInputConfigCreator(TypeClashRuleSetIPCIDRIn, func(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
- return newTextIn(TypeClashRuleSetIPCIDRIn, DescClashRuleSetIPCIDRIn, action, data)
+ return NewTextInFromBytes(TypeClashRuleSetIPCIDRIn, DescClashRuleSetIPCIDRIn, action, data)
})
- lib.RegisterInputConverter(TypeClashRuleSetIPCIDRIn, &TextIn{
+ lib.RegisterInputConverter(TypeClashRuleSetIPCIDRIn, &textIn{
Description: DescClashRuleSetIPCIDRIn,
})
}
diff --git a/plugin/plaintext/clash_out.go b/plugin/plaintext/clash_out.go
index e7a97f1b..06dffa04 100644
--- a/plugin/plaintext/clash_out.go
+++ b/plugin/plaintext/clash_out.go
@@ -21,16 +21,16 @@ const (
func init() {
lib.RegisterOutputConfigCreator(TypeClashRuleSetClassicalOut, func(action lib.Action, data json.RawMessage) (lib.OutputConverter, error) {
- return newTextOut(TypeClashRuleSetClassicalOut, DescClashRuleSetClassicalOut, action, data)
+ return NewTextOutFromBytes(TypeClashRuleSetClassicalOut, DescClashRuleSetClassicalOut, action, data)
})
- lib.RegisterOutputConverter(TypeClashRuleSetClassicalOut, &TextOut{
+ lib.RegisterOutputConverter(TypeClashRuleSetClassicalOut, &textOut{
Description: DescClashRuleSetClassicalOut,
})
lib.RegisterOutputConfigCreator(TypeClashRuleSetIPCIDROut, func(action lib.Action, data json.RawMessage) (lib.OutputConverter, error) {
- return newTextOut(TypeClashRuleSetIPCIDROut, DescClashRuleSetIPCIDROut, action, data)
+ return NewTextOutFromBytes(TypeClashRuleSetIPCIDROut, DescClashRuleSetIPCIDROut, action, data)
})
- lib.RegisterOutputConverter(TypeClashRuleSetIPCIDROut, &TextOut{
+ lib.RegisterOutputConverter(TypeClashRuleSetIPCIDROut, &textOut{
Description: DescClashRuleSetIPCIDROut,
})
}
diff --git a/plugin/plaintext/common_in.go b/plugin/plaintext/common_in.go
index f8253519..4f7ea016 100644
--- a/plugin/plaintext/common_in.go
+++ b/plugin/plaintext/common_in.go
@@ -11,7 +11,7 @@ import (
"gopkg.in/yaml.v2"
)
-type TextIn struct {
+type textIn struct {
Type string
Action lib.Action
Description string
@@ -27,7 +27,7 @@ type TextIn struct {
RemoveSuffixesInLine []string
}
-func (t *TextIn) scanFile(reader io.Reader, entry *lib.Entry) error {
+func (t *textIn) scanFile(reader io.Reader, entry *lib.Entry) error {
var err error
switch t.Type {
case TypeTextIn:
@@ -47,7 +47,7 @@ func (t *TextIn) scanFile(reader io.Reader, entry *lib.Entry) error {
return err
}
-func (t *TextIn) scanFileForTextIn(reader io.Reader, entry *lib.Entry) error {
+func (t *textIn) scanFileForTextIn(reader io.Reader, entry *lib.Entry) error {
scanner := bufio.NewScanner(reader)
for scanner.Scan() {
line := scanner.Text()
@@ -83,7 +83,7 @@ func (t *TextIn) scanFileForTextIn(reader io.Reader, entry *lib.Entry) error {
return nil
}
-func (t *TextIn) readClashRuleSetYAMLFile(reader io.Reader) ([]string, error) {
+func (t *textIn) readClashRuleSetYAMLFile(reader io.Reader) ([]string, error) {
var payload struct {
Payload []string `yaml:"payload"`
}
@@ -100,7 +100,7 @@ func (t *TextIn) readClashRuleSetYAMLFile(reader io.Reader) ([]string, error) {
return payload.Payload, nil
}
-func (t *TextIn) scanFileForClashIPCIDRRuleSetIn(reader io.Reader, entry *lib.Entry) error {
+func (t *textIn) scanFileForClashIPCIDRRuleSetIn(reader io.Reader, entry *lib.Entry) error {
payload, err := t.readClashRuleSetYAMLFile(reader)
if err != nil {
return err
@@ -119,7 +119,7 @@ func (t *TextIn) scanFileForClashIPCIDRRuleSetIn(reader io.Reader, entry *lib.En
return nil
}
-func (t *TextIn) scanFileForClashClassicalRuleSetIn(reader io.Reader, entry *lib.Entry) error {
+func (t *textIn) scanFileForClashClassicalRuleSetIn(reader io.Reader, entry *lib.Entry) error {
payload, err := t.readClashRuleSetYAMLFile(reader)
if err != nil {
return err
@@ -154,7 +154,7 @@ func (t *TextIn) scanFileForClashClassicalRuleSetIn(reader io.Reader, entry *lib
return nil
}
-func (t *TextIn) scanFileForSurgeRuleSetIn(reader io.Reader, entry *lib.Entry) error {
+func (t *textIn) scanFileForSurgeRuleSetIn(reader io.Reader, entry *lib.Entry) error {
scanner := bufio.NewScanner(reader)
for scanner.Scan() {
line := scanner.Text()
@@ -193,7 +193,7 @@ func (t *TextIn) scanFileForSurgeRuleSetIn(reader io.Reader, entry *lib.Entry) e
return nil
}
-func (t *TextIn) scanFileForJSONIn(reader io.Reader, entry *lib.Entry) error {
+func (t *textIn) scanFileForJSONIn(reader io.Reader, entry *lib.Entry) error {
data, err := io.ReadAll(reader)
if err != nil {
return err
@@ -217,7 +217,7 @@ func (t *TextIn) scanFileForJSONIn(reader io.Reader, entry *lib.Entry) error {
return nil
}
-func (t *TextIn) processJSONResult(result gjson.Result, entry *lib.Entry) error {
+func (t *textIn) processJSONResult(result gjson.Result, entry *lib.Entry) error {
switch {
case !result.Exists():
return fmt.Errorf("invaild IP address or CIDR (value not exist), please check your specified JSON path or JSON source")
diff --git a/plugin/plaintext/common_out.go b/plugin/plaintext/common_out.go
index cc7ba41e..c6588c51 100644
--- a/plugin/plaintext/common_out.go
+++ b/plugin/plaintext/common_out.go
@@ -7,6 +7,7 @@ import (
"net"
"os"
"path/filepath"
+ "strings"
"github.com/Loyalsoldier/geoip/lib"
)
@@ -18,7 +19,7 @@ var (
defaultOutputDirForSurgeRuleSetOut = filepath.Join("./", "output", "surge")
)
-type TextOut struct {
+type textOut struct {
Type string
Action lib.Action
Description string
@@ -32,7 +33,84 @@ type TextOut struct {
AddSuffixInLine string
}
-func newTextOut(iType string, iDesc string, action lib.Action, data json.RawMessage) (lib.OutputConverter, error) {
+func NewTextOut(iType string, iDesc string, action lib.Action, opts ...lib.OutputOption) lib.OutputConverter {
+ t := &textOut{
+ Type: iType,
+ Action: action,
+ Description: iDesc,
+ }
+
+ for _, opt := range opts {
+ if opt != nil {
+ opt(t)
+ }
+ }
+
+ return t
+}
+
+func WithTextOutOutputDir(iType, dir string) lib.OutputOption {
+ return func(s lib.OutputConverter) {
+ dir = strings.TrimSpace(dir)
+ if dir == "" {
+ switch iType {
+ case TypeTextOut:
+ dir = defaultOutputDirForTextOut
+ case TypeClashRuleSetClassicalOut:
+ dir = defaultOutputDirForClashRuleSetClassicalOut
+ case TypeClashRuleSetIPCIDROut:
+ dir = defaultOutputDirForClashRuleSetIPCIDROut
+ case TypeSurgeRuleSetOut:
+ dir = defaultOutputDirForSurgeRuleSetOut
+ }
+ }
+
+ s.(*textOut).OutputDir = dir
+ }
+}
+
+func WithTextOutOutputExt(ext string) lib.OutputOption {
+ return func(s lib.OutputConverter) {
+ ext = strings.TrimSpace(ext)
+ if ext == "" {
+ ext = ".txt"
+ }
+
+ s.(*textOut).OutputExt = ext
+ }
+}
+
+func WithTextOutWantedList(lists []string) lib.OutputOption {
+ return func(s lib.OutputConverter) {
+ s.(*textOut).Want = lists
+ }
+}
+
+func WithTextOutExcludedList(lists []string) lib.OutputOption {
+ return func(s lib.OutputConverter) {
+ s.(*textOut).Exclude = lists
+ }
+}
+
+func WithTextOutOnlyIPType(onlyIPType lib.IPType) lib.OutputOption {
+ return func(s lib.OutputConverter) {
+ s.(*textOut).OnlyIPType = onlyIPType
+ }
+}
+
+func WithTextOutAddPrefixInLine(prefix string) lib.OutputOption {
+ return func(s lib.OutputConverter) {
+ s.(*textOut).AddPrefixInLine = prefix
+ }
+}
+
+func WithTextOutAddSuffixInLine(suffix string) lib.OutputOption {
+ return func(s lib.OutputConverter) {
+ s.(*textOut).AddSuffixInLine = suffix
+ }
+}
+
+func NewTextOutFromBytes(iType string, iDesc string, action lib.Action, data []byte) (lib.OutputConverter, error) {
var tmp struct {
OutputDir string `json:"outputDir"`
OutputExt string `json:"outputExtension"`
@@ -50,39 +128,21 @@ func newTextOut(iType string, iDesc string, action lib.Action, data json.RawMess
}
}
- if tmp.OutputDir == "" {
- switch iType {
- case TypeTextOut:
- tmp.OutputDir = defaultOutputDirForTextOut
- case TypeClashRuleSetClassicalOut:
- tmp.OutputDir = defaultOutputDirForClashRuleSetClassicalOut
- case TypeClashRuleSetIPCIDROut:
- tmp.OutputDir = defaultOutputDirForClashRuleSetIPCIDROut
- case TypeSurgeRuleSetOut:
- tmp.OutputDir = defaultOutputDirForSurgeRuleSetOut
- }
- }
-
- if tmp.OutputExt == "" {
- tmp.OutputExt = ".txt"
- }
-
- return &TextOut{
- Type: iType,
- Action: action,
- Description: iDesc,
- OutputDir: tmp.OutputDir,
- OutputExt: tmp.OutputExt,
- Want: tmp.Want,
- Exclude: tmp.Exclude,
- OnlyIPType: tmp.OnlyIPType,
-
- AddPrefixInLine: tmp.AddPrefixInLine,
- AddSuffixInLine: tmp.AddSuffixInLine,
- }, nil
+ return NewTextOut(
+ iType,
+ iDesc,
+ action,
+ WithTextOutOutputDir(iType, tmp.OutputDir),
+ WithTextOutOutputExt(tmp.OutputExt),
+ WithTextOutWantedList(tmp.Want),
+ WithTextOutExcludedList(tmp.Exclude),
+ WithTextOutOnlyIPType(tmp.OnlyIPType),
+ WithTextOutAddPrefixInLine(tmp.AddPrefixInLine),
+ WithTextOutAddSuffixInLine(tmp.AddSuffixInLine),
+ ), nil
}
-func (t *TextOut) marshalBytes(entry *lib.Entry) ([]byte, error) {
+func (t *textOut) marshalBytes(entry *lib.Entry) ([]byte, error) {
entryCidr, err := entry.MarshalText(lib.GetIgnoreIPType(t.OnlyIPType))
if err != nil {
return nil, err
@@ -108,7 +168,7 @@ func (t *TextOut) marshalBytes(entry *lib.Entry) ([]byte, error) {
return buf.Bytes(), nil
}
-func (t *TextOut) marshalBytesForTextOut(buf *bytes.Buffer, entryCidr []string) error {
+func (t *textOut) marshalBytesForTextOut(buf *bytes.Buffer, entryCidr []string) error {
for _, cidr := range entryCidr {
if t.AddPrefixInLine != "" {
buf.WriteString(t.AddPrefixInLine)
@@ -122,7 +182,7 @@ func (t *TextOut) marshalBytesForTextOut(buf *bytes.Buffer, entryCidr []string)
return nil
}
-func (t *TextOut) marshalBytesForClashRuleSetClassicalOut(buf *bytes.Buffer, entryCidr []string) error {
+func (t *textOut) marshalBytesForClashRuleSetClassicalOut(buf *bytes.Buffer, entryCidr []string) error {
buf.WriteString("payload:\n")
for _, cidr := range entryCidr {
ip, _, err := net.ParseCIDR(cidr)
@@ -141,7 +201,7 @@ func (t *TextOut) marshalBytesForClashRuleSetClassicalOut(buf *bytes.Buffer, ent
return nil
}
-func (t *TextOut) marshalBytesForClashRuleSetIPCIDROut(buf *bytes.Buffer, entryCidr []string) error {
+func (t *textOut) marshalBytesForClashRuleSetIPCIDROut(buf *bytes.Buffer, entryCidr []string) error {
buf.WriteString("payload:\n")
for _, cidr := range entryCidr {
buf.WriteString(" - '")
@@ -152,7 +212,7 @@ func (t *TextOut) marshalBytesForClashRuleSetIPCIDROut(buf *bytes.Buffer, entryC
return nil
}
-func (t *TextOut) marshalBytesForSurgeRuleSetOut(buf *bytes.Buffer, entryCidr []string) error {
+func (t *textOut) marshalBytesForSurgeRuleSetOut(buf *bytes.Buffer, entryCidr []string) error {
for _, cidr := range entryCidr {
ip, _, err := net.ParseCIDR(cidr)
if err != nil {
@@ -173,7 +233,7 @@ func (t *TextOut) marshalBytesForSurgeRuleSetOut(buf *bytes.Buffer, entryCidr []
return nil
}
-func (t *TextOut) writeFile(filename string, data []byte) error {
+func (t *textOut) writeFile(filename string, data []byte) error {
if err := os.MkdirAll(t.OutputDir, 0755); err != nil {
return err
}
diff --git a/plugin/plaintext/json_in.go b/plugin/plaintext/json_in.go
index a4643609..9edf221a 100644
--- a/plugin/plaintext/json_in.go
+++ b/plugin/plaintext/json_in.go
@@ -13,10 +13,10 @@ const (
func init() {
lib.RegisterInputConfigCreator(TypeJSONIn, func(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
- return newTextIn(TypeJSONIn, DescJSONIn, action, data)
+ return NewTextInFromBytes(TypeJSONIn, DescJSONIn, action, data)
})
- lib.RegisterInputConverter(TypeJSONIn, &TextIn{
+ lib.RegisterInputConverter(TypeJSONIn, &textIn{
Description: DescJSONIn,
})
}
diff --git a/plugin/plaintext/surge_in.go b/plugin/plaintext/surge_in.go
index 7621d0a1..a757cbf7 100644
--- a/plugin/plaintext/surge_in.go
+++ b/plugin/plaintext/surge_in.go
@@ -18,9 +18,9 @@ const (
func init() {
lib.RegisterInputConfigCreator(TypeSurgeRuleSetIn, func(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
- return newTextIn(TypeSurgeRuleSetIn, DescSurgeRuleSetIn, action, data)
+ return NewTextInFromBytes(TypeSurgeRuleSetIn, DescSurgeRuleSetIn, action, data)
})
- lib.RegisterInputConverter(TypeSurgeRuleSetIn, &TextIn{
+ lib.RegisterInputConverter(TypeSurgeRuleSetIn, &textIn{
Description: DescSurgeRuleSetIn,
})
}
diff --git a/plugin/plaintext/surge_out.go b/plugin/plaintext/surge_out.go
index 8fe62fb4..578b440b 100644
--- a/plugin/plaintext/surge_out.go
+++ b/plugin/plaintext/surge_out.go
@@ -18,9 +18,9 @@ const (
func init() {
lib.RegisterOutputConfigCreator(TypeSurgeRuleSetOut, func(action lib.Action, data json.RawMessage) (lib.OutputConverter, error) {
- return newTextOut(TypeSurgeRuleSetOut, DescSurgeRuleSetOut, action, data)
+ return NewTextOutFromBytes(TypeSurgeRuleSetOut, DescSurgeRuleSetOut, action, data)
})
- lib.RegisterOutputConverter(TypeSurgeRuleSetOut, &TextOut{
+ lib.RegisterOutputConverter(TypeSurgeRuleSetOut, &textOut{
Description: DescSurgeRuleSetOut,
})
}
diff --git a/plugin/plaintext/text_in.go b/plugin/plaintext/text_in.go
index a75b1b12..88677ad0 100644
--- a/plugin/plaintext/text_in.go
+++ b/plugin/plaintext/text_in.go
@@ -3,6 +3,7 @@ package plaintext
import (
"encoding/json"
"fmt"
+ "log"
"net/http"
"os"
"path/filepath"
@@ -19,14 +20,118 @@ const (
func init() {
lib.RegisterInputConfigCreator(TypeTextIn, func(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
- return newTextIn(TypeTextIn, DescTextIn, action, data)
+ return NewTextInFromBytes(TypeTextIn, DescTextIn, action, data)
})
- lib.RegisterInputConverter(TypeTextIn, &TextIn{
+ lib.RegisterInputConverter(TypeTextIn, &textIn{
Description: DescTextIn,
})
}
-func newTextIn(iType string, iDesc string, action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
+func NewTextIn(iType string, iDesc string, action lib.Action, opts ...lib.InputOption) lib.InputConverter {
+ t := &textIn{
+ Type: iType,
+ Action: action,
+ Description: iDesc,
+ }
+
+ for _, opt := range opts {
+ if opt != nil {
+ opt(t)
+ }
+ }
+
+ return t
+}
+
+func WithTextInNameAndURI(iType, name, uri string) lib.InputOption {
+ return func(s lib.InputConverter) {
+ t := s.(*textIn)
+ name = strings.TrimSpace(name)
+ uri = strings.TrimSpace(uri)
+
+ if strings.TrimSpace(t.InputDir) == "" {
+ if name == "" {
+ log.Fatalf("❌ [type %s | action %s] missing inputDir or name", iType, t.Action)
+ }
+ if uri == "" && len(t.IPOrCIDR) == 0 {
+ log.Fatalf("❌ [type %s | action %s] missing uri or ipOrCIDR", iType, t.Action)
+ }
+ } else if name != "" || uri != "" {
+ log.Fatalf("❌ [type %s | action %s] inputDir is not allowed to be used with name or uri or ipOrCIDR", iType, t.Action)
+ }
+
+ t.Name = name
+ t.URI = uri
+ }
+}
+
+func WithTextInIPOrCIDR(iType string, ipOrCIDR []string) lib.InputOption {
+ return func(s lib.InputConverter) {
+ t := s.(*textIn)
+ if iType != TypeTextIn && len(ipOrCIDR) > 0 {
+ log.Fatalf("❌ [type %s | action %s] ipOrCIDR is invalid for this input format", iType, t.Action)
+ }
+
+ t.IPOrCIDR = ipOrCIDR
+ }
+}
+
+func WithTextInInputDir(iType, dir string) lib.InputOption {
+ return func(s lib.InputConverter) {
+ t := s.(*textIn)
+ dir = strings.TrimSpace(dir)
+
+ if dir != "" && (strings.TrimSpace(t.Name) != "" || strings.TrimSpace(t.URI) != "" || len(t.IPOrCIDR) > 0) {
+ log.Fatalf("❌ [type %s | action %s] inputDir is not allowed to be used with name or uri or ipOrCIDR", iType, t.Action)
+ }
+
+ t.InputDir = dir
+ }
+}
+
+func WithTextInWantedList(lists []string) lib.InputOption {
+ return func(s lib.InputConverter) {
+ wantList := make(map[string]bool)
+ for _, want := range lists {
+ if want = strings.ToUpper(strings.TrimSpace(want)); want != "" {
+ wantList[want] = true
+ }
+ }
+
+ s.(*textIn).Want = wantList
+ }
+}
+
+func WithTextInOnlyIPType(onlyIPType lib.IPType) lib.InputOption {
+ return func(s lib.InputConverter) {
+ s.(*textIn).OnlyIPType = onlyIPType
+ }
+}
+
+func WithTextInJSONPath(iType string, paths []string) lib.InputOption {
+ return func(s lib.InputConverter) {
+ t := s.(*textIn)
+ if iType == TypeJSONIn && len(paths) == 0 {
+ log.Fatalf("❌ [type %s | action %s] missing jsonPath", iType, t.Action)
+ }
+
+ t.JSONPath = paths
+ }
+}
+
+func WithTextInRemovePrefixesInLine(prefixes []string) lib.InputOption {
+ return func(s lib.InputConverter) {
+ s.(*textIn).RemovePrefixesInLine = prefixes
+ }
+}
+
+func WithTextInRemoveSuffixesInLine(suffixes []string) lib.InputOption {
+ return func(s lib.InputConverter) {
+ s.(*textIn).RemoveSuffixesInLine = suffixes
+ }
+}
+
+func NewTextInFromBytes(iType string, iDesc string, action lib.Action, data []byte) (lib.InputConverter, error) {
var tmp struct {
Name string `json:"name"`
URI string `json:"uri"`
@@ -50,63 +155,34 @@ func newTextIn(iType string, iDesc string, action lib.Action, data json.RawMessa
}
}
- if iType != TypeTextIn && len(tmp.IPOrCIDR) > 0 {
- return nil, fmt.Errorf("❌ [type %s | action %s] ipOrCIDR is invalid for this input format", iType, action)
- }
-
- if iType == TypeJSONIn && len(tmp.JSONPath) == 0 {
- return nil, fmt.Errorf("❌ [type %s | action %s] missing jsonPath", iType, action)
- }
-
- if tmp.InputDir == "" {
- if tmp.Name == "" {
- return nil, fmt.Errorf("❌ [type %s | action %s] missing inputDir or name", iType, action)
- }
- if tmp.URI == "" && len(tmp.IPOrCIDR) == 0 {
- return nil, fmt.Errorf("❌ [type %s | action %s] missing uri or ipOrCIDR", iType, action)
- }
- } else if tmp.Name != "" || tmp.URI != "" || len(tmp.IPOrCIDR) > 0 {
- return nil, fmt.Errorf("❌ [type %s | action %s] inputDir is not allowed to be used with name or uri or ipOrCIDR", iType, action)
- }
-
- // Filter want list
- wantList := make(map[string]bool)
- for _, want := range tmp.Want {
- if want = strings.ToUpper(strings.TrimSpace(want)); want != "" {
- wantList[want] = true
- }
- }
-
- return &TextIn{
- Type: iType,
- Action: action,
- Description: iDesc,
- Name: tmp.Name,
- URI: tmp.URI,
- IPOrCIDR: tmp.IPOrCIDR,
- InputDir: tmp.InputDir,
- Want: wantList,
- OnlyIPType: tmp.OnlyIPType,
-
- JSONPath: tmp.JSONPath,
- RemovePrefixesInLine: tmp.RemovePrefixesInLine,
- RemoveSuffixesInLine: tmp.RemoveSuffixesInLine,
- }, nil
+ return NewTextIn(
+ iType,
+ iDesc,
+ action,
+ WithTextInIPOrCIDR(iType, tmp.IPOrCIDR),
+ WithTextInInputDir(iType, tmp.InputDir),
+ WithTextInNameAndURI(iType, tmp.Name, tmp.URI),
+ WithTextInWantedList(tmp.Want),
+ WithTextInOnlyIPType(tmp.OnlyIPType),
+ WithTextInJSONPath(iType, tmp.JSONPath),
+ WithTextInRemovePrefixesInLine(tmp.RemovePrefixesInLine),
+ WithTextInRemoveSuffixesInLine(tmp.RemoveSuffixesInLine),
+ ), nil
}
-func (t *TextIn) GetType() string {
+func (t *textIn) GetType() string {
return t.Type
}
-func (t *TextIn) GetAction() lib.Action {
+func (t *textIn) GetAction() lib.Action {
return t.Action
}
-func (t *TextIn) GetDescription() string {
+func (t *textIn) GetDescription() string {
return t.Description
}
-func (t *TextIn) Input(container lib.Container) (lib.Container, error) {
+func (t *textIn) Input(container lib.Container) (lib.Container, error) {
entries := make(map[string]*lib.Entry)
var err error
@@ -162,7 +238,7 @@ func (t *TextIn) Input(container lib.Container) (lib.Container, error) {
return container, nil
}
-func (t *TextIn) walkDir(dir string, entries map[string]*lib.Entry) error {
+func (t *textIn) walkDir(dir string, entries map[string]*lib.Entry) error {
err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
@@ -181,7 +257,7 @@ func (t *TextIn) walkDir(dir string, entries map[string]*lib.Entry) error {
return err
}
-func (t *TextIn) walkLocalFile(path, name string, entries map[string]*lib.Entry) error {
+func (t *textIn) walkLocalFile(path, name string, entries map[string]*lib.Entry) error {
entryName := ""
name = strings.TrimSpace(name)
if name != "" {
@@ -225,7 +301,7 @@ func (t *TextIn) walkLocalFile(path, name string, entries map[string]*lib.Entry)
return nil
}
-func (t *TextIn) walkRemoteFile(url, name string, entries map[string]*lib.Entry) error {
+func (t *textIn) walkRemoteFile(url, name string, entries map[string]*lib.Entry) error {
resp, err := http.Get(url)
if err != nil {
return err
@@ -252,7 +328,7 @@ func (t *TextIn) walkRemoteFile(url, name string, entries map[string]*lib.Entry)
return nil
}
-func (t *TextIn) appendIPOrCIDR(ipOrCIDR []string, name string, entries map[string]*lib.Entry) error {
+func (t *textIn) appendIPOrCIDR(ipOrCIDR []string, name string, entries map[string]*lib.Entry) error {
name = strings.ToUpper(name)
entry, found := entries[name]
diff --git a/plugin/plaintext/text_out.go b/plugin/plaintext/text_out.go
index 06f4df63..036ccf95 100644
--- a/plugin/plaintext/text_out.go
+++ b/plugin/plaintext/text_out.go
@@ -16,26 +16,26 @@ const (
func init() {
lib.RegisterOutputConfigCreator(TypeTextOut, func(action lib.Action, data json.RawMessage) (lib.OutputConverter, error) {
- return newTextOut(TypeTextOut, DescTextOut, action, data)
+ return NewTextOutFromBytes(TypeTextOut, DescTextOut, action, data)
})
- lib.RegisterOutputConverter(TypeTextOut, &TextOut{
+ lib.RegisterOutputConverter(TypeTextOut, &textOut{
Description: DescTextOut,
})
}
-func (t *TextOut) GetType() string {
+func (t *textOut) GetType() string {
return t.Type
}
-func (t *TextOut) GetAction() lib.Action {
+func (t *textOut) GetAction() lib.Action {
return t.Action
}
-func (t *TextOut) GetDescription() string {
+func (t *textOut) GetDescription() string {
return t.Description
}
-func (t *TextOut) Output(container lib.Container) error {
+func (t *textOut) Output(container lib.Container) error {
for _, name := range t.filterAndSortList(container) {
entry, found := container.GetEntry(name)
if !found {
@@ -57,7 +57,7 @@ func (t *TextOut) Output(container lib.Container) error {
return nil
}
-func (t *TextOut) filterAndSortList(container lib.Container) []string {
+func (t *textOut) filterAndSortList(container lib.Container) []string {
excludeMap := make(map[string]bool)
for _, exclude := range t.Exclude {
if exclude = strings.ToUpper(strings.TrimSpace(exclude)); exclude != "" {
diff --git a/plugin/special/cutter.go b/plugin/special/cutter.go
index 509cddbb..589fc3f2 100644
--- a/plugin/special/cutter.go
+++ b/plugin/special/cutter.go
@@ -3,6 +3,7 @@ package special
import (
"encoding/json"
"fmt"
+ "log"
"strings"
"github.com/Loyalsoldier/geoip/lib"
@@ -15,14 +16,61 @@ const (
func init() {
lib.RegisterInputConfigCreator(TypeCutter, func(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
- return newCutter(action, data)
+ return NewCutterFromBytes(action, data)
})
- lib.RegisterInputConverter(TypeCutter, &Cutter{
+ lib.RegisterInputConverter(TypeCutter, &cutter{
Description: DescCutter,
})
}
-func newCutter(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
+type cutter struct {
+ Type string
+ Action lib.Action
+ Description string
+ Want map[string]bool
+ OnlyIPType lib.IPType
+}
+
+func NewCutter(action lib.Action, opts ...lib.InputOption) lib.InputConverter {
+ c := &cutter{
+ Type: TypeCutter,
+ Action: action,
+ Description: DescCutter,
+ }
+
+ for _, opt := range opts {
+ if opt != nil {
+ opt(c)
+ }
+ }
+
+ return c
+}
+
+func WithCutterWantedList(lists []string) lib.InputOption {
+ return func(s lib.InputConverter) {
+ wantList := make(map[string]bool)
+ for _, want := range lists {
+ if want = strings.ToUpper(strings.TrimSpace(want)); want != "" {
+ wantList[want] = true
+ }
+ }
+
+ if len(wantList) == 0 {
+ log.Fatalf("❌ [type %s] wantedList must be specified", TypeCutter)
+ }
+
+ s.(*cutter).Want = wantList
+ }
+}
+
+func WithCutterOnlyIPType(onlyIPType lib.IPType) lib.InputOption {
+ return func(s lib.InputConverter) {
+ s.(*cutter).OnlyIPType = onlyIPType
+ }
+}
+
+func NewCutterFromBytes(action lib.Action, data []byte) (lib.InputConverter, error) {
var tmp struct {
Want []string `json:"wantedList"`
OnlyIPType lib.IPType `json:"onlyIPType"`
@@ -38,48 +86,26 @@ func newCutter(action lib.Action, data json.RawMessage) (lib.InputConverter, err
return nil, fmt.Errorf("❌ [type %s] only supports `remove` action", TypeCutter)
}
- // Filter want list
- wantList := make(map[string]bool)
- for _, want := range tmp.Want {
- if want = strings.ToUpper(strings.TrimSpace(want)); want != "" {
- wantList[want] = true
- }
- }
-
- if len(wantList) == 0 {
- return nil, fmt.Errorf("❌ [type %s] wantedList must be specified", TypeCutter)
- }
-
- return &Cutter{
- Type: TypeCutter,
- Action: action,
- Description: DescCutter,
- Want: wantList,
- OnlyIPType: tmp.OnlyIPType,
- }, nil
-}
-
-type Cutter struct {
- Type string
- Action lib.Action
- Description string
- Want map[string]bool
- OnlyIPType lib.IPType
+ return NewCutter(
+ action,
+ WithCutterWantedList(tmp.Want),
+ WithCutterOnlyIPType(tmp.OnlyIPType),
+ ), nil
}
-func (c *Cutter) GetType() string {
+func (c *cutter) GetType() string {
return c.Type
}
-func (c *Cutter) GetAction() lib.Action {
+func (c *cutter) GetAction() lib.Action {
return c.Action
}
-func (c *Cutter) GetDescription() string {
+func (c *cutter) GetDescription() string {
return c.Description
}
-func (c *Cutter) Input(container lib.Container) (lib.Container, error) {
+func (c *cutter) Input(container lib.Container) (lib.Container, error) {
ignoreIPType := lib.GetIgnoreIPType(c.OnlyIPType)
for entry := range container.Loop() {
diff --git a/plugin/special/lookup.go b/plugin/special/lookup.go
index 96735146..ddf96ecd 100644
--- a/plugin/special/lookup.go
+++ b/plugin/special/lookup.go
@@ -4,6 +4,7 @@ import (
"encoding/json"
"errors"
"fmt"
+ "log"
"net/netip"
"slices"
"strings"
@@ -18,14 +19,55 @@ const (
func init() {
lib.RegisterOutputConfigCreator(TypeLookup, func(action lib.Action, data json.RawMessage) (lib.OutputConverter, error) {
- return newLookup(action, data)
+ return NewLookupFromBytes(action, data)
})
- lib.RegisterOutputConverter(TypeLookup, &Lookup{
+ lib.RegisterOutputConverter(TypeLookup, &lookup{
Description: DescLookup,
})
}
-func newLookup(action lib.Action, data json.RawMessage) (lib.OutputConverter, error) {
+type lookup struct {
+ Type string
+ Action lib.Action
+ Description string
+ Search string
+ SearchList []string
+}
+
+func NewLookup(action lib.Action, opts ...lib.OutputOption) lib.OutputConverter {
+ l := &lookup{
+ Type: TypeLookup,
+ Action: action,
+ Description: DescLookup,
+ }
+
+ for _, opt := range opts {
+ if opt != nil {
+ opt(l)
+ }
+ }
+
+ return l
+}
+
+func WithLookupSearch(search string) lib.OutputOption {
+ return func(s lib.OutputConverter) {
+ search = strings.TrimSpace(search)
+ if search == "" {
+ log.Fatalf("❌ [type %s | action %s] please specify an IP or a CIDR as search target", TypeLookup, s.(*lookup).Action)
+ }
+
+ s.(*lookup).Search = search
+ }
+}
+
+func WithLookupSearchList(lists []string) lib.OutputOption {
+ return func(s lib.OutputConverter) {
+ s.(*lookup).SearchList = lists
+ }
+}
+
+func NewLookupFromBytes(action lib.Action, data []byte) (lib.OutputConverter, error) {
var tmp struct {
Search string `json:"search"`
SearchList []string `json:"searchList"`
@@ -37,41 +79,26 @@ func newLookup(action lib.Action, data json.RawMessage) (lib.OutputConverter, er
}
}
- tmp.Search = strings.TrimSpace(tmp.Search)
- if tmp.Search == "" {
- return nil, fmt.Errorf("❌ [type %s | action %s] please specify an IP or a CIDR as search target", TypeLookup, action)
- }
-
- return &Lookup{
- Type: TypeLookup,
- Action: action,
- Description: DescLookup,
- Search: tmp.Search,
- SearchList: tmp.SearchList,
- }, nil
-}
-
-type Lookup struct {
- Type string
- Action lib.Action
- Description string
- Search string
- SearchList []string
+ return NewLookup(
+ action,
+ WithLookupSearch(tmp.Search),
+ WithLookupSearchList(tmp.SearchList),
+ ), nil
}
-func (l *Lookup) GetType() string {
+func (l *lookup) GetType() string {
return l.Type
}
-func (l *Lookup) GetAction() lib.Action {
+func (l *lookup) GetAction() lib.Action {
return l.Action
}
-func (l *Lookup) GetDescription() string {
+func (l *lookup) GetDescription() string {
return l.Description
}
-func (l *Lookup) Output(container lib.Container) error {
+func (l *lookup) Output(container lib.Container) error {
switch strings.Contains(l.Search, "/") {
case true: // CIDR
if _, err := netip.ParsePrefix(l.Search); err != nil {
diff --git a/plugin/special/private.go b/plugin/special/private.go
index 1bc170bc..eee31bd7 100644
--- a/plugin/special/private.go
+++ b/plugin/special/private.go
@@ -38,14 +38,43 @@ var privateCIDRs = []string{
func init() {
lib.RegisterInputConfigCreator(TypePrivate, func(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
- return newPrivate(action, data)
+ return NewPrivateFromBytes(action, data)
})
- lib.RegisterInputConverter(TypePrivate, &Private{
+ lib.RegisterInputConverter(TypePrivate, &private{
Description: DescPrivate,
})
}
-func newPrivate(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
+type private struct {
+ Type string
+ Action lib.Action
+ Description string
+ OnlyIPType lib.IPType
+}
+
+func NewPrivate(action lib.Action, opts ...lib.InputOption) lib.InputConverter {
+ p := &private{
+ Type: TypePrivate,
+ Action: action,
+ Description: DescPrivate,
+ }
+
+ for _, opt := range opts {
+ if opt != nil {
+ opt(p)
+ }
+ }
+
+ return p
+}
+
+func WithPrivateOnlyIPType(onlyIPType lib.IPType) lib.InputOption {
+ return func(s lib.InputConverter) {
+ s.(*private).OnlyIPType = onlyIPType
+ }
+}
+
+func NewPrivateFromBytes(action lib.Action, data []byte) (lib.InputConverter, error) {
var tmp struct {
OnlyIPType lib.IPType `json:"onlyIPType"`
}
@@ -56,34 +85,25 @@ func newPrivate(action lib.Action, data json.RawMessage) (lib.InputConverter, er
}
}
- return &Private{
- Type: TypePrivate,
- Action: action,
- Description: DescPrivate,
- OnlyIPType: tmp.OnlyIPType,
- }, nil
-}
-
-type Private struct {
- Type string
- Action lib.Action
- Description string
- OnlyIPType lib.IPType
+ return NewPrivate(
+ action,
+ WithPrivateOnlyIPType(tmp.OnlyIPType),
+ ), nil
}
-func (p *Private) GetType() string {
+func (p *private) GetType() string {
return p.Type
}
-func (p *Private) GetAction() lib.Action {
+func (p *private) GetAction() lib.Action {
return p.Action
}
-func (p *Private) GetDescription() string {
+func (p *private) GetDescription() string {
return p.Description
}
-func (p *Private) Input(container lib.Container) (lib.Container, error) {
+func (p *private) Input(container lib.Container) (lib.Container, error) {
entry, found := container.GetEntry(entryNamePrivate)
if !found {
entry = lib.NewEntry(entryNamePrivate)
diff --git a/plugin/special/stdin.go b/plugin/special/stdin.go
index f2f9cf9c..459bdf9f 100644
--- a/plugin/special/stdin.go
+++ b/plugin/special/stdin.go
@@ -3,7 +3,7 @@ package special
import (
"bufio"
"encoding/json"
- "fmt"
+ "log"
"os"
"strings"
@@ -17,14 +17,55 @@ const (
func init() {
lib.RegisterInputConfigCreator(TypeStdin, func(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
- return newStdin(action, data)
+ return NewStdinFromBytes(action, data)
})
- lib.RegisterInputConverter(TypeStdin, &Stdin{
+ lib.RegisterInputConverter(TypeStdin, &stdin{
Description: DescStdin,
})
}
-func newStdin(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
+type stdin struct {
+ Type string
+ Action lib.Action
+ Description string
+ Name string
+ OnlyIPType lib.IPType
+}
+
+func NewStdin(action lib.Action, opts ...lib.InputOption) lib.InputConverter {
+ s := &stdin{
+ Type: TypeStdin,
+ Action: action,
+ Description: DescStdin,
+ }
+
+ for _, opt := range opts {
+ if opt != nil {
+ opt(s)
+ }
+ }
+
+ return s
+}
+
+func WithStdinName(name string) lib.InputOption {
+ return func(s lib.InputConverter) {
+ name = strings.TrimSpace(name)
+ if name == "" {
+ log.Fatalf("❌ [type %s | action %s] missing name", TypeStdin, s.(*stdin).Action)
+ }
+
+ s.(*stdin).Name = name
+ }
+}
+
+func WithStdinOnlyIPType(onlyIPType lib.IPType) lib.InputOption {
+ return func(s lib.InputConverter) {
+ s.(*stdin).OnlyIPType = onlyIPType
+ }
+}
+
+func NewStdinFromBytes(action lib.Action, data []byte) (lib.InputConverter, error) {
var tmp struct {
Name string `json:"name"`
OnlyIPType lib.IPType `json:"onlyIPType"`
@@ -36,40 +77,26 @@ func newStdin(action lib.Action, data json.RawMessage) (lib.InputConverter, erro
}
}
- if tmp.Name == "" {
- return nil, fmt.Errorf("❌ [type %s | action %s] missing name", TypeStdin, action)
- }
-
- return &Stdin{
- Type: TypeStdin,
- Action: action,
- Description: DescStdin,
- Name: tmp.Name,
- OnlyIPType: tmp.OnlyIPType,
- }, nil
-}
-
-type Stdin struct {
- Type string
- Action lib.Action
- Description string
- Name string
- OnlyIPType lib.IPType
+ return NewStdin(
+ action,
+ WithStdinName(tmp.Name),
+ WithStdinOnlyIPType(tmp.OnlyIPType),
+ ), nil
}
-func (s *Stdin) GetType() string {
+func (s *stdin) GetType() string {
return s.Type
}
-func (s *Stdin) GetAction() lib.Action {
+func (s *stdin) GetAction() lib.Action {
return s.Action
}
-func (s *Stdin) GetDescription() string {
+func (s *stdin) GetDescription() string {
return s.Description
}
-func (s *Stdin) Input(container lib.Container) (lib.Container, error) {
+func (s *stdin) Input(container lib.Container) (lib.Container, error) {
entry := lib.NewEntry(s.Name)
scanner := bufio.NewScanner(os.Stdin)
diff --git a/plugin/special/stdout.go b/plugin/special/stdout.go
index 614deaac..e356c577 100644
--- a/plugin/special/stdout.go
+++ b/plugin/special/stdout.go
@@ -18,14 +18,57 @@ const (
func init() {
lib.RegisterOutputConfigCreator(TypeStdout, func(action lib.Action, data json.RawMessage) (lib.OutputConverter, error) {
- return newStdout(action, data)
+ return NewStdoutFromBytes(action, data)
})
- lib.RegisterOutputConverter(TypeStdout, &Stdout{
+ lib.RegisterOutputConverter(TypeStdout, &stdout{
Description: DescStdout,
})
}
-func newStdout(action lib.Action, data json.RawMessage) (lib.OutputConverter, error) {
+type stdout struct {
+ Type string
+ Action lib.Action
+ Description string
+ Want []string
+ Exclude []string
+ OnlyIPType lib.IPType
+}
+
+func NewStdout(action lib.Action, opts ...lib.OutputOption) lib.OutputConverter {
+ s := &stdout{
+ Type: TypeStdout,
+ Action: action,
+ Description: DescStdout,
+ }
+
+ for _, opt := range opts {
+ if opt != nil {
+ opt(s)
+ }
+ }
+
+ return s
+}
+
+func WithStdoutWantedList(lists []string) lib.OutputOption {
+ return func(s lib.OutputConverter) {
+ s.(*stdout).Want = lists
+ }
+}
+
+func WithStdoutExcludedList(lists []string) lib.OutputOption {
+ return func(s lib.OutputConverter) {
+ s.(*stdout).Exclude = lists
+ }
+}
+
+func WithStdoutOnlyIPType(onlyIPType lib.IPType) lib.OutputOption {
+ return func(s lib.OutputConverter) {
+ s.(*stdout).OnlyIPType = onlyIPType
+ }
+}
+
+func NewStdoutFromBytes(action lib.Action, data []byte) (lib.OutputConverter, error) {
var tmp struct {
Want []string `json:"wantedList"`
Exclude []string `json:"excludedList"`
@@ -38,38 +81,27 @@ func newStdout(action lib.Action, data json.RawMessage) (lib.OutputConverter, er
}
}
- return &Stdout{
- Type: TypeStdout,
- Action: action,
- Description: DescStdout,
- Want: tmp.Want,
- Exclude: tmp.Exclude,
- OnlyIPType: tmp.OnlyIPType,
- }, nil
-}
-
-type Stdout struct {
- Type string
- Action lib.Action
- Description string
- Want []string
- Exclude []string
- OnlyIPType lib.IPType
+ return NewStdout(
+ action,
+ WithStdoutWantedList(tmp.Want),
+ WithStdoutExcludedList(tmp.Exclude),
+ WithStdoutOnlyIPType(tmp.OnlyIPType),
+ ), nil
}
-func (s *Stdout) GetType() string {
+func (s *stdout) GetType() string {
return s.Type
}
-func (s *Stdout) GetAction() lib.Action {
+func (s *stdout) GetAction() lib.Action {
return s.Action
}
-func (s *Stdout) GetDescription() string {
+func (s *stdout) GetDescription() string {
return s.Description
}
-func (s *Stdout) Output(container lib.Container) error {
+func (s *stdout) Output(container lib.Container) error {
for _, name := range s.filterAndSortList(container) {
entry, found := container.GetEntry(name)
if !found {
@@ -89,7 +121,7 @@ func (s *Stdout) Output(container lib.Container) error {
return nil
}
-func (s *Stdout) filterAndSortList(container lib.Container) []string {
+func (s *stdout) filterAndSortList(container lib.Container) []string {
excludeMap := make(map[string]bool)
for _, exclude := range s.Exclude {
if exclude = strings.ToUpper(strings.TrimSpace(exclude)); exclude != "" {
@@ -125,7 +157,7 @@ func (s *Stdout) filterAndSortList(container lib.Container) []string {
return list
}
-func (s *Stdout) generateCIDRList(entry *lib.Entry) ([]string, error) {
+func (s *stdout) generateCIDRList(entry *lib.Entry) ([]string, error) {
entryList, err := entry.MarshalText(lib.GetIgnoreIPType(s.OnlyIPType))
if err != nil {
return nil, err
diff --git a/plugin/special/test.go b/plugin/special/test.go
index cf83ed83..6425ca93 100644
--- a/plugin/special/test.go
+++ b/plugin/special/test.go
@@ -18,19 +18,31 @@ var testCIDRs = []string{
func init() {
lib.RegisterInputConfigCreator(typeTest, func(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
- return newTest(action, data)
+ return NewTestFromBytes(action, data)
})
lib.RegisterInputConverter(typeTest, &test{
Description: descTest,
})
}
-func newTest(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
- return &test{
+func NewTest(action lib.Action, opts ...lib.InputOption) lib.InputConverter {
+ t := &test{
Type: typeTest,
Action: action,
Description: descTest,
- }, nil
+ }
+
+ for _, opt := range opts {
+ if opt != nil {
+ opt(t)
+ }
+ }
+
+ return t
+}
+
+func NewTestFromBytes(action lib.Action, data []byte) (lib.InputConverter, error) {
+ return NewTest(action), nil
}
type test struct {
diff --git a/plugin/v2ray/dat_in.go b/plugin/v2ray/dat_in.go
index 33a24061..dede0165 100644
--- a/plugin/v2ray/dat_in.go
+++ b/plugin/v2ray/dat_in.go
@@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"io"
+ "log"
"net"
"net/http"
"os"
@@ -20,70 +21,102 @@ const (
func init() {
lib.RegisterInputConfigCreator(TypeGeoIPDatIn, func(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
- return newGeoIPDatIn(action, data)
+ return NewGeoIPDatInFromBytes(action, data)
})
- lib.RegisterInputConverter(TypeGeoIPDatIn, &GeoIPDatIn{
+ lib.RegisterInputConverter(TypeGeoIPDatIn, &geoIPDatIn{
Description: DescGeoIPDatIn,
})
}
-func newGeoIPDatIn(action lib.Action, data json.RawMessage) (lib.InputConverter, error) {
- var tmp struct {
- URI string `json:"uri"`
- Want []string `json:"wantedList"`
- OnlyIPType lib.IPType `json:"onlyIPType"`
+type geoIPDatIn struct {
+ Type string
+ Action lib.Action
+ Description string
+ URI string
+ Want map[string]bool
+ OnlyIPType lib.IPType
+}
+
+func NewGeoIPDatIn(action lib.Action, opts ...lib.InputOption) lib.InputConverter {
+ g := &geoIPDatIn{
+ Type: TypeGeoIPDatIn,
+ Action: action,
+ Description: DescGeoIPDatIn,
}
- if len(data) > 0 {
- if err := json.Unmarshal(data, &tmp); err != nil {
- return nil, err
+ for _, opt := range opts {
+ if opt != nil {
+ opt(g)
}
}
- if tmp.URI == "" {
- return nil, fmt.Errorf("❌ [type %s | action %s] uri must be specified in config", TypeGeoIPDatIn, action)
+ return g
+}
+
+func WithGeoIPDatInURI(uri string) lib.InputOption {
+ return func(s lib.InputConverter) {
+ uri = strings.TrimSpace(uri)
+ if uri == "" {
+ log.Fatalf("❌ [type %s | action %s] uri must be specified in config", TypeGeoIPDatIn, s.(*geoIPDatIn).Action)
+ }
+
+ s.(*geoIPDatIn).URI = uri
}
+}
- // Filter want list
- wantList := make(map[string]bool)
- for _, want := range tmp.Want {
- if want = strings.ToUpper(strings.TrimSpace(want)); want != "" {
- wantList[want] = true
+func WithGeoIPDatInWantedList(lists []string) lib.InputOption {
+ return func(s lib.InputConverter) {
+ wantList := make(map[string]bool)
+ for _, want := range lists {
+ if want = strings.ToUpper(strings.TrimSpace(want)); want != "" {
+ wantList[want] = true
+ }
}
+
+ s.(*geoIPDatIn).Want = wantList
}
+}
- return &GeoIPDatIn{
- Type: TypeGeoIPDatIn,
- Action: action,
- Description: DescGeoIPDatIn,
- URI: tmp.URI,
- Want: wantList,
- OnlyIPType: tmp.OnlyIPType,
- }, nil
+func WithGeoIPDatInOnlyIPType(onlyIPType lib.IPType) lib.InputOption {
+ return func(s lib.InputConverter) {
+ s.(*geoIPDatIn).OnlyIPType = onlyIPType
+ }
}
-type GeoIPDatIn struct {
- Type string
- Action lib.Action
- Description string
- URI string
- Want map[string]bool
- OnlyIPType lib.IPType
+func NewGeoIPDatInFromBytes(action lib.Action, data []byte) (lib.InputConverter, error) {
+ var tmp struct {
+ URI string `json:"uri"`
+ Want []string `json:"wantedList"`
+ OnlyIPType lib.IPType `json:"onlyIPType"`
+ }
+
+ if len(data) > 0 {
+ if err := json.Unmarshal(data, &tmp); err != nil {
+ return nil, err
+ }
+ }
+
+ return NewGeoIPDatIn(
+ action,
+ WithGeoIPDatInURI(tmp.URI),
+ WithGeoIPDatInWantedList(tmp.Want),
+ WithGeoIPDatInOnlyIPType(tmp.OnlyIPType),
+ ), nil
}
-func (g *GeoIPDatIn) GetType() string {
+func (g *geoIPDatIn) GetType() string {
return g.Type
}
-func (g *GeoIPDatIn) GetAction() lib.Action {
+func (g *geoIPDatIn) GetAction() lib.Action {
return g.Action
}
-func (g *GeoIPDatIn) GetDescription() string {
+func (g *geoIPDatIn) GetDescription() string {
return g.Description
}
-func (g *GeoIPDatIn) Input(container lib.Container) (lib.Container, error) {
+func (g *geoIPDatIn) Input(container lib.Container) (lib.Container, error) {
entries := make(map[string]*lib.Entry)
var err error
@@ -122,7 +155,7 @@ func (g *GeoIPDatIn) Input(container lib.Container) (lib.Container, error) {
return container, nil
}
-func (g *GeoIPDatIn) walkLocalFile(path string, entries map[string]*lib.Entry) error {
+func (g *geoIPDatIn) walkLocalFile(path string, entries map[string]*lib.Entry) error {
file, err := os.Open(path)
if err != nil {
return err
@@ -136,7 +169,7 @@ func (g *GeoIPDatIn) walkLocalFile(path string, entries map[string]*lib.Entry) e
return nil
}
-func (g *GeoIPDatIn) walkRemoteFile(url string, entries map[string]*lib.Entry) error {
+func (g *geoIPDatIn) walkRemoteFile(url string, entries map[string]*lib.Entry) error {
resp, err := http.Get(url)
if err != nil {
return err
@@ -154,7 +187,7 @@ func (g *GeoIPDatIn) walkRemoteFile(url string, entries map[string]*lib.Entry) e
return nil
}
-func (g *GeoIPDatIn) generateEntries(reader io.Reader, entries map[string]*lib.Entry) error {
+func (g *geoIPDatIn) generateEntries(reader io.Reader, entries map[string]*lib.Entry) error {
geoipBytes, err := io.ReadAll(reader)
if err != nil {
return err
diff --git a/plugin/v2ray/dat_out.go b/plugin/v2ray/dat_out.go
index 5137e828..c361bcd4 100644
--- a/plugin/v2ray/dat_out.go
+++ b/plugin/v2ray/dat_out.go
@@ -26,14 +26,88 @@ var (
func init() {
lib.RegisterOutputConfigCreator(TypeGeoIPDatOut, func(action lib.Action, data json.RawMessage) (lib.OutputConverter, error) {
- return newGeoIPDatOut(action, data)
+ return NewGeoIPDatOutFromBytes(action, data)
})
- lib.RegisterOutputConverter(TypeGeoIPDatOut, &GeoIPDatOut{
+ lib.RegisterOutputConverter(TypeGeoIPDatOut, &geoIPDatOut{
Description: DescGeoIPDatOut,
})
}
-func newGeoIPDatOut(action lib.Action, data json.RawMessage) (lib.OutputConverter, error) {
+type geoIPDatOut struct {
+ Type string
+ Action lib.Action
+ Description string
+ OutputName string
+ OutputDir string
+ Want []string
+ Exclude []string
+ OneFilePerList bool
+ OnlyIPType lib.IPType
+}
+
+func NewGeoIPDatOut(action lib.Action, opts ...lib.OutputOption) lib.OutputConverter {
+ g := &geoIPDatOut{
+ Type: TypeGeoIPDatOut,
+ Action: action,
+ Description: DescGeoIPDatOut,
+ }
+
+ for _, opt := range opts {
+ if opt != nil {
+ opt(g)
+ }
+ }
+
+ return g
+}
+
+func WithGeoIPDatOutOutputName(name string) lib.OutputOption {
+ return func(s lib.OutputConverter) {
+ name = strings.TrimSpace(name)
+ if name == "" {
+ name = defaultOutputName
+ }
+
+ s.(*geoIPDatOut).OutputName = name
+ }
+}
+
+func WithGeoIPDatOutOutputDir(dir string) lib.OutputOption {
+ return func(s lib.OutputConverter) {
+ dir = strings.TrimSpace(dir)
+ if dir == "" {
+ dir = defaultOutputDir
+ }
+
+ s.(*geoIPDatOut).OutputDir = dir
+ }
+}
+
+func WithGeoIPDatOutWantedList(lists []string) lib.OutputOption {
+ return func(s lib.OutputConverter) {
+ s.(*geoIPDatOut).Want = lists
+ }
+}
+
+func WithGeoIPDatOutExcludedList(lists []string) lib.OutputOption {
+ return func(s lib.OutputConverter) {
+ s.(*geoIPDatOut).Exclude = lists
+ }
+}
+
+func WithGeoIPDatOutOneFilePerList(oneFilePerList bool) lib.OutputOption {
+ return func(s lib.OutputConverter) {
+ s.(*geoIPDatOut).OneFilePerList = oneFilePerList
+ }
+}
+
+func WithGeoIPDatOutOnlyIPType(onlyIPType lib.IPType) lib.OutputOption {
+ return func(s lib.OutputConverter) {
+ s.(*geoIPDatOut).OnlyIPType = onlyIPType
+ }
+}
+
+func NewGeoIPDatOutFromBytes(action lib.Action, data []byte) (lib.OutputConverter, error) {
var tmp struct {
OutputName string `json:"outputName"`
OutputDir string `json:"outputDir"`
@@ -49,52 +123,30 @@ func newGeoIPDatOut(action lib.Action, data json.RawMessage) (lib.OutputConverte
}
}
- if tmp.OutputName == "" {
- tmp.OutputName = defaultOutputName
- }
-
- if tmp.OutputDir == "" {
- tmp.OutputDir = defaultOutputDir
- }
-
- return &GeoIPDatOut{
- Type: TypeGeoIPDatOut,
- Action: action,
- Description: DescGeoIPDatOut,
- OutputName: tmp.OutputName,
- OutputDir: tmp.OutputDir,
- Want: tmp.Want,
- Exclude: tmp.Exclude,
- OneFilePerList: tmp.OneFilePerList,
- OnlyIPType: tmp.OnlyIPType,
- }, nil
-}
-
-type GeoIPDatOut struct {
- Type string
- Action lib.Action
- Description string
- OutputName string
- OutputDir string
- Want []string
- Exclude []string
- OneFilePerList bool
- OnlyIPType lib.IPType
+ return NewGeoIPDatOut(
+ action,
+ WithGeoIPDatOutOutputName(tmp.OutputName),
+ WithGeoIPDatOutOutputDir(tmp.OutputDir),
+ WithGeoIPDatOutWantedList(tmp.Want),
+ WithGeoIPDatOutExcludedList(tmp.Exclude),
+ WithGeoIPDatOutOneFilePerList(tmp.OneFilePerList),
+ WithGeoIPDatOutOnlyIPType(tmp.OnlyIPType),
+ ), nil
}
-func (g *GeoIPDatOut) GetType() string {
+func (g *geoIPDatOut) GetType() string {
return g.Type
}
-func (g *GeoIPDatOut) GetAction() lib.Action {
+func (g *geoIPDatOut) GetAction() lib.Action {
return g.Action
}
-func (g *GeoIPDatOut) GetDescription() string {
+func (g *geoIPDatOut) GetDescription() string {
return g.Description
}
-func (g *GeoIPDatOut) Output(container lib.Container) error {
+func (g *geoIPDatOut) Output(container lib.Container) error {
geoIPList := new(GeoIPList)
geoIPList.Entry = make([]*GeoIP, 0, 300)
updated := false
@@ -144,7 +196,7 @@ func (g *GeoIPDatOut) Output(container lib.Container) error {
return nil
}
-func (g *GeoIPDatOut) filterAndSortList(container lib.Container) []string {
+func (g *geoIPDatOut) filterAndSortList(container lib.Container) []string {
excludeMap := make(map[string]bool)
for _, exclude := range g.Exclude {
if exclude = strings.ToUpper(strings.TrimSpace(exclude)); exclude != "" {
@@ -180,7 +232,7 @@ func (g *GeoIPDatOut) filterAndSortList(container lib.Container) []string {
return list
}
-func (g *GeoIPDatOut) generateGeoIP(entry *lib.Entry) (*GeoIP, error) {
+func (g *geoIPDatOut) generateGeoIP(entry *lib.Entry) (*GeoIP, error) {
entryCidr, err := entry.MarshalPrefix(lib.GetIgnoreIPType(g.OnlyIPType))
if err != nil {
return nil, err
@@ -205,13 +257,13 @@ func (g *GeoIPDatOut) generateGeoIP(entry *lib.Entry) (*GeoIP, error) {
}
// Sort by country code to make reproducible builds
-func (g *GeoIPDatOut) sort(list *GeoIPList) {
+func (g *geoIPDatOut) sort(list *GeoIPList) {
sort.SliceStable(list.Entry, func(i, j int) bool {
return list.Entry[i].CountryCode < list.Entry[j].CountryCode
})
}
-func (g *GeoIPDatOut) writeFile(filename string, geoIPBytes []byte) error {
+func (g *geoIPDatOut) writeFile(filename string, geoIPBytes []byte) error {
if err := os.MkdirAll(g.OutputDir, 0755); err != nil {
return err
}