schema.determineSchemaType   A
last analyzed

Complexity

Conditions 4

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 9
nop 1
dl 0
loc 14
rs 9.95
c 0
b 0
f 0
1
package schema
2
3
import (
4
	"errors"
5
	"fmt"
6
	"io"
7
	"net/http"
8
	"net/url"
9
	"os"
10
	"path/filepath"
11
)
12
13
// Type defines an enumeration for different schema types.
14
type Type int
15
16
const (
17
	// URL represents a schema type for URLs.
18
	URL Type = iota
19
20
	// File represents a schema type for file paths.
21
	File
22
23
	// Inline represents a schema type for inline data.
24
	Inline
25
)
26
27
// Loader is a struct that holds a map of loader functions, each corresponding to a Type.
28
type Loader struct {
29
	// loaders is a map where each Type is associated with a corresponding function
30
	// that takes a string as input and returns a string and an error.
31
	// These functions are responsible for loading data based on the Type.
32
	loaders map[Type]func(string) (string, error)
33
}
34
35
// NewSchemaLoader initializes and returns a new Loader instance.
36
// It sets up the map of loader functions for each Type.
37
func NewSchemaLoader() *Loader {
38
	return &Loader{
39
		loaders: map[Type]func(string) (string, error){
40
			// URL loader function for handling URL type schemas.
41
			URL: loadFromURL,
42
43
			// File loader function for handling file path type schemas.
44
			File: loadFromFile,
45
46
			// Inline loader function for handling inline type schemas.
47
			Inline: loadInline,
48
		},
49
	}
50
}
51
52
// LoadSchema loads a schema based on its type
53
func (s *Loader) LoadSchema(input string) (string, error) {
54
	schemaType, err := determineSchemaType(input)
55
	if err != nil {
56
		return "", fmt.Errorf("error determining schema type: %w", err)
0 ignored issues
show
introduced by
unrecognized printf verb 'w'
Loading history...
57
	}
58
59
	loaderFunc, exists := s.loaders[schemaType]
60
	if !exists {
61
		return "", fmt.Errorf("loader function not found for schema type: %v", schemaType)
62
	}
63
64
	return loaderFunc(input)
65
}
66
67
// determineSchemaType determines the type of schema based on the input string
68
func determineSchemaType(input string) (Type, error) {
69
	if isURL(input) {
70
		return URL, nil
71
	}
72
73
	valid, err := isFilePath(input)
74
	if err != nil {
75
		return Inline, nil
76
	}
77
	if valid {
78
		return File, nil
79
	}
80
81
	return Inline, nil
82
}
83
84
func isURL(input string) bool {
85
	parsedURL, err := url.Parse(input)
86
	if err != nil {
87
		return false
88
	}
89
90
	// Check if the URL has a valid scheme and host
91
	return parsedURL.Scheme != "" && parsedURL.Host != ""
92
}
93
94
func isFilePath(input string) (bool, error) {
95
	_, err := os.Stat(input)
96
	if err != nil {
97
		if os.IsNotExist(err) {
98
			return false, errors.New("file does not exist")
99
		}
100
		if os.IsPermission(err) {
101
			return false, errors.New("permission denied")
102
		}
103
		return false, err
104
	}
105
106
	return true, nil
107
}
108
109
func loadFromURL(inputURL string) (string, error) {
110
	// Parse and validate the URL
111
	parsedURL, err := url.Parse(inputURL)
112
	if err != nil {
113
		return "", err
114
	}
115
116
	// Add checks here to validate the scheme, host, etc., of parsedURL
117
	// For example, ensure the scheme is either http or https
118
	if parsedURL.Scheme != "http" && parsedURL.Scheme != "https" {
119
		return "", errors.New("invalid URL scheme")
120
	}
121
122
	// Perform the HTTP GET request
123
	resp, err := http.Get(parsedURL.String())
124
	if err != nil {
125
		return "", err
126
	}
127
	defer resp.Body.Close()
128
129
	// Read the response body
130
	body, err := io.ReadAll(resp.Body)
131
	if err != nil {
132
		return "", err
133
	}
134
135
	return string(body), nil
136
}
137
138
func loadFromFile(path string) (string, error) {
139
	// Clean the path
140
	cleanPath := filepath.Clean(path)
141
142
	// Check if the cleaned path is trying to traverse directories
143
	if filepath.IsAbs(cleanPath) || filepath.HasPrefix(cleanPath, "..") {
144
		return "", errors.New("invalid file path")
145
	}
146
147
	content, err := os.ReadFile(path)
148
	if err != nil {
149
		return "", err
150
	}
151
152
	return string(content), nil
153
}
154
155
// loadInline is a function that handles inline schema types.
156
func loadInline(schema string) (string, error) {
157
	// Add validation if necessary. For example:
158
	if schema == "" {
159
		return "", errors.New("schema is empty")
160
	}
161
162
	return schema, nil
163
}
164