├── .gitignore
├── README.md
├── _examples
├── README.md
├── auth
│ ├── api_key.go
│ ├── api_key_user.go
│ ├── basic_auth.go
│ └── db_api.sql
├── datetime
│ ├── extraction.go
│ ├── operations.go
│ └── validation.go
├── downloader
│ ├── downloader.go
│ └── sample3.txt
├── excel-generator
│ └── main.go
├── formatter
│ └── formater.go
├── http-client
│ ├── get.go
│ ├── head.go
│ ├── post.go
│ ├── post_file.go
│ └── test.go
├── pagination
│ ├── default.go
│ ├── pagination-json.go
│ └── pagination-xml.go
├── pdf-generator
│ ├── main.go
│ └── templates
│ │ ├── simple-pdf.html
│ │ └── table-report.html
├── pubsub
│ ├── helpers
│ │ ├── functions.go
│ │ └── messages.txt
│ ├── nats
│ │ ├── process_message.go
│ │ ├── publish.go
│ │ ├── subscribe.go
│ │ └── subscribe_queue.go
│ └── rabbitmq
│ │ ├── consume_exchange.go
│ │ ├── consume_queue.go
│ │ ├── process_exchange.go
│ │ ├── process_queue.go
│ │ ├── publish_exchange.go
│ │ └── publish_queue.go
├── reflection
│ ├── inspect.go
│ ├── tags.go
│ └── var_dump.go
├── serialization
│ ├── from_file.go
│ ├── json.go
│ ├── rest_api.go
│ ├── transform.go
│ └── xml.go
├── sessions
│ ├── gorilla.go
│ └── http.go
└── uploader
│ ├── templates
│ ├── upload-multiple.html
│ └── upload.html
│ ├── uploader-multiple.go
│ └── uploader-single.go
├── calculations
├── README.md
├── percentage.go
└── percentage_test.go
├── collections
├── README.md
└── functions.go
├── conversion
├── README.md
├── any_type.go
├── any_type_test.go
├── boolean.go
├── float.go
├── float_test.go
├── integer.go
├── integer_test.go
├── nil_value.go
├── nil_value_test.go
├── string.go
└── string_test.go
├── database
├── README.md
├── connection.go
├── query_builder.go
└── table.go
├── datetime
├── README.md
├── conversion.go
├── conversion_test.go
├── current_time.go
├── current_time_test.go
├── extraction.go
├── extraction_string.go
├── last_time.go
├── last_time_string.go
├── layouts.go
├── operation.go
├── operation_test.go
├── operations_string.go
├── time_ago.go
├── time_ago_string.go
├── time_ago_test.go
└── validation.go
├── docgen
├── README.md
├── excel_generator.go
├── excel_generator_test.go
├── html_pdf_generator.go
└── html_pdf_generator_test.go
├── encryption
├── README.md
├── base64.go
├── base64_test.go
├── md5.go
├── md5_test.go
├── password.go
├── password_test.go
├── string.go
└── string_test.go
├── filemanager
├── README.md
├── consts.go
├── download_test.go
├── downloader.go
├── file_info.go
├── file_info_test.go
├── file_manager.go
├── file_manager_test.go
├── file_zip.go
├── file_zip_test.go
├── sample3.txt
└── uploader.go
├── formatter
├── README.md
├── currency.go
├── currency_test.go
├── padding.go
└── padding_test.go
├── go.mod
├── go.sum
├── httputils
├── README.md
├── apikey_middleware.go
├── apikey_middleware_test.go
├── apikey_user_middleware.go
├── apikey_user_middleware_test.go
├── basic_auth_middleware.go
├── basic_auth_middleware_test.go
├── csrf_middleware.go
├── hateoas_response.go
├── hateoas_response_test.go
├── http_client.go
├── http_client_test.go
├── json_response.go
├── json_response_test.go
├── log_request.go
├── pagination.go
├── pagination_xml.go
├── session_gorilla.go
├── session_http.go
├── xml_response.go
└── xss_middleware.go
├── logging
├── README.md
├── logger.go
└── logger_test.go
├── mailer
├── README.md
├── email_service.go
└── email_service_test.go
├── messages
├── README.md
├── color.go
├── color_test.go
├── message_handler.go
├── message_handler_test.go
├── message_type.go
└── message_type_test.go
├── progress
├── README.md
├── completion_percentage.go
├── loader.go
├── loader_test.go
├── paralell_progress.go
├── percentage_progress.go
├── progressbar.go
├── spinner_test.go
└── sprinner.go
├── pubsub
├── README.md
├── kafka
│ └── types.go
├── nats
│ ├── publisher.go
│ ├── subscriber.go
│ └── types.go
└── rabbitmq
│ ├── common.go
│ ├── consumer.go
│ ├── producer.go
│ ├── test.go
│ └── types.go
├── random
├── README.md
├── number.go
├── number_test.go
├── random.go
├── string.go
└── string_test.go
├── reflection
├── README.md
├── inspection.go
├── inspection_test.go
├── name_of.go
├── reflection.go
├── reflection_test.go
├── tags.go
└── var_dump.go
├── serialization
├── README.md
├── asn1.go
├── asn1_test.go
├── conversion.go
├── conversion_test.go
├── from_file.go
├── from_file_test.go
├── json.go
├── json_test.go
├── xml.go
└── xml_test.go
└── units
├── README.md
├── length.go
├── length_test.go
├── storage.go
├── storage_test.go
├── volume.go
├── volume_test.go
├── weight.go
└── weight_test.go
/.gitignore:
--------------------------------------------------------------------------------
1 | Files/
2 | uploads/
3 |
4 | *.sql
5 | *.pdf
6 | *.xlsx
7 | *.csv
8 | *.png
9 | .*jpeg
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # go-nopain
2 |
3 | **go-nopain** is a comprehensive library offering helper functions designed for real-world systems, aiming to streamline common tasks and simplify development in Go.
4 |
5 | **Packages:**
6 | - [**Publish Subscribe**](/pubsub): Messaging with most common brokers.
7 | - [**HTTP Utils**](/httputils): HTTP authentication methods, middlewares, HTTP requests, and responses.
8 | - [**Serialization**](/serialization): Functions for serializing and deserializing data (JSON, XML, CSV, and ASN.1).
9 | - [**Mail Management**](/mailer): Managing email messages.
10 | - [**Date Handling**](/datetime): Dates, times, and durations.
11 | - [**File Manager**](/filemanager): File management, including upload, download, reading, writing, and manipulation.
12 | - [**Type Conversion**](/conversion): Functions for converting different data types.
13 | - [**Random Numbers**](/random): Generation of random numbers and random data.
14 | - [**Messages**](/messages): Managing messages (errors, information, success, and message coloring).
15 | - [**Units of Measurement**](/units): Units of measurement, conversion, and formatting.
16 | - [**Logging**](/logging): Logging utilities for monitoring and debugging applications.
17 | - [**Formatters**](/formatter): Formatters for data presentation and formatting.
18 | - [**Reflection**](/reflection): Runtime type manipulation.
19 | - [**Calculations**](/calculations): Mathematical and numerical calculations.
20 | - [**Progress**](/progress): Tracking and displaying progress in long-running tasks.
21 | - [**Collections**](/collections): Data structure manipulation.
--------------------------------------------------------------------------------
/_examples/README.md:
--------------------------------------------------------------------------------
1 | # examples
2 | Basic exmaples for each package
3 |
--------------------------------------------------------------------------------
/_examples/auth/api_key.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 |
7 | "github.com/ortizdavid/go-nopain/httputils"
8 | )
9 |
10 | func main() {
11 |
12 | mux := http.NewServeMux()
13 | middleware := httputils.NewApiKeyMiddleWare("key123")
14 |
15 | mux.HandleFunc("GET /", indexHandler)
16 | mux.HandleFunc("GET /public", publicHandler)
17 | mux.Handle("GET /protected-get", middleware.Apply(protectedHandler))
18 | mux.Handle("DELETE /protected-delete", middleware.Apply(protectedHandler2))
19 |
20 | fmt.Println("Listen at http://127.0.0.1:7000")
21 | http.ListenAndServe(":7000", mux)
22 | }
23 |
24 | func indexHandler(w http.ResponseWriter, r *http.Request) {
25 | fmt.Fprintln(w, "Index")
26 | }
27 |
28 | func publicHandler(w http.ResponseWriter, r *http.Request) {
29 | fmt.Fprintln(w, "Public Content")
30 | }
31 |
32 | func protectedHandler(w http.ResponseWriter, r *http.Request) {
33 | fmt.Fprintln(w, "Protected GET Content")
34 | }
35 |
36 | func protectedHandler2(w http.ResponseWriter, r *http.Request) {
37 | fmt.Fprintln(w, "Protected DELETE Content")
38 | }
39 |
40 |
41 |
--------------------------------------------------------------------------------
/_examples/auth/api_key_user.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 | "github.com/ortizdavid/go-nopain/httputils"
7 | //"gorm.io/gorm"
8 | )
9 |
10 |
11 | func main() {
12 |
13 | // Create a new ServeMux
14 | mux := http.NewServeMux()
15 |
16 | // Retrieve user API keys
17 | userApiKeys := GetAllUserKeys()
18 |
19 | // Initialize the API key middleware
20 | middleware := httputils.NewApiKeyUserMiddleware(userApiKeys)
21 |
22 | // Set up routes
23 | mux.HandleFunc("/", indexHandler)
24 | mux.HandleFunc("/public", publicHandler)
25 | mux.Handle("/protected-1", middleware.Apply(protectedHandler1))
26 | mux.Handle("/protected-2", middleware.Apply(protectedHandler2))
27 |
28 | // Start the server
29 | fmt.Println("Listening at http://127.0.0.1:7000")
30 | http.ListenAndServe(":7000", mux)
31 | }
32 |
33 | func indexHandler(w http.ResponseWriter, r *http.Request) {
34 | fmt.Fprintln(w, "Example of API KEY In Go")
35 | }
36 |
37 | func publicHandler(w http.ResponseWriter, r *http.Request) {
38 | fmt.Fprintln(w, "Public Content")
39 | }
40 |
41 | func protectedHandler1(w http.ResponseWriter, r *http.Request) {
42 | fmt.Fprintln(w, "Protected 1 Content")
43 | }
44 |
45 | func protectedHandler2(w http.ResponseWriter, r *http.Request) {
46 | fmt.Fprintln(w, "Protected 2 Content")
47 | }
48 |
49 | // GetAllUserKeysFromDB returns a list of API keys and user IDs stored in a database.
50 | // This function connects to a PostgreSQL database and queries the 'api_key_users' table to retrieve the data.
51 | /*func GetAllUserKeysFromDB() []httputils.UserApiKey{
52 | dsn := "host=localhost user=yourusername password=yourpassword dbname=yourdbname port=5432 sslmode=disable"
53 | db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
54 | if err != nil {
55 | panic("failed to connect database")
56 | }
57 | var userKeys []httputils.UserApiKey
58 | db.Raw("SELECT * FROM api_key_users").Scan(&userKeys)
59 | return userKeys
60 | }*/
61 |
62 | // GetAllUserKeys returns a list of API keys and user IDs from hardcoded values.
63 | // This function uses values directly encoded in the code.
64 | func GetAllUserKeys() []httputils.UserApiKey{
65 | return []httputils.UserApiKey{
66 | {UserId: "user1", ApiKey: "key1"},
67 | {UserId: "user2", ApiKey: "key2"},
68 | {UserId: "user3", ApiKey: "key3"},
69 | {UserId: "user4", ApiKey: "key4"},
70 | }
71 | }
--------------------------------------------------------------------------------
/_examples/auth/basic_auth.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 | "github.com/ortizdavid/go-nopain/httputils"
7 | )
8 |
9 |
10 | // Define a list of users
11 | var users = []httputils.UserBasicAuth{
12 | {"user1", "pass1"},
13 | {"user2", "pass2"},
14 | }
15 |
16 |
17 | func main() {
18 |
19 | mux := http.NewServeMux()
20 | // Define your handler function
21 |
22 | middleware := httputils.NewBasicAuthMiddleware(users)
23 |
24 | // Wrap your handler function with BasicAuth middleware
25 | mux.Handle("GET /", middleware.Apply(helloHandler))
26 |
27 | // Start the HTTP server
28 | http.ListenAndServe(":7000", mux)
29 | }
30 |
31 |
32 | func helloHandler(w http.ResponseWriter, r *http.Request) {
33 | fmt.Fprintln(w, "Hello, authenticated user!")
34 | }
--------------------------------------------------------------------------------
/_examples/auth/db_api.sql:
--------------------------------------------------------------------------------
1 | DROP DATABASE IF EXISTS db_api_key;
2 | CREATE DATABASE db_api_key;
3 |
4 | \c db_api_key;
5 |
6 | DROP TABLE IF EXISTS api_key_users;
7 | CREATE TABLE api_key_users(
8 | user_id VARCHAR(50) UNIQUE,
9 | api_key VARCHAR(150) UNIQUE,
10 | role VARCHAR(30)
11 | );
12 |
13 |
14 |
--------------------------------------------------------------------------------
/_examples/datetime/extraction.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "time"
6 |
7 | "github.com/ortizdavid/go-nopain/datetime"
8 | )
9 |
10 | func main() {
11 |
12 | date := time.Now()
13 | fmt.Println("Date and Time Extraction")
14 | fmt.Println("Original: ", date)
15 | fmt.Println("Date: ", datetime.ExtractDate(date))
16 | fmt.Println("Date Time: ", datetime.ExtractDateTime(date))
17 | fmt.Println("Year: ", datetime.ExtractYear(date))
18 | fmt.Println("Week: ", datetime.ExtractWeek(date))
19 | fmt.Println("Day: ", datetime.ExtractDay(date))
20 | fmt.Println("Month: ", datetime.ExtractMonth(date))
21 | fmt.Println("Hour: ", datetime.ExtractHour(date))
22 | fmt.Println("Seconds: ", datetime.ExtractSeconds(date))
23 | fmt.Println("Milliseconds: ", datetime.ExtractMilliseconds(date))
24 | }
--------------------------------------------------------------------------------
/_examples/datetime/operations.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "github.com/ortizdavid/go-nopain/datetime"
6 | )
7 |
8 | func main() {
9 | fmt.Println("Some Operations\n")
10 | fmt.Println("--------------------------")
11 | fmt.Println("Add Days: ", datetime.AddDaysStr("1994-01-01", 12))
12 | fmt.Println("Add Weeks: ", datetime.AddWeeksStr("1994-01-01", 3))
13 | fmt.Println("Add Years: ", datetime.AddYearsStr("1994-01-01", 20))
14 | fmt.Println("Subtract Years: ", datetime.SubtractYearsStr("1994-01-01", 5))
15 | fmt.Println("--------------------------")
16 | fmt.Println("Age: ", datetime.GetAgeStr("1994-01-01"))
17 | fmt.Println("--------------------------")
18 | fmt.Println("Time Ago: ", datetime.TimeAgoStr("2014-01-19 16:20:16"))
19 | fmt.Println("Time Ago Betwen: ", datetime.TimeAgoBetweenStr("2019-01-19 16:20:16", "2023-12-31 23:59:00"))
20 | fmt.Println()
21 | }
--------------------------------------------------------------------------------
/_examples/datetime/validation.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "github.com/ortizdavid/go-nopain/datetime"
6 | )
7 |
8 | func main() {
9 | // Validations
10 | // date
11 | fmt.Println("Validating date")
12 | fmt.Println("1 == ", datetime.IsValidDate("0000-00-00"))
13 | fmt.Println("2 == ", datetime.IsValidDate("2024-09-270"))
14 |
15 | // date time
16 | fmt.Println("Validating date time")
17 | fmt.Println("1 == ", datetime.IsValidDateTime("20000-008-10 10:00:2"))
18 | fmt.Println("2 == ", datetime.IsValidDateTime("2024-09-27 12:54:09"))
19 |
20 | // hour
21 | fmt.Println("Validating hour")
22 | fmt.Println("1 == ", datetime.IsValidHour("1:00:802"))
23 | fmt.Println("2 == ", datetime.IsValidHour("12:54:09"))
24 | }
--------------------------------------------------------------------------------
/_examples/downloader/downloader.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "log"
6 |
7 | "github.com/ortizdavid/go-nopain/filemanager"
8 | )
9 |
10 | func main() {
11 | // Create a new instance of Downloader
12 | downloader := filemanager.NewDownloader()
13 |
14 | // URL of the file to download
15 | url := "https://filesamples.com/samples/document/txt/sample3.txt" // Replace with your desired URL
16 |
17 | // Download the file
18 | downloadInfo, err := downloader.DownloadFile(url)
19 | if err != nil {
20 | log.Fatalf("Error downloading file: %v", err)
21 | }
22 |
23 | // Print download information
24 | fmt.Println("Download Information:")
25 | fmt.Printf("URL: %s\n", downloadInfo.URL)
26 | fmt.Printf("File Name: %s\n", downloadInfo.FileName)
27 | fmt.Printf("Directory: %s\n", downloadInfo.Directory)
28 | fmt.Printf("Size: %d MB\n",downloadInfo.Size)
29 | }
30 |
--------------------------------------------------------------------------------
/_examples/downloader/sample3.txt:
--------------------------------------------------------------------------------
1 | Quod equidem non reprehendo;
2 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quibus natura iure responderit non esse verum aliunde finem beate vivendi, a se principia rei gerendae peti; Quae enim adhuc protulisti, popularia sunt, ego autem a te elegantiora desidero. Duo Reges: constructio interrete. Tum Lucius: Mihi vero ista valde probata sunt, quod item fratri puto. Bestiarum vero nullum iudicium puto. Nihil enim iam habes, quod ad corpus referas; Deinde prima illa, quae in congressu solemus: Quid tu, inquit, huc? Et homini, qui ceteris animantibus plurimum praestat, praecipue a natura nihil datum esse dicemus?
3 |
4 | Iam id ipsum absurdum, maximum malum neglegi. Quod ea non occurrentia fingunt, vincunt Aristonem; Atqui perspicuum est hominem e corpore animoque constare, cum primae sint animi partes, secundae corporis. Fieri, inquam, Triari, nullo pacto potest, ut non dicas, quid non probes eius, a quo dissentias. Equidem e Cn. An dubium est, quin virtus ita maximam partem optineat in rebus humanis, ut reliquas obruat?
5 |
6 | Quis istum dolorem timet?
7 | Summus dolor plures dies manere non potest? Dicet pro me ipsa virtus nec dubitabit isti vestro beato M. Tubulum fuisse, qua illum, cuius is condemnatus est rogatione, P. Quod si ita sit, cur opera philosophiae sit danda nescio.
8 |
9 | Ex eorum enim scriptis et institutis cum omnis doctrina liberalis, omnis historia.
10 | Quod si ita est, sequitur id ipsum, quod te velle video, omnes semper beatos esse sapientes. Cum enim fertur quasi torrens oratio, quamvis multa cuiusque modi rapiat, nihil tamen teneas, nihil apprehendas, nusquam orationem rapidam coerceas. Ita redarguitur ipse a sese, convincunturque scripta eius probitate ipsius ac moribus. At quanta conantur! Mundum hunc omnem oppidum esse nostrum! Incendi igitur eos, qui audiunt, vides. Vide, ne magis, inquam, tuum fuerit, cum re idem tibi, quod mihi, videretur, non nova te rebus nomina inponere. Qui-vere falsone, quaerere mittimus-dicitur oculis se privasse; Si ista mala sunt, in quae potest incidere sapiens, sapientem esse non esse ad beate vivendum satis. At vero si ad vitem sensus accesserit, ut appetitum quendam habeat et per se ipsa moveatur, quid facturam putas?
11 |
12 | Quem si tenueris, non modo meum Ciceronem, sed etiam me ipsum abducas licebit.
13 | Stulti autem malorum memoria torquentur, sapientes bona praeterita grata recordatione renovata delectant.
14 | Esse enim quam vellet iniquus iustus poterat inpune.
15 | Quae autem natura suae primae institutionis oblita est?
16 | Verum tamen cum de rebus grandioribus dicas, ipsae res verba rapiunt;
17 | Hoc est non modo cor non habere, sed ne palatum quidem.
18 | Voluptatem cum summum bonum diceret, primum in eo ipso parum vidit, deinde hoc quoque alienum; Sed tu istuc dixti bene Latine, parum plane. Nam haec ipsa mihi erunt in promptu, quae modo audivi, nec ante aggrediar, quam te ab istis, quos dicis, instructum videro. Fatebuntur Stoici haec omnia dicta esse praeclare, neque eam causam Zenoni desciscendi fuisse. Non autem hoc: igitur ne illud quidem. Ratio quidem vestra sic cogit. Cum audissem Antiochum, Brute, ut solebam, cum M. An quod ita callida est, ut optime possit architectari voluptates?
19 |
20 | Idemne, quod iucunde?
21 | Haec mihi videtur delicatior, ut ita dicam, molliorque ratio, quam virtutis vis gravitasque postulat. Sed quoniam et advesperascit et mihi ad villam revertendum est, nunc quidem hactenus; Cuius ad naturam apta ratio vera illa et summa lex a philosophis dicitur. Neque solum ea communia, verum etiam paria esse dixerunt. Sed nunc, quod agimus; A mene tu?
--------------------------------------------------------------------------------
/_examples/excel-generator/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 | "time"
7 | "github.com/ortizdavid/go-nopain/docgen"
8 | )
9 |
10 |
11 | func main() {
12 | http.HandleFunc("/customers-excel", greet)
13 | http.ListenAndServe(":8080", nil)
14 | }
15 |
16 |
17 | type customer struct {
18 | Name string
19 | Gender string
20 | Age int
21 | }
22 |
23 |
24 | func getAllCustomers() []customer {
25 | return []customer{
26 | {Name: "John Doe", Gender: "Male", Age: 30},
27 | {Name: "Jane Smith", Gender: "Female", Age: 25},
28 | {Name: "Emily Davis", Gender: "Female", Age: 22},
29 | {Name: "Michael Brown", Gender: "Male", Age: 40},
30 | {Name: "Jessica Wilson", Gender: "Female", Age: 35},
31 | {Name: "David Johnson", Gender: "Male", Age: 28},
32 | {Name: "Laura Martinez", Gender: "Female", Age: 32},
33 | {Name: "James Rodriguez", Gender: "Male", Age: 27},
34 | {Name: "Maria Hernandez", Gender: "Female", Age: 29},
35 | {Name: "Robert Clark", Gender: "Male", Age: 45},
36 | }
37 | }
38 |
39 |
40 | func greet(w http.ResponseWriter, r *http.Request) {
41 | excelGenerator := docgen.NewExcelGenerator()
42 | excelGenerator.AddTitle("Customers")
43 |
44 | excelGenerator.AddHeaderRow(
45 | "Name",
46 | "Gender",
47 | "Age",
48 | )
49 |
50 | for _, cu := range getAllCustomers() {
51 | excelGenerator.AddDataRow(
52 | cu.Name,
53 | cu.Gender,
54 | cu.Age,
55 | )
56 | }
57 |
58 | err := excelGenerator.SaveToFile(w, "customers.xlsx")
59 | if err != nil {
60 | panic(err)
61 | }
62 | fmt.Fprintf(w, "Hello World! %s", time.Now())
63 | }
64 |
65 |
--------------------------------------------------------------------------------
/_examples/formatter/formater.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | // FormatCurrency formats the given amount as currency with the specified symbol.
8 | // It takes a float64 amount and a string symbol as input and returns the formatted currency string.
9 | func FormatCurrency(amount float64, symbol string) string {
10 | // Format the amount with two decimal places and append the currency symbol.
11 | return fmt.Sprintf("%s %.2f", symbol, amount)
12 | }
13 |
14 | func main() {
15 | // Define the amount and symbol
16 | amount := 1234.56
17 | symbol := "$"
18 |
19 | // Format the currency using the FormatCurrency function
20 | formatted := FormatCurrency(amount, symbol)
21 |
22 | // Print the formatted currency
23 | fmt.Println("Formatted currency:", formatted)
24 | }
25 |
--------------------------------------------------------------------------------
/_examples/http-client/get.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "github.com/ortizdavid/go-nopain/httputils"
7 | )
8 |
9 | func main() {
10 | client := httputils.NewHttpClient()
11 |
12 | url := "https://dog.ceo/api/breeds/image/random"
13 | resp, err := client.Get(url)
14 | if err != nil {
15 | log.Fatal(err)
16 | }
17 | fmt.Println(resp.StatusCode)
18 | fmt.Println(string(resp.Body))
19 | }
--------------------------------------------------------------------------------
/_examples/http-client/head.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "log"
6 |
7 | "github.com/ortizdavid/go-nopain/httputils"
8 | )
9 |
10 | func main() {
11 | client := httputils.NewHttpClient()
12 |
13 | url := "https://dog.ceo/dog-api/"
14 |
15 | resp, err := client.Head(url)
16 | if err != nil {
17 | log.Fatal(err)
18 | }
19 | fmt.Println(resp.StatusCode)
20 | fmt.Println(string(resp.Body))
21 | }
--------------------------------------------------------------------------------
/_examples/http-client/post.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "github.com/ortizdavid/go-nopain/httputils"
6 | )
7 |
8 | func main() {
9 | client := httputils.NewHttpClient()
10 |
11 | data := `
12 | {
13 | "customerType": 1,
14 | "customerName": "Kelson Firmino",
15 | "identificationNumber": "Q3D95FS672HOA",
16 | "gender": "Female",
17 | "birthDate": "1997-08-01",
18 | "email": "eliandra@gmail.com",
19 | "phone": "+2493698765",
20 | "address": "Luanda, Viana, Zango"
21 | }`
22 |
23 | // Example Post request
24 | response, err := client.Post("http://localhost:5062/api/customers", data)
25 | if err != nil {
26 | fmt.Println("Error:", err)
27 | return
28 | }
29 |
30 | fmt.Println("Status Code:", response.StatusCode)
31 | fmt.Println("Body:", string(response.Body))
32 | }
--------------------------------------------------------------------------------
/_examples/http-client/post_file.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "github.com/ortizdavid/go-nopain/httputils"
6 | )
7 |
8 | func main() {
9 | client := httputils.NewHttpClient()
10 | //client.SetHeader("X-API-KEY", "key123")
11 |
12 | data := "files/patterns.png"
13 |
14 | // Example Post request
15 | response, err := client.Post("http://localhost:8080/upload", data)
16 | if err != nil {
17 | fmt.Println("Error:", err)
18 | return
19 | }
20 |
21 | fmt.Println("Status Code:", response.StatusCode)
22 | fmt.Println("Body:", string(response.Body))
23 | }
--------------------------------------------------------------------------------
/_examples/http-client/test.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "github.com/ortizdavid/go-nopain/httputils"
7 | )
8 |
9 | func main() {
10 | client := httputils.NewHttpClient()
11 | client.SetHeader("X-USER-ID", "fdeb15cb-3431-47e1-aed6-72d6aea822f0")
12 | client.SetHeader("X-API-KEY", "4--GyiF-grNz7vpeD1u1WfkMcC_nsnkNiK3xQMgUskgIyFH6IMvCSSS_CaSkzYtyUIZi7AKKtHJGRdAJsTiXiMgayza0UGkRTPWTuxf9OW5BmApXJZbWnL-kM8_rLFWMiAlWsw")
13 |
14 | url := "http://127.0.0.1:4003/api/configurations/basic-configurations"
15 | resp, err := client.Get(url)
16 | if err != nil {
17 | log.Fatal(err)
18 | }
19 | fmt.Println(resp.StatusCode)
20 | fmt.Println(string(resp.Body))
21 | }
--------------------------------------------------------------------------------
/_examples/pagination/default.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "math/rand"
6 | "net/http"
7 | "time"
8 | "github.com/ortizdavid/go-nopain/conversion"
9 | "github.com/ortizdavid/go-nopain/httputils"
10 | )
11 |
12 | type Product struct {
13 | Id int
14 | Name string
15 | Price float32
16 | }
17 |
18 | var productList []Product
19 |
20 | func GenerateProducts(qtd int) error {
21 | rand.Seed(time.Now().UnixNano())
22 | var products []Product
23 | for i := 1; i <= qtd; i++ {
24 | product := Product{
25 | Id: i,
26 | Name: fmt.Sprintf("Product %d", i),
27 | Price: rand.Float32() * 100,
28 | }
29 | products = append(products, product)
30 | }
31 | productList = products
32 | return nil
33 | }
34 |
35 | func getProductsLimit(start int, end int) ([]Product, error) {
36 | if start < 0 || end > len(productList) || start >= end {
37 | return nil, fmt.Errorf("invalid range: start=%d, end=%d", start, end)
38 | }
39 | return productList[start:end], nil
40 | }
41 |
42 | func listProductHandler(w http.ResponseWriter, r *http.Request) {
43 | currentPage := r.URL.Query().Get("current_page")
44 | limit := r.URL.Query().Get("limit")
45 |
46 | if currentPage == "" {
47 | currentPage = "1"
48 | }
49 | if limit == "" {
50 | limit = "10"
51 | }
52 |
53 | GenerateProducts(20)
54 |
55 | index := conversion.StringToInt(currentPage)
56 | size := conversion.StringToInt(limit)
57 |
58 | start := (index - 1) * size
59 | end := start + size
60 |
61 | products, err := getProductsLimit(start, end)
62 | if err != nil {
63 | http.Error(w, err.Error(), http.StatusBadRequest)
64 | return
65 | }
66 |
67 | count := len(productList)
68 |
69 | pagination, err := httputils.NewPagination(r, products, count, index, size)
70 | if err != nil {
71 | http.Error(w, err.Error(), http.StatusInternalServerError)
72 | return
73 | }
74 |
75 | httputils.WriteJson(w, http.StatusOK, pagination)
76 | }
77 |
78 | func main() {
79 | mux := http.NewServeMux()
80 | mux.HandleFunc("/products", listProductHandler)
81 | http.ListenAndServe(":4000", mux)
82 | }
83 |
--------------------------------------------------------------------------------
/_examples/pagination/pagination-json.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 | "time"
7 | "github.com/ortizdavid/go-nopain/conversion"
8 | "github.com/ortizdavid/go-nopain/httputils"
9 | "github.com/ortizdavid/go-nopain/random"
10 | )
11 |
12 | type Product struct {
13 | Id int
14 | Name string
15 | Price float32
16 | }
17 |
18 | var productList []Product
19 |
20 | func GenerateProducts(qtd int) error {
21 | var products []Product
22 | for i := 1; i <= qtd; i++ {
23 | product := Product{
24 | Id: i,
25 | Name: fmt.Sprintf("Product %d", i),
26 | Price: float32(random.Float64(100.5, 12977.99)),
27 | }
28 | products = append(products, product)
29 | }
30 | productList = products
31 | return nil
32 | }
33 |
34 | func getProductsLimit(start int, end int) ([]Product, error) {
35 | if start < 0 || end > len(productList) || start >= end {
36 | return nil, fmt.Errorf("invalid range: start=%d, end=%d", start, end)
37 | }
38 | return productList[start:end], nil
39 | }
40 |
41 | func getProductsHandler(w http.ResponseWriter, r *http.Request) {
42 | currentPage := r.URL.Query().Get("current_page")
43 | limit := r.URL.Query().Get("limit")
44 |
45 | if currentPage == "" { currentPage = "1" }
46 | if limit == "" { limit = "5" }
47 |
48 | GenerateProducts(20)
49 |
50 | index := conversion.StringToInt(currentPage)
51 | size := conversion.StringToInt(limit)
52 |
53 | start := (index - 1) * size
54 | end := start + size
55 |
56 | products, err := getProductsLimit(start, end)
57 | if err != nil {
58 | http.Error(w, err.Error(), http.StatusBadRequest)
59 | return
60 | }
61 |
62 | count := len(productList)
63 | httputils.WriteJsonPaginated(w, r, products, count, index, size)
64 | }
65 |
66 | // Start the server
67 | func main() {
68 | mux := http.NewServeMux()
69 |
70 | mux.HandleFunc("/products", getProductsHandler)
71 |
72 | http.ListenAndServe(":4000", mux)
73 | }
74 |
--------------------------------------------------------------------------------
/_examples/pagination/pagination-xml.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "math/rand"
6 | "net/http"
7 | "time"
8 | "github.com/ortizdavid/go-nopain/conversion"
9 | "github.com/ortizdavid/go-nopain/httputils"
10 | )
11 |
12 | type Product struct {
13 | Id int
14 | Name string
15 | Price float32
16 | }
17 |
18 | var productList []Product
19 |
20 | func GenerateProducts(qtd int) error {
21 | rand.Seed(time.Now().UnixNano())
22 | var products []Product
23 | for i := 1; i <= qtd; i++ {
24 | product := Product{
25 | Id: i,
26 | Name: fmt.Sprintf("Product %d", i),
27 | Price: rand.Float32() * 100,
28 | }
29 | products = append(products, product)
30 | }
31 | productList = products
32 | return nil
33 | }
34 |
35 | func getProductsLimit(start int, end int) ([]Product, error) {
36 | if start < 0 || end > len(productList) || start >= end {
37 | return nil, fmt.Errorf("invalid range: start=%d, end=%d", start, end)
38 | }
39 | return productList[start:end], nil
40 | }
41 |
42 | func listProductHandler(w http.ResponseWriter, r *http.Request) {
43 | currentPage := r.URL.Query().Get("current_page")
44 | limit := r.URL.Query().Get("limit")
45 |
46 | if currentPage == "" { currentPage = "1" }
47 | if limit == "" { limit = "5" }
48 |
49 | GenerateProducts(20)
50 |
51 | index := conversion.StringToInt(currentPage)
52 | size := conversion.StringToInt(limit)
53 |
54 | start := (index - 1) * size
55 | end := start + size
56 | count := len(productList)
57 |
58 | products, err := getProductsLimit(start, end)
59 | if err != nil {
60 | http.Error(w, err.Error(), http.StatusBadRequest)
61 | return
62 | }
63 |
64 | httputils.WriteXmlPaginated(w, r, products, count, index, size)
65 | }
66 |
67 | func main() {
68 | mux := http.NewServeMux()
69 | mux.HandleFunc("/products", listProductHandler)
70 | http.ListenAndServe(":4000", mux)
71 | }
72 |
--------------------------------------------------------------------------------
/_examples/pdf-generator/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "net/http"
5 | "github.com/ortizdavid/go-nopain/docgen"
6 | "github.com/ortizdavid/go-nopain/httputils"
7 | )
8 |
9 |
10 | func main() {
11 | http.HandleFunc("/simple-pdf", simplePdfHandler)
12 | http.ListenAndServe(":8080", nil)
13 | }
14 |
15 |
16 | func simplePdfHandler(w http.ResponseWriter, r *http.Request) {
17 | var pdfGen docgen.HtmlPdfGenerator
18 |
19 | data := map[string]interface{}{
20 | "Title": "Simple PDF",
21 | }
22 | pdfBytes, err := pdfGen.GeneratePDF("templates/simple-pdf.html", data)
23 | if err != nil {
24 | httputils.WriteJsonError(w, err.Error(), http.StatusInternalServerError)
25 | return
26 | }
27 | pdfGen.SetOutput(w, pdfBytes, "simple.pdf")
28 | }
29 |
30 |
31 | func tableReportHandler(w http.ResponseWriter, r *http.Request) {
32 | }
--------------------------------------------------------------------------------
/_examples/pdf-generator/templates/simple-pdf.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Simple PDF
7 |
8 |
9 |
10 | This is a simple Html to PDF Report
11 | Using go.wkhtmltopdf Library
12 |
13 |
14 |
--------------------------------------------------------------------------------
/_examples/pdf-generator/templates/table-report.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | {{ .Title }}
7 |
27 |
28 |
29 | {{ .Title }}
30 | Count: {{ .Count }}
31 | {{ if .Count }}
32 |
33 |
34 | Id |
35 | Name |
36 | Price |
37 |
38 | {{ range .ProductList }}
39 |
40 | {{ .Id }} |
41 | {{ .Name }} |
42 | {{ .Price }} |
43 |
44 | {{ end }}
45 |
46 | {{ else }}
47 |
48 |
No Records Found!
49 |
50 | {{ end }}
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/_examples/pubsub/helpers/functions.go:
--------------------------------------------------------------------------------
1 | package helpers
2 |
3 | import (
4 | "fmt"
5 | "time"
6 | "github.com/ortizdavid/go-nopain/filemanager"
7 | )
8 |
9 | type GolangMessage struct {
10 | Text string `json:"text"`
11 | Number int `json:"number"`
12 | Boolean bool `json:"boolean"`
13 | }
14 |
15 | var slices []GolangMessage
16 |
17 | func PrintMessage(msg GolangMessage) error {
18 | fmt.Println(msg)
19 | return nil
20 | }
21 |
22 | func AddMessageToSlice(msg GolangMessage) error {
23 | slices = append(slices, msg)
24 | return nil
25 | }
26 |
27 | func SaveMessageToFile(msg GolangMessage) error {
28 | var filemanager filemanager.FileManager
29 | currentTime := time.Now().Format("2006-01-02 15:04:05")
30 | newContent := fmt.Sprintf("[%s] --> %d\t%s\t%v\n", currentTime, msg.Number, msg.Text, msg.Boolean)
31 | filemanager.WriteFile(".", "../helpers/messages.txt", newContent)
32 | return nil
33 | }
34 |
--------------------------------------------------------------------------------
/_examples/pubsub/helpers/messages.txt:
--------------------------------------------------------------------------------
1 | [2025-03-21 08:28:53] --> 34 Ortiz David true
2 | [2025-03-21 08:28:57] --> 34 Ortiz David true
3 | [2025-03-21 08:29:28] --> 34 Ortiz David true
4 | [2025-03-21 08:29:30] --> 34 Ortiz David true
5 | [2025-03-21 08:29:32] --> 34 Ortiz David true
6 | [2025-03-21 08:29:52] --> 34 Ortiz David true
7 | [2025-03-21 08:29:54] --> 34 Ortiz David true
8 |
--------------------------------------------------------------------------------
/_examples/pubsub/nats/process_message.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "log"
6 |
7 | "github.com/ortizdavid/go-nopain/_examples/pubsub/helpers"
8 | pubsub "github.com/ortizdavid/go-nopain/pubsub/nats"
9 | "github.com/ortizdavid/go-nopain/reflection"
10 | )
11 |
12 | func main() {
13 | subscriber, err := pubsub.NewNatsSubscriberDefault()
14 | if err != nil {
15 | log.Println(err)
16 | }
17 | defer subscriber.Close()
18 |
19 | err = pubsub.ProcessMessage(subscriber, "golang_test", helpers.SaveMessageToFile)
20 | if err != nil {
21 | log.Println(err)
22 | }
23 | select {}
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/_examples/pubsub/nats/publish.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 |
6 | "github.com/ortizdavid/go-nopain/_examples/pubsub/helpers"
7 | pubsub "github.com/ortizdavid/go-nopain/pubsub/nats"
8 | )
9 |
10 | func main() {
11 |
12 | publisher, err := pubsub.NewNatsPublisherDefault()
13 | if err != nil {
14 | log.Println(err)
15 | }
16 | defer publisher.Close()
17 |
18 | message := helpers.GolangMessage{
19 | Text: "Ortiz David",
20 | Number: 34,
21 | Boolean: true,
22 | }
23 |
24 | err = publisher.Publish("golang_test", message)
25 | if err != nil {
26 | log.Println(err)
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/_examples/pubsub/nats/subscribe.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 |
6 | pubsub "github.com/ortizdavid/go-nopain/pubsub/nats"
7 | )
8 |
9 | func main() {
10 |
11 | subscriber, err := pubsub.NewNatsSubscriberDefault()
12 | if err != nil {
13 | log.Println(err)
14 | }
15 | defer subscriber.Close()
16 |
17 | err = subscriber.Subscribe("golang_test")
18 | if err != nil {
19 | log.Println(err)
20 | }
21 |
22 | select {}
23 | }
--------------------------------------------------------------------------------
/_examples/pubsub/nats/subscribe_queue.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 |
6 | pubsub "github.com/ortizdavid/go-nopain/pubsub/nats"
7 | )
8 |
9 | func main() {
10 | subscriber, err := pubsub.NewNatsSubscriberDefault()
11 | if err != nil {
12 | log.Println(err)
13 | }
14 | defer subscriber.Close()
15 |
16 | err = subscriber.SubscribeQueue("golang_test", "test.queue")
17 | if err != nil {
18 | log.Println(err)
19 | }
20 |
21 | select {}
22 |
23 | }
--------------------------------------------------------------------------------
/_examples/pubsub/rabbitmq/consume_exchange.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 |
6 | "github.com/ortizdavid/go-nopain/pubsub/rabbitmq"
7 | )
8 |
9 | func main() {
10 | consumeFromExchange()
11 | }
12 |
13 | func consumeFromExchange() {
14 |
15 | rmq2, _ := pubsub.NewRabbitMQConsumerDefault()
16 |
17 | err := rmq2.ConsumeFromExchange(pubsub.DefaultExchange("user.exchange"), "user.updated")
18 | if err != nil {
19 | log.Println(err)
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/_examples/pubsub/rabbitmq/consume_queue.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 | "github.com/ortizdavid/go-nopain/pubsub/rabbitmq"
6 | )
7 |
8 | func main() {
9 | rmq2, _ := pubsub.NewRabbitMQConsumerDefault()
10 |
11 | err := rmq2.ConsumeFromQueue(pubsub.DefaultQueue("golang_queue"))
12 | if err != nil {
13 | log.Println(err)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/_examples/pubsub/rabbitmq/process_exchange.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 | "github.com/ortizdavid/go-nopain/_examples/pubsub/helpers"
6 | pubsub "github.com/ortizdavid/go-nopain/pubsub/rabbitmq"
7 | )
8 |
9 | func processMessageFromExchange() {
10 |
11 | consumer, _ := pubsub.NewRabbitMQConsumerDefault()
12 |
13 | err := pubsub.ProcessMessageFromExchange(consumer, pubsub.DefaultExchange("golang_exchange"), "golang_key", helpers.AddMessageToSlice)
14 | if err != nil {
15 | log.Println(err)
16 | }
17 | }
18 |
19 | func main() {
20 | processMessageFromExchange()
21 | }
22 |
--------------------------------------------------------------------------------
/_examples/pubsub/rabbitmq/process_queue.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 |
6 | "github.com/ortizdavid/go-nopain/_examples/pubsub/helpers"
7 | pubsub "github.com/ortizdavid/go-nopain/pubsub/rabbitmq"
8 | )
9 |
10 | func processMessageFromQueue() {
11 |
12 | consumer, _ := pubsub.NewRabbitMQConsumerDefault()
13 |
14 | err := pubsub.ProcessMessageFromQueue(consumer, pubsub.DefaultQueue("golang_queue"), helpers.SaveMessageToFile)
15 | if err != nil {
16 | log.Println(err)
17 | }
18 | }
19 |
20 | func main() {
21 | processMessageFromQueue()
22 | }
23 |
--------------------------------------------------------------------------------
/_examples/pubsub/rabbitmq/publish_exchange.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 | "github.com/ortizdavid/go-nopain/_examples/pubsub/helpers"
6 | pubsub "github.com/ortizdavid/go-nopain/pubsub/rabbitmq"
7 | )
8 |
9 | func publishToExchange() {
10 | message := helpers.GolangMessage{
11 | Text: "Message for para Exchange",
12 | Number: 82736,
13 | Boolean: false,
14 | }
15 |
16 | producer, _ := pubsub.NewRabbitMQProducerDefault()
17 |
18 | err := producer.PublishToExchange(pubsub.DefaultExchange("golang_exchange"), "golang_key", message)
19 | if err != nil {
20 | log.Println(err)
21 | }
22 | }
23 |
24 | func main() {
25 | publishToExchange()
26 | }
27 |
--------------------------------------------------------------------------------
/_examples/pubsub/rabbitmq/publish_queue.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "log"
5 |
6 | "github.com/ortizdavid/go-nopain/_examples/pubsub/helpers"
7 | pubsub "github.com/ortizdavid/go-nopain/pubsub/rabbitmq"
8 | )
9 |
10 | func main() {
11 | message := helpers.GolangMessage{
12 | Text: "New message for queue",
13 | Number: 12,
14 | Boolean: false,
15 | }
16 | producer, _ := pubsub.NewRabbitMQProducerDefault()
17 |
18 | err := producer.PublishToQueue(pubsub.DefaultQueue("golang_queue"), message)
19 | if err != nil {
20 | log.Println(err)
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/_examples/reflection/inspect.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | func main() {
4 |
5 | }
--------------------------------------------------------------------------------
/_examples/reflection/tags.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/ortizdavid/go-nopain/reflection"
7 | )
8 |
9 | type User struct {
10 | ID int `json:"id"`
11 | Name string `json:"name"`
12 | Email string `json:"email"`
13 | Age int `numb:"age"`
14 | }
15 |
16 | func main() {
17 | fmt.Println(reflection.HasTag(User{}, "ID", "json"))
18 | fmt.Println(reflection.HasTag(User{}, "Name", "json"))
19 | fmt.Println(reflection.HasTag(User{}, "Age", "json"))
20 |
21 | fmt.Println(reflection.GetTag(User{}, "Email", "json"))
22 | fmt.Println(reflection.GetTag(User{}, "ID", "json"))
23 | fmt.Println(reflection.GetTag(User{}, "Name", "json"))
24 | }
--------------------------------------------------------------------------------
/_examples/reflection/var_dump.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/ortizdavid/go-nopain/reflection"
7 | )
8 |
9 | type Object struct {
10 | Text string
11 | Number int
12 | Boolean bool
13 | }
14 |
15 | func main() {
16 | obj := Object{"hello", 123, true}
17 | slice := []int{1, 2, 3}
18 | arr := [4]float32{0.9, 1, -4.6, 90.3}
19 | map1 := map[string]any{"a": 10, "b": 3.8, "c": true}
20 |
21 | fmt.Println("Using 'VarDumpBasic'")
22 | fmt.Println("-----------------------------------------------------")
23 | reflection.VarDumpBasic(1, "test", 23.4, obj, slice, arr, map1, nil)
24 | fmt.Println()
25 |
26 | fmt.Println("Using Detailed 'VarDump'")
27 | fmt.Println("-----------------------------------------------------")
28 | reflection.VarDump(1, "test", 23.4, obj, slice, arr, map1, nil)
29 | }
--------------------------------------------------------------------------------
/_examples/serialization/from_file.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "log"
6 |
7 | "github.com/ortizdavid/go-nopain/serialization"
8 | )
9 |
10 | // Define a struct to represent a CSV record
11 | type Record struct {
12 | ID int
13 | Name string
14 | Age int
15 | }
16 |
17 | func main() {
18 | // Example usage of FromJsonFile
19 | /*var jsonData []Record
20 | err := serialization.FromJsonFile("files/data.json", &jsonData)
21 | if err != nil {
22 | log.Fatalf("Error converting form JSON file: %v", err)
23 | }
24 | fmt.Println("JSON Data:", jsonData)*/
25 |
26 | // Example usage of FromXmlFile
27 | var xmlData []Record
28 | err := serialization.FromXmlFile("files/data.xml", &xmlData)
29 | if err != nil {
30 | log.Fatalf("Error converting from XML file: %v", err)
31 | }
32 | fmt.Println("XML Data:", xmlData)
33 | }
34 |
--------------------------------------------------------------------------------
/_examples/serialization/json.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/ortizdavid/go-nopain/serialization"
7 | )
8 |
9 | type message struct {
10 | Text string
11 | Number int
12 | Boolean bool
13 | }
14 |
15 |
16 | func main() {
17 | //jsonSerialization()
18 | jsonUnserialization()
19 | }
20 |
21 | func jsonSerialization() {
22 | data := message{
23 | Text: "hello",
24 | Number: 123,
25 | Boolean: true,
26 | }
27 |
28 | jsonData, _ := serialization.SerializeJson(data)
29 | fmt.Println(string(jsonData))
30 | }
31 |
32 | func jsonUnserialization() {
33 | j := `{
34 | "Text": "hello",
35 | "Number": 123,
36 | "Boolean": true
37 | }
38 | `
39 | var msg message
40 |
41 | err := serialization.UnserializeJson([]byte(j), &msg)
42 | if err != nil {
43 | fmt.Println(err)
44 | }
45 | fmt.Println(msg)
46 | }
47 |
--------------------------------------------------------------------------------
/_examples/serialization/rest_api.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "net/http"
5 | "github.com/ortizdavid/go-nopain/httputils"
6 | )
7 |
8 | func greet(w http.ResponseWriter, r *http.Request) {
9 | httputils.WriteXml(w, 200,
10 | struct{
11 | Text string `json:"text"`
12 | Number int `json:"number"`
13 | Boolean bool `json:"boolean"`
14 | }{
15 | Text: "hello",
16 | Number: 123,
17 | Boolean: true,
18 | }, 1)
19 | }
20 |
21 | func main() {
22 | http.HandleFunc("/", greet)
23 | http.ListenAndServe(":8080", nil)
24 | }
--------------------------------------------------------------------------------
/_examples/serialization/transform.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/ortizdavid/go-nopain/serialization"
7 | )
8 |
9 | func main() {
10 |
11 | xml := `
12 | Andre
13 | 23
14 | `
15 |
16 |
17 | jsonData, _ := serialization.XmlToJson([]byte(xml))
18 | fmt.Println(string(jsonData))
19 | }
--------------------------------------------------------------------------------
/_examples/serialization/xml.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "encoding/xml"
6 | "github.com/ortizdavid/go-nopain/serialization"
7 | )
8 |
9 |
10 | type person struct {
11 | XMLName xml.Name `xml:"person"`
12 | Name string `xml:"name"`
13 | Age int `xml:"age"`
14 | }
15 |
16 | func main() {
17 |
18 | //jsonSerialization()
19 | //xmlSerialization()
20 | xmlUnserialization()
21 | }
22 |
23 | func xmlSerialization() {
24 | data2 := person{
25 | XMLName: xml.Name{},
26 | Name: "Andre",
27 | Age: 23,
28 | }
29 |
30 | xmlData, _ := serialization.SerializeXml(data2)
31 | fmt.Println(string(xmlData))
32 | }
33 |
34 | func xmlUnserialization() {
35 | x := `
36 | Andre
37 | 23
38 |
39 | `
40 | var p person
41 | serialization.UnserializeXml([]byte(x), &p)
42 | fmt.Println(p)
43 | }
--------------------------------------------------------------------------------
/_examples/sessions/gorilla.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 | "time"
7 | "github.com/ortizdavid/go-nopain/httputils"
8 | "github.com/ortizdavid/go-nopain/random"
9 | )
10 |
11 | // Create a new gorilla session store
12 | var store = httputils.NewGorillaStore("secret-key")
13 | var session = httputils.NewSessionGorilla(store, "session-name", 10 * time.Second)
14 |
15 | func main() {
16 | http.HandleFunc("/set", set)
17 | http.HandleFunc("/get", get)
18 | http.HandleFunc("/remove", remove)
19 | http.HandleFunc("/destroy", destroy)
20 | fmt.Println("Starting server on :8081")
21 | http.ListenAndServe(":8081", nil)
22 | }
23 |
24 | func set(w http.ResponseWriter, r *http.Request) {
25 | err := session.Set(r, w, "username", "Anna")
26 | session.Set(r, w, "password", "Anna123$")
27 | session.Set(r, w, "company", "company .Corp")
28 | session.Set(r, w, "session_id", random.String(20))
29 | if err != nil {
30 | http.Error(w, err.Error(), http.StatusInternalServerError)
31 | return
32 | }
33 | fmt.Fprintln(w, "Session set successfully")
34 | }
35 |
36 | func get(w http.ResponseWriter, r *http.Request) {
37 | username, _ := session.Get(r, "user_name")
38 | password, _ := session.Get(r, "password")
39 | empresa, _ := session.Get(r, "company")
40 | sessionId, _ := session.Get(r, "session_id")
41 | fmt.Fprintln(w, "Username:", username)
42 | fmt.Fprintln(w, "Password:", password)
43 | fmt.Fprintln(w, "Company:", empresa)
44 | fmt.Fprintln(w, "Session Id:", sessionId)
45 | }
46 |
47 | func remove(w http.ResponseWriter, r *http.Request) {
48 | err := session.Remove(r, w, "user_name")
49 | if err != nil {
50 | http.Error(w, err.Error(), http.StatusInternalServerError)
51 | return
52 | }
53 | fmt.Fprintln(w, "Username removed from session")
54 | }
55 |
56 | func destroy(w http.ResponseWriter, r *http.Request) {
57 | err := session.Destroy(r, w)
58 | if err != nil {
59 | http.Error(w, err.Error(), http.StatusInternalServerError)
60 | return
61 | }
62 | fmt.Fprintln(w, "Session destroyed")
63 | }
--------------------------------------------------------------------------------
/_examples/sessions/http.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 | "time"
7 | "github.com/ortizdavid/go-nopain/httputils"
8 | )
9 |
10 | // Create a new global SessionHttp instance with a custom cookie name and expiration
11 | var session = httputils.NewSessionHttp("mySessionCookie", 30*time.Second)
12 |
13 | func main() {
14 | http.HandleFunc("/set", set)
15 | http.HandleFunc("/get", get)
16 | http.HandleFunc("/set-expiration", setExpiration)
17 | http.HandleFunc("/remove", remove)
18 | http.HandleFunc("/destroy", destroy)
19 | fmt.Println("Starting server on :8080")
20 | http.ListenAndServe(":8080", nil)
21 | }
22 |
23 | func set(w http.ResponseWriter, r *http.Request) {
24 | // Set a session value
25 | err := session.Set(w, "user_name", "john_doe")
26 | if err != nil {
27 | http.Error(w, "Unable to set session value", http.StatusInternalServerError)
28 | return
29 | }
30 | fmt.Fprintln(w, "Session value set")
31 | }
32 |
33 | func get(w http.ResponseWriter, r *http.Request) {
34 | // Get a session value
35 | value, err := session.Get(r, "user_name")
36 | if err != nil {
37 | http.Error(w, "Unable to get session value", http.StatusInternalServerError)
38 | return
39 | }
40 | if value == "" {
41 | fmt.Fprintln(w, "No session value found")
42 | } else {
43 | fmt.Fprintf(w, "Session value: %s\n", value)
44 | }
45 | }
46 |
47 | func setExpiration(w http.ResponseWriter, r *http.Request) {
48 | // Change session expiration
49 | session.SetExpiration(30 * time.Minute)
50 | fmt.Fprintln(w, "Session expiration set to 30 minutes")
51 | }
52 |
53 | func remove(w http.ResponseWriter, r *http.Request) {
54 | // Remove a session value
55 | err := session.Remove(w, "user_name")
56 | if err != nil {
57 | http.Error(w, "Unable to remove session value", http.StatusInternalServerError)
58 | return
59 | }
60 | fmt.Fprintln(w, "Session value removed")
61 | }
62 |
63 | func destroy(w http.ResponseWriter, r *http.Request) {
64 | // Destroy the session
65 | err := session.Destroy(r, w)
66 | if err != nil {
67 | http.Error(w, "Unable to destroy session", http.StatusInternalServerError)
68 | return
69 | }
70 | fmt.Fprintln(w, "Session destroyed")
71 | }
--------------------------------------------------------------------------------
/_examples/uploader/templates/upload-multiple.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | File Upload
7 |
8 |
9 | Upload many Files
10 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/_examples/uploader/templates/upload.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | File Upload
7 |
8 |
9 | Upload single File
10 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/_examples/uploader/uploader-multiple.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 | "time"
7 |
8 | "github.com/ortizdavid/go-nopain/filemanager"
9 | )
10 |
11 | func main() {
12 | // Define upload configurations
13 | destinationPath := "./uploads"
14 | maxSize := 5 // Maximum file size in MB
15 | allowedExtensions := filemanager.ExtImages // Allow only image files
16 |
17 | // Create a new Uploader instance
18 | uploader := filemanager.NewUploader(destinationPath, maxSize, allowedExtensions)
19 | // Define an HTTP handler function for file upload
20 | http.HandleFunc("/upload", func(w http.ResponseWriter, r *http.Request) {
21 | if r.Method != http.MethodPost {
22 | fmt.Fprintf(w, "Only POST requests allowed")
23 | return
24 | }
25 |
26 | // Upload multiple files using UploadMultipleFiles
27 | uploadInfos, err := uploader.UploadMultipleFiles(r, "files") // Assuming your form field name is "files" (for multiple files)
28 | if err != nil {
29 | fmt.Fprintf(w, "Error uploading files: %v", err)
30 | return
31 | }
32 |
33 | fmt.Fprintf(w, "Files uploaded successfully!\n")
34 | for _, info := range uploadInfos {
35 | fmt.Fprintf(w, " - Original name: %s\n - Final name: %s\n - Size: %d bytes\n - Content type: %s\n - Uploaded at: %s\n",
36 | info.OriginalFileName, info.FinalName, info.Size, info.ContentType, info.UploadTime.Format(time.RFC3339))
37 | }
38 | })
39 |
40 | // Start the HTTP server
41 | fmt.Println("Server listening on port 8090")
42 | http.ListenAndServe(":8090", nil)
43 | }
44 |
--------------------------------------------------------------------------------
/_examples/uploader/uploader-single.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 | "time"
7 | "github.com/ortizdavid/go-nopain/filemanager"
8 | )
9 |
10 | func main() {
11 | // Define upload configurations
12 | destinationPath := "./uploads"
13 | maxSize := 5 // Maximum file size in MB
14 | allowedExtensions := filemanager.ExtImages // Allow only image files
15 |
16 | // Create a new Uploader instance
17 | uploader := filemanager.NewUploader(destinationPath, maxSize, allowedExtensions)
18 |
19 | // Define an HTTP handler function for file upload
20 | http.HandleFunc("/upload", func(w http.ResponseWriter, r *http.Request) {
21 | if r.Method != http.MethodPost {
22 | fmt.Fprintf(w, "Only POST requests allowed")
23 | return
24 | }
25 |
26 | // Upload single file using UploadSingleFile
27 | fileInfo, err := uploader.UploadSingleFile(r, "file") // Assuming your form field name is "file"
28 | if err != nil {
29 | fmt.Fprintf(w, "Error uploading file: %v", err)
30 | return
31 | }
32 |
33 | fmt.Fprintf(w, "File uploaded successfully!\nDetails:\n - Original name: %s\n - Final name: %s\n - Size: %d bytes\n - Content type: %s\n - Uploaded at: %s\n",
34 | fileInfo.OriginalFileName, fileInfo.FinalName, fileInfo.Size, fileInfo.ContentType, fileInfo.UploadTime.Format(time.RFC3339))
35 | })
36 |
37 | // Start the HTTP server
38 | fmt.Println("Server listening on port 8080")
39 | http.ListenAndServe(":8080", nil)
40 | }
41 |
42 |
--------------------------------------------------------------------------------
/calculations/README.md:
--------------------------------------------------------------------------------
1 | # calculations
2 |
3 | Package for simple calculations
4 |
5 | ## Features
6 | - [x] Percents
--------------------------------------------------------------------------------
/calculations/percentage.go:
--------------------------------------------------------------------------------
1 | package calculations
2 |
3 | // PercentageOfvalue calculates the percentage of a given value.
4 | // It takes the original value and the percentage to calculate.
5 | func PercentageOfValue(originalValue float32, percentage float32) float32 {
6 | return (percentage * originalValue) / 100
7 | }
8 |
9 | // alueFromPercentage calculates the original value based on a given percentage and a calculated value.
10 | // It takes the percentage and the calculated value as parameters.
11 | func ValueFromPercentage(percentage float32, calculatedValue float32) float32 {
12 | return (calculatedValue * 100) / percentage
13 | }
14 |
--------------------------------------------------------------------------------
/calculations/percentage_test.go:
--------------------------------------------------------------------------------
1 | package calculations
2 |
3 | import "testing"
4 |
5 |
6 | func Test_PercentageOfValue(t *testing.T) {
7 |
8 | }
9 |
10 |
11 | func Test_ValueOfPercentage(t *testing.T) {
12 |
13 | }
--------------------------------------------------------------------------------
/collections/README.md:
--------------------------------------------------------------------------------
1 | # collections
2 | Package with most common data structures/collections and operations
3 |
4 | ## collections
5 | - [ ] Arrays
6 | - [ ] Slices
7 | - [ ] Maps
8 | - [ ] Stacks
9 | - [ ] Queues
10 | - [ ] Lists
11 | - [ ] Other functions/helpers
--------------------------------------------------------------------------------
/collections/functions.go:
--------------------------------------------------------------------------------
1 | package collections
2 |
3 | func ContainsString(stringSlice []string, str string) bool {
4 | for _, item := range stringSlice {
5 | if str == item {
6 | return true
7 | }
8 | }
9 | return false
10 | }
--------------------------------------------------------------------------------
/conversion/README.md:
--------------------------------------------------------------------------------
1 | # conversion
2 |
3 | Package for Type conversions
4 |
5 | ## Conversions
6 | - [x] String to number
7 | - [x] Number to string
8 | - [x] Strings to Number Nil values
9 | - [x] Nil to String or Number
10 | - [ ] Tests
--------------------------------------------------------------------------------
/conversion/any_type.go:
--------------------------------------------------------------------------------
1 | package conversion
2 |
3 | import "fmt"
4 |
5 | // interface{} -> string.
6 | func AnyToString(value interface{}) string {
7 | return fmt.Sprintf("%v", value)
8 | }
9 |
--------------------------------------------------------------------------------
/conversion/any_type_test.go:
--------------------------------------------------------------------------------
1 | package conversion
2 |
3 | import "testing"
4 |
5 | func Test_AnyToString(t *testing.T) {
6 | // Test case for int
7 | var intValue interface{} = 123
8 | expectedInt := "123"
9 | resultInt := AnyToString(intValue)
10 | if resultInt != expectedInt {
11 | t.Errorf("AnyToString(%v) returned %s, expected %s", intValue, resultInt, expectedInt)
12 | }
13 |
14 | // Test case for float
15 | var floatValue interface{} = 123.45
16 | expectedFloat := "123.45"
17 | resultFloat := AnyToString(floatValue)
18 | if resultFloat != expectedFloat {
19 | t.Errorf("AnyToString(%v) returned %s, expected %s", floatValue, resultFloat, expectedFloat)
20 | }
21 |
22 | // Test case for string
23 | var strValue interface{} = "hello"
24 | expectedStr := "hello"
25 | resultStr := AnyToString(strValue)
26 | if resultStr != expectedStr {
27 | t.Errorf("AnyToString(%v) returned %s, expected %s", strValue, resultStr, expectedStr)
28 | }
29 |
30 | // Test case for boolean
31 | var boolValue interface{} = true
32 | expectedBool := "true"
33 | resultBool := AnyToString(boolValue)
34 | if resultBool != expectedBool {
35 | t.Errorf("AnyToString(%v) returned %s, expected %s", boolValue, resultBool, expectedBool)
36 | }
37 |
38 | // Test case for slice
39 | var sliceValue interface{} = []int{1, 2, 3}
40 | expectedSlice := "[1 2 3]"
41 | resultSlice := AnyToString(sliceValue)
42 | if resultSlice != expectedSlice {
43 | t.Errorf("AnyToString(%v) returned %s, expected %s", sliceValue, resultSlice, expectedSlice)
44 | }
45 |
46 | // Test case for struct
47 | type testStruct struct {
48 | Name string
49 | Age int
50 | }
51 | var structValue interface{} = testStruct{Name: "John", Age: 30}
52 | expectedStruct := "{John 30}"
53 | resultStruct := AnyToString(structValue)
54 | if resultStruct != expectedStruct {
55 | t.Errorf("AnyToString(%v) returned %s, expected %s", structValue, resultStruct, expectedStruct)
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/conversion/boolean.go:
--------------------------------------------------------------------------------
1 | package conversion
2 |
3 | import "strconv"
4 |
5 | func StringToBool(strBool string) (bool, error) {
6 | return strconv.ParseBool(strBool)
7 | }
8 |
9 | func BoolToString(value bool) string {
10 | if value {
11 | return "true"
12 | }
13 | return "false"
14 | }
--------------------------------------------------------------------------------
/conversion/float.go:
--------------------------------------------------------------------------------
1 | package conversion
2 |
3 | import (
4 | "strconv"
5 | )
6 |
7 | // float32 -> string
8 | func Float32ToString(number float32) string {
9 | return strconv.FormatFloat(float64(number), 'f', -1, 32)
10 | }
11 |
12 | // float32 -> int
13 | func Float32ToInt(number float32) int {
14 | return int(number)
15 | }
16 |
17 | // float32 -> int32
18 | func Float32ToInt32(number float32) int32 {
19 | return int32(number)
20 | }
21 |
22 | // float32 -> int64
23 | func Float32ToInt64(number float32) int64 {
24 | return int64(number)
25 | }
26 |
27 | // float64 -> string
28 | func Float64ToString(number float64) string {
29 | return strconv.FormatFloat(number, 'f', -1, 64)
30 | }
31 |
32 | // float64 -> int
33 | func Float64ToInt(number float64) int {
34 | return int(number)
35 | }
36 |
37 | // float64 -> int32
38 | func Float64ToInt32(number float64) int32 {
39 | return int32(number)
40 | }
41 |
42 | // float32 -> int64
43 | func Float64ToInt64(number float64) int64 {
44 | return int64(number)
45 | }
46 |
--------------------------------------------------------------------------------
/conversion/integer.go:
--------------------------------------------------------------------------------
1 | package conversion
2 |
3 | import "strconv"
4 |
5 | // int -> string
6 | func IntToString(number int) string {
7 | return strconv.Itoa(number)
8 | }
9 |
10 | // int -> float32
11 | func IntToFloat32(number int) float32 {
12 | return float32(number)
13 | }
14 |
15 | // int -> float64
16 | func IntToFloat64(number int) float64 {
17 | return float64(number)
18 | }
19 |
20 | // int32 -> string
21 | func Int32ToString(number int32) string {
22 | return strconv.FormatInt(int64(number), 10)
23 | }
24 |
25 | // int32 -> float32
26 | func Int32ToFloat32(number int32) float32 {
27 | return float32(number)
28 | }
29 |
30 | // int32 -> float64
31 | func Int32ToFloat64(number int32) float64 {
32 | return float64(number)
33 | }
34 |
35 | // int64 -> string
36 | func Int64ToString(number int64) string {
37 | return strconv.FormatInt(number, 10)
38 | }
39 |
40 | // int64 -> float32
41 | func Int64ToFloat32(number int64) float32 {
42 | return float32(number)
43 | }
44 |
45 | // int64 -> float64
46 | func Int64ToFloat64(number int64) float64 {
47 | return float64(number)
48 | }
49 |
--------------------------------------------------------------------------------
/conversion/nil_value.go:
--------------------------------------------------------------------------------
1 | package conversion
2 |
3 | import "strconv"
4 |
5 | // string -> int or nil
6 | func StringToIntOrNil(strNumber string) *int {
7 | if strNumber == "" {
8 | return nil
9 | }
10 | numInt, err := strconv.Atoi(strNumber)
11 | if err != nil {
12 | return nil
13 | }
14 | return &numInt
15 | }
16 |
17 | // string -> int32 or nil
18 | func StringToInt32OrNil(strNumber string) *int32 {
19 | if strNumber == "" {
20 | return nil
21 | }
22 | numInt, err := strconv.ParseInt(strNumber, 10, 32)
23 | if err != nil {
24 | return nil
25 | }
26 | result := int32(numInt)
27 | return &result
28 | }
29 |
30 | // string -> int64 or nil
31 | func StringToInt64OrNil(strNumber string) *int64 {
32 | if strNumber == "" {
33 | return nil
34 | }
35 | numInt, err := strconv.ParseInt(strNumber, 10, 64)
36 | if err != nil {
37 | return nil
38 | }
39 | result := int64(numInt)
40 | return &result
41 | }
42 |
43 | // string -> float32 or nil
44 | func StringToFloat32OrNil(strNumber string) *float32 {
45 | if strNumber == "" {
46 | return nil
47 | }
48 | numFloat, err := strconv.ParseFloat(strNumber, 32)
49 | if err != nil {
50 | return nil
51 | }
52 | result := float32(numFloat)
53 | return &result
54 | }
55 |
56 | // string -> float64 or nil
57 | func StringToFloat64OrNil(strNumber string) *float64 {
58 | if strNumber == "" {
59 | return nil
60 | }
61 | numFloat, err := strconv.ParseFloat(strNumber, 64)
62 | if err != nil {
63 | return nil
64 | }
65 | result := float64(numFloat)
66 | return &result
67 | }
68 |
69 | // string -> String or Nil
70 | func StringOrNil(str string) *string {
71 | if str == "" {
72 | return nil
73 | }
74 | return &str
75 | }
76 |
77 | // Int or Nil -> string
78 | func IntOrNilToString(number *int) string {
79 | if number == nil {
80 | return ""
81 | }
82 | return strconv.Itoa(*number)
83 | }
84 |
85 | // Int32 or Nil -> string
86 | func Int32OrNilToString(number *int32) string {
87 | if number == nil {
88 | return ""
89 | }
90 | return strconv.Itoa(int(*number))
91 | }
92 |
93 | // Int642 or Nil -> string
94 | func Int64OrNilToString(number *int64) string {
95 | if number == nil {
96 | return ""
97 | }
98 | return strconv.Itoa(int(*number))
99 | }
100 |
101 | // Int32 or Nil -> string
102 | func Float32OrNilToString(number *float32) string {
103 | if number == nil {
104 | return ""
105 | }
106 | return strconv.FormatFloat(float64(*number), 'f', -1, 32)
107 | }
108 |
109 | // Int642 or Nil -> string
110 | func Float64OrNilToString(number *float64) string {
111 | if number == nil {
112 | return ""
113 | }
114 | return strconv.FormatFloat(float64(*number), 'f', -1, 64)
115 | }
116 |
--------------------------------------------------------------------------------
/conversion/string.go:
--------------------------------------------------------------------------------
1 | package conversion
2 |
3 | import (
4 | "fmt"
5 | "strconv"
6 | )
7 |
8 | // StringToInt converts a string to an int and returns an error if conversion fails.
9 | func StringToInt(strNumber string) (int, error) {
10 | result, err := strconv.Atoi(strNumber)
11 | if err != nil {
12 | return 0, fmt.Errorf("error converting string to int '%s': %w", strNumber, err)
13 | }
14 | return result, nil
15 | }
16 |
17 | // StringToInt32 converts a string to an int32 and returns an error if conversion fails.
18 | func StringToInt32(strNumber string) (int32, error) {
19 | result, err := strconv.ParseInt(strNumber, 10, 32)
20 | if err != nil {
21 | return 0, fmt.Errorf("error converting string to int32 '%s': %w", strNumber, err)
22 | }
23 | return int32(result), nil
24 | }
25 |
26 | // StringToInt64 converts a string to an int64 and returns an error if conversion fails.
27 | func StringToInt64(strNumber string) (int64, error) {
28 | result, err := strconv.ParseInt(strNumber, 10, 64)
29 | if err != nil {
30 | return 0, fmt.Errorf("error converting string to int64 '%s': %w", strNumber, err)
31 | }
32 | return result, nil
33 | }
34 |
35 | // StringToInt8 converts a string to an int8 and returns an error if conversion fails.
36 | func StringToInt8(strNumber string) (int8, error) {
37 | result, err := strconv.ParseInt(strNumber, 10, 8)
38 | if err != nil {
39 | return 0, fmt.Errorf("error converting string to int8 '%s': %w", strNumber, err)
40 | }
41 | // Check if the result is within the valid range for int8
42 | if result < -128 || result > 127 {
43 | return 0, fmt.Errorf("value out of range for int8: %d", result)
44 | }
45 | return int8(result), nil
46 | }
47 |
48 | // StringToFloat32 converts a string to a float32 and returns an error if conversion fails.
49 | func StringToFloat32(strNumber string) (float32, error) {
50 | result, err := strconv.ParseFloat(strNumber, 32)
51 | if err != nil {
52 | return 0, fmt.Errorf("error converting string to float32 '%s': %w", strNumber, err)
53 | }
54 | return float32(result), nil
55 | }
56 |
57 | // StringToFloat64 converts a string to a float64 and returns an error if conversion fails.
58 | func StringToFloat64(strNumber string) (float64, error) {
59 | result, err := strconv.ParseFloat(strNumber, 64)
60 | if err != nil {
61 | return 0, fmt.Errorf("error converting string to float64 '%s': %w", strNumber, err)
62 | }
63 | return result, nil
64 | }
65 |
--------------------------------------------------------------------------------
/conversion/string_test.go:
--------------------------------------------------------------------------------
1 | package conversion
2 |
3 | import "testing"
4 |
5 | func Test_StringToInt(t *testing.T) {
6 | // Test case for a valid integer string
7 | str := "123"
8 | expected := 123
9 | result, _ := StringToInt(str)
10 | if result != expected {
11 | t.Errorf("StringToInt(%s) returned %d, expected %d", str, result, expected)
12 | }
13 |
14 | // Test case for an invalid integer string
15 | str = "abc"
16 | defer func() {
17 | if r := recover(); r == nil {
18 | t.Errorf("StringToInt(%s) did not panic as expected", str)
19 | }
20 | }()
21 | StringToInt(str)
22 | }
23 |
24 | func Test_StringToInt32(t *testing.T) {
25 | // Test case for a valid int32 string
26 | str := "123"
27 | expected := int32(123)
28 | result, _ := StringToInt32(str)
29 | if result != expected {
30 | t.Errorf("StringToInt32(%s) returned %d, expected %d", str, result, expected)
31 | }
32 |
33 | // Test case for an invalid int32 string
34 | str = "abc"
35 | defer func() {
36 | if r := recover(); r == nil {
37 | t.Errorf("StringToInt32(%s) did not panic as expected", str)
38 | }
39 | }()
40 | StringToInt32(str)
41 | }
42 |
43 | func Test_StringToInt64(t *testing.T) {
44 | // Test case for a valid int64 string
45 | str := "123"
46 | expected := int64(123)
47 | result, _ := StringToInt64(str)
48 | if result != expected {
49 | t.Errorf("StringToInt64(%s) returned %d, expected %d", str, result, expected)
50 | }
51 |
52 | // Test case for an invalid int64 string
53 | str = "abc"
54 | defer func() {
55 | if r := recover(); r == nil {
56 | t.Errorf("StringToInt64(%s) did not panic as expected", str)
57 | }
58 | }()
59 | StringToInt64(str)
60 | }
61 |
62 | func Test_StringToFloat32(t *testing.T) {
63 | // Test case for a valid float32 string
64 | str := "123.45"
65 | expected := float32(123.45)
66 | result, _ := StringToFloat32(str)
67 | if result != expected {
68 | t.Errorf("StringToFloat32(%s) returned %f, expected %f", str, result, expected)
69 | }
70 |
71 | // Test case for an invalid float32 string
72 | str = "abc"
73 | defer func() {
74 | if r := recover(); r == nil {
75 | t.Errorf("StringToFloat32(%s) did not panic as expected", str)
76 | }
77 | }()
78 | StringToFloat32(str)
79 | }
80 |
81 | func Test_StringToFloat64(t *testing.T) {
82 | // Test case for a valid float64 string
83 | str := "123.45"
84 | expected := 123.45
85 | result, _ := StringToFloat64(str)
86 | if result != expected {
87 | t.Errorf("StringToFloat64(%s) returned %f, expected %f", str, result, expected)
88 | }
89 |
90 | // Test case for an invalid float64 string
91 | str = "abc"
92 | defer func() {
93 | if r := recover(); r == nil {
94 | t.Errorf("StringToFloat64(%s) did not panic as expected", str)
95 | }
96 | }()
97 | StringToFloat64(str)
98 | }
99 |
--------------------------------------------------------------------------------
/database/README.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ortizdavid/go-nopain/450845036aa5080f63939ebd15e03530dfbdc30a/database/README.md
--------------------------------------------------------------------------------
/database/connection.go:
--------------------------------------------------------------------------------
1 | package database
2 |
3 | import (
4 | "database/sql"
5 | "fmt"
6 | )
7 |
8 | type DBType string
9 |
10 | const (
11 | MySQL = "mysql"
12 | Postgres = "postgres"
13 | )
14 |
15 | func NewDbConnection(dbType DBType, connString string) (*sql.DB, error) {
16 | var db *sql.DB
17 | var err error
18 |
19 | switch dbType {
20 | case MySQL:
21 | db, err = sql.Open("mysql", connString)
22 | case Postgres:
23 | db, err = sql.Open("postgres", connString)
24 | default:
25 | return nil, fmt.Errorf("unsupported database type: %v", dbType)
26 | }
27 |
28 | if err != nil {
29 | return nil, fmt.Errorf("error openning database: %v", err)
30 | }
31 |
32 | if err := db.Ping(); err != nil {
33 | return nil, fmt.Errorf("error pinging database: %v", err)
34 | }
35 |
36 | return db, err
37 | }
38 |
--------------------------------------------------------------------------------
/database/query_builder.go:
--------------------------------------------------------------------------------
1 | package database
2 |
3 | type QueryBuilder struct {
4 |
5 | }
6 |
7 | func (qb *QueryBuilder) Build() string {
8 | return ""
9 | }
--------------------------------------------------------------------------------
/database/table.go:
--------------------------------------------------------------------------------
1 | package database
2 |
3 | type Table struct {
4 | TableName string
5 | PrimaryKey string
6 | AffectedRows int64
7 | AffectedCols int64
8 | LastInsertedId int64
9 | }
10 |
11 | type Fields map[string]interface{}
12 | type Conditions map[string]interface{}
13 |
14 | func (tb *Table) Insert(data Fields) error {
15 | return nil
16 | }
17 |
18 | func (tb *Table) Update(data Fields, conditions Conditions) error {
19 | return nil
20 | }
21 |
22 | func (tb *Table) Delete(conditions Conditions) error {
23 | return nil
24 | }
25 |
26 |
--------------------------------------------------------------------------------
/datetime/README.md:
--------------------------------------------------------------------------------
1 | # datetime
2 |
3 | Package for handling and manipulating dates and times with ease.
4 | It provides utilities for date and time conversion, as well as performing operations such as adding or subtracting days, weeks, months, and years, calculating time differences, and formatting human-readable time ago strings.
5 |
6 | ## Features
7 |
8 | - [x] **Date and Time Conversion**:
9 | - Convert between `time.Time` and string representations.
10 | - Support for `YYYY-MM-DD` and `YYYY-MM-DD HH:MM:SS` formats.
11 |
12 | - [x] **Date Operations**:
13 | - Add or subtract days, weeks, months, or years from a given date.
14 | - Calculate the difference between two dates.
15 |
16 | - [x] **Age Calculation**:
17 | - Calculate the age based on a given birthdate.
18 |
19 | - [x] **Time Ago Functionality**:
20 | - Return a human-readable string indicating how much time has passed since a given date or between two dates.
21 |
--------------------------------------------------------------------------------
/datetime/conversion.go:
--------------------------------------------------------------------------------
1 | package datetime
2 |
3 | import (
4 | "fmt"
5 | "time"
6 | )
7 |
8 | // StringToDate converts a string date in the format "2006-01-02" to a time.Time object.
9 | func StringToDate(strDate string) (time.Time, error) {
10 | date, err := time.Parse(time.DateOnly, strDate)
11 | if err != nil {
12 | return time.Time{}, fmt.Errorf("error parsing date: %v", err)
13 | }
14 | return date, nil
15 | }
16 |
17 | // StringToDateTime converts a string date and time in the format "2006-01-02 15:04:05" to a time.Time object.
18 | func StringToDateTime(strDateTime string) (time.Time, error) {
19 | dateTime, err := time.Parse(dateTimeLayout, strDateTime)
20 | if err != nil {
21 | return time.Time{}, fmt.Errorf("error parsing dateTime: %v", err)
22 | }
23 | return dateTime, nil
24 | }
25 |
26 | // DateToString converts a time.Time object to a string date in the format "2006-01-02".
27 | func DateToString(date time.Time) string {
28 | return date.Format(dateLayout)
29 | }
30 |
31 | // DateTimeToString converts a time.Time object to a string date and time in the format "2006-01-02 15:04:05".
32 | func DateTimeToString(date time.Time) string {
33 | return date.Format(dateTimeLayout)
34 | }
35 |
--------------------------------------------------------------------------------
/datetime/conversion_test.go:
--------------------------------------------------------------------------------
1 | package datetime
--------------------------------------------------------------------------------
/datetime/current_time.go:
--------------------------------------------------------------------------------
1 | package datetime
2 |
3 | import "time"
4 |
5 | // CurrentDate returns the current date as a formatted string in the format "2006-01-02".
6 | func CurrentDate() string {
7 | return time.Now().Format(dateLayout)
8 | }
9 |
10 | // CurrentDateTime returns the current date and time as a formatted string in the format "2006-01-02 15:04:05".
11 | func CurrentDateTime() string {
12 | return time.Now().Format(dateTimeLayout)
13 | }
14 |
15 | // CurrentYear returns the current year as a formatted string.
16 | func CurrentYear() string {
17 | return time.Now().Format(yearLayout)
18 | }
19 |
20 | // CurrentTime returns the current time as a formatted string in the format "15:04:05".
21 | func CurrentTime() string {
22 | return time.Now().Format(secondsLayout)
23 | }
24 |
25 | // CurrentHour returns the current hour as a formatted string in the format "15".
26 | func CurrentHour() string {
27 | return time.Now().Format(hourLayout)
28 | }
29 |
--------------------------------------------------------------------------------
/datetime/current_time_test.go:
--------------------------------------------------------------------------------
1 | package datetime
--------------------------------------------------------------------------------
/datetime/extraction.go:
--------------------------------------------------------------------------------
1 | package datetime
2 |
3 | import (
4 | "fmt"
5 | "time"
6 | )
7 | // ExtractTimeZone returns the time zone name and offset of the given time
8 | func ExtractTimeZone(date time.Time) (string, int) {
9 | _, offset := date.Zone()
10 | return date.Location().String(), offset / 3600
11 | }
12 |
13 | // ExtractDateTime returns the date and time in the format YYYY-MM-DD HH:MM:SS
14 | func ExtractDateTime(date time.Time) string {
15 | return date.Format(dateTimeLayout)
16 | }
17 |
18 | // ExtractDate returns the date in the format YYYY-MM-DD
19 | func ExtractDate(date time.Time) string {
20 | return date.Format(dateLayout)
21 | }
22 |
23 | // ExtractYear returns the year as a string in the format YYYY
24 | func ExtractYear(date time.Time) string {
25 | return date.Format(yearLayout)
26 | }
27 |
28 | // ExtractMonth returns the month as a string in the format MM
29 | func ExtractMonth(date time.Time) string {
30 | return date.Format("01") // Keeping this as is for direct month extraction.
31 | }
32 |
33 | // ExtractWeek returns the ISO week number as a string (e.g., "39" for week 39)
34 | func ExtractWeek(date time.Time) string {
35 | _, week := date.ISOWeek()
36 | return fmt.Sprintf("%02d", week)
37 | }
38 |
39 | // ExtractDay returns the day of the month as a string in the format DD
40 | func ExtractDay(date time.Time) string {
41 | return date.Format(dayLayout)
42 | }
43 |
44 | // ExtractHour returns the time in the format HH:MM:SS
45 | func ExtractHour(date time.Time) string {
46 | return date.Format(hourLayout)
47 | }
48 |
49 | // ExtractSeconds returns only the seconds part (SS) of the time
50 | func ExtractSeconds(date time.Time) string {
51 | return date.Format(secondsLayout)
52 | }
53 |
54 | // ExtractMilliseconds returns the time with milliseconds in the format HH:MM:SS.SSS
55 | func ExtractMilliseconds(date time.Time) string {
56 | return date.Format(millisecondsLayout)
57 | }
--------------------------------------------------------------------------------
/datetime/extraction_string.go:
--------------------------------------------------------------------------------
1 | package datetime
2 |
3 | import (
4 | "log"
5 | "time"
6 | )
7 |
8 | // ExtractDateTimeZoneStr extracts the timezone from a date string.
9 | func ExtractDateTimeZoneStr(dateStr string) string {
10 | date, err := time.Parse(dateTimeLayout, dateStr)
11 | if err != nil {
12 | log.Printf("Error parsing dateStr in ExtractDateTimeZoneStr: %v", err)
13 | return "Invalid date"
14 | }
15 | return date.Location().String()
16 | }
17 |
18 | // ExtractDateTimeStr extracts the date and time (YYYY-MM-DD HH:MM:SS).
19 | func ExtractDateTimeStr(dateStr string) string {
20 | date, err := time.Parse(dateTimeLayout, dateStr)
21 | if err != nil {
22 | log.Printf("Error parsing dateStr in ExtractDateTimeStr: %v", err)
23 | return "Invalid date"
24 | }
25 | return date.Format(dateTimeLayout)
26 | }
27 |
28 | // ExtractDateStr extracts the date (YYYY-MM-DD).
29 | func ExtractDateStr(dateStr string) string {
30 | date, err := time.Parse(dateLayout, dateStr)
31 | if err != nil {
32 | log.Printf("Error parsing dateStr in ExtractDateStr: %v", err)
33 | return "Invalid date"
34 | }
35 | return date.Format(dateLayout)
36 | }
37 |
38 | // ExtractYearStr extracts the year.
39 | func ExtractYearStr(dateStr string) string {
40 | date, err := time.Parse(dateLayout, dateStr)
41 | if err != nil {
42 | log.Printf("Error parsing dateStr in ExtractYearStr: %v", err)
43 | return "Invalid date"
44 | }
45 | return date.Format("2006")
46 | }
47 |
48 | // ExtractMonthStr extracts the month.
49 | func ExtractMonthStr(dateStr string) string {
50 | date, err := time.Parse(dateLayout, dateStr)
51 | if err != nil {
52 | log.Printf("Error parsing dateStr in ExtractMonthStr: %v", err)
53 | return "Invalid date"
54 | }
55 | return date.Format("01")
56 | }
57 |
58 | // ExtractWeekStr extracts the ISO week.
59 | func ExtractWeekStr(dateStr string) int {
60 | date, err := time.Parse(dateLayout, dateStr)
61 | if err != nil {
62 | log.Printf("Error parsing dateStr in ExtractWeekStr: %v", err)
63 | return -1
64 | }
65 | _, week := date.ISOWeek()
66 | return week
67 | }
68 |
69 | // ExtractDayStr extracts the day.
70 | func ExtractDayStr(dateStr string) string {
71 | date, err := time.Parse(dateLayout, dateStr)
72 | if err != nil {
73 | log.Printf("Error parsing dateStr in ExtractDayStr: %v", err)
74 | return "Invalid date"
75 | }
76 | return date.Format("02")
77 | }
78 |
79 | // ExtractHourStr extracts the hour.
80 | func ExtractHourStr(dateStr string) string {
81 | date, err := time.Parse(dateTimeLayout, dateStr)
82 | if err != nil {
83 | log.Printf("Error parsing dateStr in ExtractHourStr: %v", err)
84 | return "Invalid date"
85 | }
86 | return date.Format("15")
87 | }
88 |
89 | // ExtractSecondStr extracts the seconds.
90 | func ExtractSecondStr(dateStr string) string {
91 | date, err := time.Parse(dateTimeLayout, dateStr)
92 | if err != nil {
93 | log.Printf("Error parsing dateStr in ExtractSecondStr: %v", err)
94 | return "Invalid date"
95 | }
96 | return date.Format("05")
97 | }
98 |
--------------------------------------------------------------------------------
/datetime/last_time.go:
--------------------------------------------------------------------------------
1 | package datetime
2 |
3 | import "time"
4 |
5 | // LastDateOfYear returns the last date of the current year.
6 | func LastDateOfYear() time.Time {
7 | currentYear := time.Now().Year()
8 | lastDate := time.Date(currentYear, time.December, 31, 0, 0, 0, 0, time.Local)
9 | return lastDate
10 | }
11 |
12 | // LastDayOfCurrentWeek returns the last day (Sunday) of the current week.
13 | func LastDayOfCurrentWeek() time.Time {
14 | now := time.Now()
15 | daysUntilSunday := 7 - int(now.Weekday())
16 | return now.AddDate(0, 0, daysUntilSunday).Truncate(24 * time.Hour).Add(23 * time.Hour + 59 * time.Minute + 59 * time.Second) // Setting time to 23:59:59
17 | }
18 |
19 | // LastDayOfCurrentMonth returns the last day of the current month.
20 | func LastDayOfCurrentMonth() time.Time {
21 | now := time.Now()
22 | return time.Date(now.Year(), now.Month()+1, 1, 0, 0, 0, 0, time.Local).Add(-time.Nanosecond)
23 | }
24 |
25 | // LastDayOfMonth returns the last day of the specified month of a given year.
26 | func LastDayOfMonth(year int, month time.Month) time.Time {
27 | firstDayOfNextMonth := time.Date(year, month+1, 1, 0, 0, 0, 0, time.Local)
28 | return firstDayOfNextMonth.Add(-time.Nanosecond)
29 | }
30 |
31 | // LastDayOfWeek returns the last day (Sunday) of the specified week of a given year.
32 | func LastDayOfWeek(year int, week int) time.Time {
33 | firstDayOfWeek := time.Date(year, 1, 1, 0, 0, 0, 0, time.Local).AddDate(0, 0, (week-1)*7)
34 | lastDayOfWeek := firstDayOfWeek.AddDate(0, 0, 6)
35 | return lastDayOfWeek
36 | }
37 |
--------------------------------------------------------------------------------
/datetime/last_time_string.go:
--------------------------------------------------------------------------------
1 | package datetime
2 |
3 | import "time"
4 |
5 | // LastDateOfYearStr returns the last date of the current year as a formatted string.
6 | func LastDateOfYearStr() string {
7 | return DateToString(LastDateOfYear())
8 | }
9 |
10 | // LastDayOfCurrentWeekStr returns the last day (Sunday) of the current week as a formatted string.
11 | func LastDayOfCurrentWeekStr() string {
12 | return DateTimeToString(LastDayOfCurrentWeek())
13 | }
14 |
15 | // LastDayOfCurrentMonthStr returns the last day of the current month as a formatted string.
16 | func LastDayOfCurrentMonthStr() string {
17 | return DateToString(LastDayOfCurrentMonth())
18 | }
19 |
20 | // LastDayOfMonthStr returns the last day of the specified month and year as a formatted string.
21 | func LastDayOfMonthStr(year int, month int) string {
22 | return DateToString(LastDayOfMonth(year, time.Month(month)))
23 | }
24 |
25 | // LastDayOfWeekStr returns the last day (Sunday) of the specified week and year as a formatted string.
26 | func LastDayOfWeekStr(year int, week int) string {
27 | return DateToString(LastDayOfWeek(year, week))
28 | }
29 |
--------------------------------------------------------------------------------
/datetime/layouts.go:
--------------------------------------------------------------------------------
1 | package datetime
2 |
3 | // Common layouts for formatting and parsing date and time strings.
4 | const (
5 | fullTimeWithMonotonicClockLayout = "2006-01-02 15:04:05.999999999 -0700 MST m=+0.000000001"
6 | fullTimeLayoutTZ = "2006-01-02 15:04:05.999999999 -0700 MST" // Full timestamp with nanoseconds and time zone.
7 | dateTimeLayoutTZ = "2006-01-02 15:04:05 -0700 MST" // Date and time with time zone.
8 | dateTimeLayout = "2006-01-02 15:04:05" // Date and time without time zone.
9 | dateLayout = "2006-01-02" // Date only (YYYY-MM-DD).
10 | yearLayout = "2006" // Year (YYYY).
11 | dayLayout = "02" // Day of the month (DD).
12 | hourLayout = "15:04:05" // Hour (HH:MM:SS).
13 | secondsLayout = "05" // Seconds (SS).
14 | millisecondsLayout = "15:04:05.000" // Time with milliseconds.
15 | fullDateTimeWithTZ = "2006-01-02 15:04:05.999999999....-0700....MST" // Full date-time with custom separators.
16 | )
17 |
--------------------------------------------------------------------------------
/datetime/operation.go:
--------------------------------------------------------------------------------
1 | package datetime
2 |
3 | import "time"
4 |
5 | // AddDays adds the specified number of days to the given date.
6 | func AddDays(date time.Time, days int) time.Time {
7 | return date.AddDate(0, 0, days)
8 | }
9 |
10 | // SubtractDays subtracts the specified number of days from the given date.
11 | func SubtractDays(date time.Time, days int) time.Time {
12 | return date.AddDate(0, 0, -days)
13 | }
14 |
15 | // AddWeeks adds the specified number of weeks to the given date.
16 | func AddWeeks(date time.Time, weeks int) time.Time {
17 | return date.AddDate(0, 0, weeks*7)
18 | }
19 |
20 | // SubtractWeeks subtracts the specified number of weeks from the given date.
21 | func SubtractWeeks(date time.Time, weeks int) time.Time {
22 | return date.AddDate(0, 0, -weeks*7)
23 | }
24 |
25 | // AddMonths adds the specified number of months to the given date.
26 | func AddMonths(date time.Time, months int) time.Time {
27 | return date.AddDate(0, months, 0)
28 | }
29 |
30 | // SubtractMonths subtracts the specified number of months from the given date.
31 | func SubtractMonths(date time.Time, months int) time.Time {
32 | return date.AddDate(0, -months, 0)
33 | }
34 |
35 | // AddYears adds the specified number of years to the given date.
36 | func AddYears(date time.Time, years int) time.Time {
37 | return date.AddDate(years, 0, 0)
38 | }
39 |
40 | // SubtractYears subtracts the specified number of years from the given date.
41 | func SubtractYears(date time.Time, years int) time.Time {
42 | return date.AddDate(-years, 0, 0)
43 | }
44 |
45 | // SumDates calculates the sum of two dates and returns the resulting date.
46 | func SumDates(date1 time.Time, date2 time.Time) time.Time {
47 | return date1.Add(date2.Sub(date1))
48 | }
49 |
50 | // SubtractDates calculates the difference between two dates and returns the result as a time.Duration.
51 | func SubtractDates(date1 time.Time, date2 time.Time) time.Duration {
52 | return date1.Sub(date2)
53 | }
54 |
55 | // GetAge calculates the age based on the given birthdate.
56 | func GetAge(birthDate time.Time) int {
57 | current := time.Now()
58 | age := current.Year() - birthDate.Year()
59 | if current.YearDay() < birthDate.YearDay() {
60 | age--
61 | }
62 | return age
63 | }
64 |
--------------------------------------------------------------------------------
/datetime/operation_test.go:
--------------------------------------------------------------------------------
1 | package datetime
--------------------------------------------------------------------------------
/datetime/time_ago.go:
--------------------------------------------------------------------------------
1 | package datetime
2 |
3 | import (
4 | "fmt"
5 | "time"
6 | )
7 |
8 | // TimeAgo calculates the time difference from a given time to the current time and returns a human-readable string.
9 | func TimeAgo(fromTime time.Time) string {
10 | now := time.Now()
11 | diff := now.Sub(fromTime)
12 |
13 | // Calculate the time difference in different units.
14 | years := int(diff.Hours() / 24 / 365)
15 | months := int(diff.Hours() / 24 / 30)
16 | days := int(diff.Hours() / 24)
17 | hours := int(diff.Hours())
18 | minutes := int(diff.Minutes())
19 | seconds := int(diff.Seconds())
20 |
21 | // Return the appropriate time difference string.
22 | if years >= 1 {
23 | return fmt.Sprintf("%d years ago", years)
24 | } else if months >= 1 {
25 | return fmt.Sprintf("%d months ago", months)
26 | } else if days >= 1 {
27 | return fmt.Sprintf("%d days ago", days)
28 | } else if hours >= 1 {
29 | return fmt.Sprintf("%d hours ago", hours)
30 | } else if minutes >= 1 {
31 | return fmt.Sprintf("%d minutes ago", minutes)
32 | } else {
33 | return fmt.Sprintf("%d seconds ago", seconds)
34 | }
35 | }
36 |
37 | // TimeAgoBetweenDates calculates the time difference between two time values and returns a string representing the duration.
38 | func TimeAgoBetween(start time.Time, end time.Time) string {
39 | diff := end.Sub(start)
40 | return TimeAgo(start) + " to " + TimeAgo(end) + " (" + diff.String() + ")"
41 | }
42 |
--------------------------------------------------------------------------------
/datetime/time_ago_string.go:
--------------------------------------------------------------------------------
1 | package datetime
2 |
3 | import "log"
4 |
5 | // TimeAgoStr calculates the time difference from a given time string to the current time and returns a human-readable string.
6 | func TimeAgoStr(fromTimeStr string) string {
7 | fromTime, err := StringToDateTime(fromTimeStr)
8 | if err != nil {
9 | log.Printf("Error parsing fromTimeStr: %v", err)
10 | return "Invalid date"
11 | }
12 | return TimeAgo(fromTime)
13 | }
14 |
15 | // TimeAgoBetweenDatesStr calculates the time difference between two time strings and returns a string representing the duration.
16 | func TimeAgoBetweenStr(startStr string, endStr string) string {
17 | startTime, err := StringToDateTime(startStr)
18 | if err != nil {
19 | log.Printf("Error parsing startStr: %v", err)
20 | return "Invalid start date"
21 | }
22 | endTime, err := StringToDateTime(endStr)
23 | if err != nil {
24 | log.Printf("Error parsing endStr: %v", err)
25 | return "Invalid end date"
26 | }
27 | return TimeAgoBetween(startTime, endTime)
28 | }
29 |
--------------------------------------------------------------------------------
/datetime/time_ago_test.go:
--------------------------------------------------------------------------------
1 | package datetime
--------------------------------------------------------------------------------
/datetime/validation.go:
--------------------------------------------------------------------------------
1 | package datetime
2 |
3 | import (
4 | "time"
5 | )
6 |
7 | // IsValidDateTime checks if a string is a valid date-time in the format "2006-01-02 15:04:05".
8 | func IsValidDateTime(dateTimeStr string) bool {
9 | _, err := time.Parse(dateTimeLayout, dateTimeStr)
10 | return err == nil
11 | }
12 |
13 | // IsValidDate checks if a string is a valid date in the format "2006-01-02".
14 | func IsValidDate(dateStr string) bool {
15 | _, err := time.Parse(dateLayout, dateStr)
16 | return err == nil
17 | }
18 |
19 | // IsValidYear checks if a string is a valid year.
20 | func IsValidYear(yearStr string) bool {
21 | _, err := time.Parse(yearLayout, yearStr)
22 | return err == nil
23 | }
24 |
25 | // IsValidMonth checks if a string is a valid month as part of a full date (YYYY-MM).
26 | func IsValidMonth(monthStr string) bool {
27 | _, err := time.Parse("2006-01", monthStr)
28 | return err == nil
29 | }
30 |
31 | // IsValidDay checks if a string is a valid day as part of a full date (YYYY-MM-DD).
32 | func IsValidDay(dayStr string) bool {
33 | _, err := time.Parse(dateLayout, dayStr)
34 | return err == nil
35 | }
36 |
37 | // IsValidHour checks if a string is a valid time in the format "15:04:05".
38 | func IsValidHour(timeStr string) bool {
39 | _, err := time.Parse("15:04:05", timeStr)
40 | return err == nil
41 | }
42 |
43 | // IsValidSecond checks if a string is a valid time with seconds (SS).
44 | func IsValidSecond(secondStr string) bool {
45 | _, err := time.Parse("15:04:05", "00:00:"+secondStr)
46 | return err == nil
47 | }
48 |
49 | // IsValidMillisecond checks if a string is a valid time with milliseconds (HH:MM:SS.SSS).
50 | func IsValidMillisecond(timeStr string) bool {
51 | _, err := time.Parse(millisecondsLayout, "00:00:00."+timeStr)
52 | return err == nil
53 | }
--------------------------------------------------------------------------------
/docgen/README.md:
--------------------------------------------------------------------------------
1 | # docgen
2 |
3 | Package for PDF, Excel and other document generators
4 |
5 | ## Generators
6 | - [x] HTML to PDF generator (requires [wkhtmltopdf](https://wkhtmltopdf.org/downloads.html))
7 | - [x] Excel Generator
--------------------------------------------------------------------------------
/docgen/excel_generator.go:
--------------------------------------------------------------------------------
1 | package docgen
2 |
3 | import (
4 | "bytes"
5 | "fmt"
6 | "net/http"
7 | "github.com/tealeg/xlsx"
8 | )
9 |
10 |
11 | type ExcelGenerator struct {
12 | File *xlsx.File
13 | Sheet *xlsx.Sheet
14 | HeaderColor string
15 | TitleSize int
16 | TitleFont string
17 | CellPatterType string
18 | CellFgColor string
19 | CellBgColor string
20 | ColMultiplier float64
21 | }
22 |
23 |
24 | func NewExcelGenerator() *ExcelGenerator {
25 | file := xlsx.NewFile()
26 | sheet, _ := file.AddSheet("Sheet1")
27 | return &ExcelGenerator{
28 | File: file,
29 | Sheet: sheet,
30 | HeaderColor: "CC9F26",
31 | TitleSize: 12,
32 | TitleFont: "Arial",
33 | CellPatterType: "solid",
34 | CellFgColor: "FF0000",
35 | CellBgColor: "FF0000",
36 | ColMultiplier: 1.2,// Adjust this multiplier based on your requirements
37 | }
38 | }
39 |
40 |
41 | func (eg *ExcelGenerator) AddTitle(title string) {
42 | row := eg.Sheet.AddRow()
43 | cell := row.AddCell()
44 | cell.Value = title
45 | titleStyle := xlsx.NewStyle()
46 | titleStyle.Font = *xlsx.NewFont(eg.TitleSize, eg.TitleFont)
47 | titleStyle.Font.Bold = true
48 | cell.SetStyle(titleStyle)
49 | }
50 |
51 |
52 | func (eg *ExcelGenerator) AddHeaderRow(columns ...string) {
53 | row := eg.Sheet.AddRow()
54 | for _, col := range columns {
55 | cell := row.AddCell()
56 | cell.Value = col
57 | cell.SetStyle(eg.getHeaderBorderStyle())
58 | }
59 | style := xlsx.NewStyle()
60 | fill := *xlsx.NewFill("solid", eg.HeaderColor, eg.HeaderColor)
61 | style.Fill = fill
62 | for _, cell := range row.Cells {
63 | cell.SetStyle(style)
64 | }
65 | }
66 |
67 | func (eg *ExcelGenerator) AddDataRow(data ...interface{}) {
68 | row := eg.Sheet.AddRow()
69 | for idx, d := range data {
70 | cell := row.AddCell()
71 | cell.Value = fmt.Sprintf("%v", d)
72 | cell.SetStyle(eg.getCellBorderStyle())
73 |
74 | textLength := len(fmt.Sprintf("%v", d))
75 | eg.adjustColumnWidth(idx, textLength)
76 | }
77 | }
78 |
79 | // getHeaderBorderStyle returns a style with header cell borders.
80 | func (eg *ExcelGenerator) getHeaderBorderStyle() *xlsx.Style {
81 | style := xlsx.NewStyle()
82 | border := xlsx.Border{
83 | Left: "thin",
84 | Right: "thin",
85 | Top: "thin",
86 | Bottom: "thin",
87 | }
88 | style.Border = border
89 | return style
90 | }
91 |
92 |
93 | // getCellBorderStyle returns a style with data cell borders.
94 | func (eg *ExcelGenerator) getCellBorderStyle() *xlsx.Style {
95 | style := xlsx.NewStyle()
96 | border := xlsx.Border{
97 | Left: "thin",
98 | Right: "thin",
99 | Top: "thin",
100 | Bottom: "thin",
101 | }
102 | style.Border = border
103 | return style
104 | }
105 |
106 | // getTitleBorderStyle returns a style with title cell borders.
107 | func (eg *ExcelGenerator) getTitleBorderStyle() *xlsx.Style {
108 | style := xlsx.NewStyle()
109 | border := xlsx.Border{
110 | Left: "thin",
111 | Right: "thin",
112 | Top: "thin",
113 | Bottom: "thin",
114 | }
115 | style.Border = border
116 | return style
117 | }
118 |
119 |
120 | // adjustColumnWidth adjusts the column width based on the text length.
121 | func (eg *ExcelGenerator) adjustColumnWidth(colIndex int, textLength int) {
122 | colWidth := float64(textLength) * eg.ColMultiplier
123 | eg.Sheet.Col(colIndex).Width = colWidth
124 | }
125 |
126 |
127 | // getCellBackgroundColorStyle returns a style with a specific background color.
128 | func (eg *ExcelGenerator) getCellBackgroundColorStyle() *xlsx.Style {
129 | style := xlsx.NewStyle()
130 | style.Fill = *xlsx.NewFill(eg.CellPatterType, eg.CellFgColor, eg.CellBgColor)
131 | return style
132 | }
133 |
134 |
135 | // SaveToFile writes the Excel file to the response
136 | func (eg *ExcelGenerator) SaveToFile(w http.ResponseWriter, fileName string) error {
137 | var buf bytes.Buffer
138 | err := eg.File.Write(&buf)
139 | if err != nil {
140 | http.Error(w, "Error writing Excel data", http.StatusInternalServerError)
141 | return err
142 | }
143 | w.Header().Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
144 | w.Header().Set("Content-Disposition", "attachment; filename="+fileName)
145 | if _, err := w.Write(buf.Bytes()); err != nil {
146 | http.Error(w, "Error sending Excel file", http.StatusInternalServerError)
147 | return err
148 | }
149 | return nil
150 | }
--------------------------------------------------------------------------------
/docgen/excel_generator_test.go:
--------------------------------------------------------------------------------
1 | package docgen
--------------------------------------------------------------------------------
/docgen/html_pdf_generator.go:
--------------------------------------------------------------------------------
1 | package docgen
2 |
3 | import (
4 | "bytes"
5 | "html/template"
6 | "net/http"
7 |
8 | "github.com/SebastiaanKlippert/go-wkhtmltopdf"
9 | )
10 |
11 | type HtmlPdfGenerator struct {
12 | FooterFontSize uint
13 | PageZoom float64
14 | Dpi uint
15 | Grayscale bool
16 | }
17 |
18 |
19 | func NewHtmlPdfGenerator() *HtmlPdfGenerator {
20 | return &HtmlPdfGenerator{
21 | FooterFontSize: 10,
22 | PageZoom: 0.95,
23 | Dpi: 100,
24 | Grayscale: false,
25 | }
26 | }
27 |
28 |
29 | func (gen HtmlPdfGenerator) GeneratePDF(htmlTemplate string, data map[string]interface{}) ([]byte, error) {
30 | var buf bytes.Buffer
31 | // Load HTML template
32 | tmpl, err := gen.LoadHtmlTemplate(htmlTemplate)
33 | if err != nil {
34 | return nil, err
35 | }
36 | // Execute the template with the data
37 | err = tmpl.Execute(&buf, data)
38 | if err != nil {
39 | return nil, err
40 | }
41 | // Create new PDF generator
42 | pdfGen, err := wkhtmltopdf.NewPDFGenerator()
43 | if err != nil {
44 | return nil, err
45 | }
46 | // Set global options
47 | pdfGen.Dpi.Set(gen.Dpi)
48 | pdfGen.Orientation.Set(wkhtmltopdf.OrientationPortrait)
49 | pdfGen.Grayscale.Set(gen.Grayscale)
50 | // Create a new input page from HTML content
51 | page := wkhtmltopdf.NewPageReader(&buf)
52 | // Set options for this page
53 | page.FooterRight.Set("[page]")
54 | page.FooterFontSize.Set(gen.FooterFontSize)
55 | page.Zoom.Set(gen.PageZoom)
56 | // Add to the document
57 | pdfGen.AddPage(page)
58 | // Create PDF document in the internal buffer
59 | err = pdfGen.Create()
60 | if err != nil {
61 | return nil, err
62 | }
63 | // Get the PDF bytes
64 | pdfBytes := pdfGen.Bytes()
65 | return pdfBytes, nil
66 | }
67 |
68 |
69 | func (gen HtmlPdfGenerator) LoadHtmlTemplate(filePath string) (*template.Template, error) {
70 | tmpl, err := template.ParseFiles(filePath)
71 | return tmpl, err
72 | }
73 |
74 |
75 | func (gen HtmlPdfGenerator) SetOutput(w http.ResponseWriter, pdfBytes []byte, fileName string) error {
76 | w.Header().Set("Content-Type", "application/pdf")
77 | w.Header().Set("Content-Disposition", "attachment; filename="+fileName)
78 | _, err := w.Write(pdfBytes)
79 | return err
80 | }
--------------------------------------------------------------------------------
/docgen/html_pdf_generator_test.go:
--------------------------------------------------------------------------------
1 | package docgen
--------------------------------------------------------------------------------
/encryption/README.md:
--------------------------------------------------------------------------------
1 | # encryption
2 |
3 | Package for data encryption and random generation
4 |
5 | ## Features
6 | - [x] MD5 encoding
7 | - [x] Base64 encoding
8 | - [x] Password hashing
9 | - [x] UUID
10 | - [x] Random Token
11 | - [x] Tests
12 |
--------------------------------------------------------------------------------
/encryption/base64.go:
--------------------------------------------------------------------------------
1 | package encryption
2 |
3 | import (
4 | "encoding/base64"
5 | "log"
6 | )
7 |
8 | // EncodeBase64 encodes the given text to Base64.
9 | func EncodeBase64(text string) string {
10 | byteText := []byte(text)
11 | encoded := base64.StdEncoding.EncodeToString(byteText)
12 | return encoded
13 | }
14 |
15 | // DecodeBase64 decodes the given Base64 encoded string.
16 | func DecodeBase64(strEncoded string) string {
17 | decoded, err := base64.StdEncoding.DecodeString(strEncoded)
18 | if err != nil {
19 | log.Fatal(err.Error())
20 | }
21 | text := string(decoded)
22 | return text
23 | }
24 |
25 | // CompareBase64Encoding compares a Base64 encoded string with its original text.
26 | func CompareBase64Encoding(encodedStr string, originalStr string) bool {
27 | decodedStr := DecodeBase64(encodedStr)
28 | return decodedStr == originalStr
29 | }
30 |
--------------------------------------------------------------------------------
/encryption/base64_test.go:
--------------------------------------------------------------------------------
1 | package encryption
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 |
9 | const (
10 | originalStrBase64 = "This is the original String"
11 | encodedStrBase64 = "VGhpcyBpcyB0aGUgb3JpZ2luYWwgU3RyaW5n"
12 | )
13 |
14 |
15 | func TestEncodeBase64(t *testing.T) {
16 | got := encodedStrBase64
17 | expected := EncodeBase64(originalStrBase64)
18 | fmt.Println(got, expected)
19 | if got != expected {
20 | t.Errorf("Encoding failed '%s' != %s", got, expected)
21 | }
22 | }
23 |
24 |
25 | func TestDecodeBase64(t *testing.T) {
26 | got := originalStrBase64
27 | expected := DecodeBase64(encodedStrBase64)
28 | if got != expected {
29 | t.Errorf("Encoding failed '%s' != %s", got, expected)
30 | }
31 | }
--------------------------------------------------------------------------------
/encryption/md5.go:
--------------------------------------------------------------------------------
1 | package encryption
2 |
3 | import (
4 | "crypto/md5"
5 | "encoding/hex"
6 | "log"
7 | )
8 |
9 | // EncodeMD5 encodes the given text using MD5 hashing algorithm.
10 | func EncodeMD5(text string) string {
11 | hasher := md5.New()
12 | hasher.Write([]byte(text))
13 | return hex.EncodeToString(hasher.Sum(nil))
14 | }
15 |
16 | // DecodeMD5 decodes the given hexadecimal encoded MD5 hash.
17 | func DecodeMD5(encoded string) string {
18 | decoded, err := hex.DecodeString(encoded)
19 | if err != nil {
20 | log.Fatal(err.Error())
21 | }
22 | return string(decoded)
23 | }
24 |
--------------------------------------------------------------------------------
/encryption/md5_test.go:
--------------------------------------------------------------------------------
1 | package encryption
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | const (
9 | originalStrMd5 = "Hello World"
10 | encodedStrMd5 = "b10a8db164e0754105b7a99be72e3fe5"
11 | )
12 |
13 | func TestEncodeMD5(t *testing.T) {
14 | text := originalStrMd5
15 | fmt.Println(EncodeMD5(text))
16 | }
17 |
18 |
19 | func TestDecodeMD5(t *testing.T) {
20 | encoded := encodedStrMd5
21 | fmt.Println(DecodeMD5(encoded))
22 | }
--------------------------------------------------------------------------------
/encryption/password.go:
--------------------------------------------------------------------------------
1 | package encryption
2 |
3 | import (
4 | "log"
5 | "golang.org/x/crypto/bcrypt"
6 | )
7 |
8 |
9 | // HashPassword generates a bcrypt hash from the given password.
10 | func HashPassword(password string) string {
11 | hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
12 | if err != nil {
13 | log.Fatal(err.Error())
14 | }
15 | return string(hash)
16 | }
17 |
18 | // CheckPassword compares a bcrypt hashed password with a plain-text password.
19 | func CheckPassword(hashedPassword string, password string) bool {
20 | err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
21 | return err == nil
22 | }
--------------------------------------------------------------------------------
/encryption/password_test.go:
--------------------------------------------------------------------------------
1 | package encryption
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func TestPassword(t *testing.T) {
8 |
9 | }
--------------------------------------------------------------------------------
/encryption/string.go:
--------------------------------------------------------------------------------
1 | package encryption
2 |
3 | import (
4 | "log"
5 | "time"
6 | "crypto/rand"
7 | "encoding/base64"
8 | "github.com/google/uuid"
9 | )
10 |
11 |
12 | func GenerateUUID() string {
13 | uniqueId := uuid.New()
14 | return uniqueId.String()
15 | }
16 |
17 | func GenerateRandomToken(length int) string {
18 | randomBytes := make([]byte, length)
19 | _, err := rand.Read(randomBytes)
20 | if err != nil {
21 | log.Fatal(err.Error())
22 | }
23 | token := base64.RawURLEncoding.EncodeToString(randomBytes)
24 | return token
25 | }
26 |
27 | func GenerateCode(prefix string) string {
28 | timestamp := time.Now().Format("20060102150405")
29 | return prefix + timestamp
30 | }
31 |
--------------------------------------------------------------------------------
/encryption/string_test.go:
--------------------------------------------------------------------------------
1 | package encryption
2 |
3 | import (
4 | "testing"
5 | "strings"
6 | )
7 |
8 | func TestGenerateUUID(t *testing.T) {
9 | uuid := GenerateUUID()
10 | if len(uuid) != 36 {
11 | t.Errorf("GenerateUUID() returned an invalid UUID: %s", uuid)
12 | }
13 | }
14 |
15 | func TestGenerateRandomToken(t *testing.T) {
16 | token := GenerateRandomToken(100)
17 | if len(token) != 136 { // 100 bytes when base64 encoded
18 | t.Errorf("GenerateRandomToken() returned an invalid token: %s", token)
19 | }
20 | }
21 |
22 | func TestGenerateCode(t *testing.T) {
23 | prefix := "PREFIX"
24 | code := GenerateCode(prefix)
25 | if !strings.HasPrefix(code, prefix) {
26 | t.Errorf("GenerateCode() did not prepend the prefix: %s", code)
27 | }
28 | if len(code) != len(prefix)+14 { // 14 characters for the timestamp
29 | t.Errorf("GenerateCode() generated an invalid code: %s", code)
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/filemanager/README.md:
--------------------------------------------------------------------------------
1 | # filemanager
2 |
3 | Package for file handling
4 |
5 | ## Features
6 | - [x] File Info
7 | - [x] Create Files
8 | - [x] Write into files
9 | - [x] Zip Files
10 | - [x] Upload Files
11 | - [x] Download Files
12 | - [ ] Tests
13 |
--------------------------------------------------------------------------------
/filemanager/consts.go:
--------------------------------------------------------------------------------
1 | package filemanager
2 |
3 | // Constants for messages related to file and folder operations.
4 | const (
5 | FOLDER_CREATED string = "\nFolder '%s' Created"
6 | FILE_CREATED string = "\nFile '%s' Created"
7 | FOLDER_REMOVED string = "\nFolder '%s' Removed"
8 | FILE_REMOVED string = "\nFile '%s' Removed"
9 | )
10 |
--------------------------------------------------------------------------------
/filemanager/download_test.go:
--------------------------------------------------------------------------------
1 | package filemanager
2 |
3 | import "testing"
4 |
5 |
6 | var downloader = NewDownloader()
7 |
8 | func Test_Download_NonExistingURL(t *testing.T) {
9 | nonExistingURL := "http://localhost:12345/nonexistentfile.txt"
10 |
11 | _, err := downloader.DownloadFile(nonExistingURL)
12 | if err == nil {
13 | t.Errorf("This Download must Fail. ")
14 | }
15 | }
16 |
17 |
18 | func Test_Download_ExistingUrl(t *testing.T) {
19 | existingURL := "https://filesamples.com/samples/document/txt/sample3.txt"
20 |
21 | _, err := downloader.DownloadFile(existingURL)
22 | if err != nil {
23 | t.Errorf("Error for existing URL: %s. ", existingURL)
24 | }
25 | }
--------------------------------------------------------------------------------
/filemanager/downloader.go:
--------------------------------------------------------------------------------
1 | package filemanager
2 |
3 | import (
4 | "fmt"
5 | "io"
6 | "net/http"
7 | "os"
8 | "path/filepath"
9 | "strings"
10 | )
11 |
12 | // Downloader manages file downloads.
13 | type Downloader struct {
14 | Errors []string
15 | }
16 |
17 | // DownloadInfo contains information about a downloaded file
18 | type DownloadInfo struct {
19 | URL string // URL is the source URL from which the file is downloaded.
20 | FileName string // FileName is the name of the downloaded file.
21 | Directory string // Directory is the directory where the file is saved.
22 | Size int64 // Size is the size of the downloaded file in MB.
23 | }
24 |
25 | // NewDownloader creates a new instance of Downloader.
26 | func NewDownloader() Downloader {
27 | return Downloader{
28 | Errors: make([]string, 0),
29 | }
30 | }
31 |
32 | // DownloadFile downloads a file from the specified URL.
33 | // DownloadFile downloads a file from the specified URL.
34 | func (dl Downloader) DownloadFile(url string) (DownloadInfo, error) {
35 | if !dl.isValidURL(url) {
36 | return DownloadInfo{}, fmt.Errorf("invalid URL: %s", url)
37 | }
38 |
39 | fileName := dl.extractNameFromURL(url)
40 | directory := "." // Default directory is current working directory
41 | fullPath := filepath.Join(directory, fileName)
42 |
43 | // Create directory if it doesn't exist
44 | err := os.MkdirAll(directory, os.ModePerm)
45 | if err != nil {
46 | return DownloadInfo{}, fmt.Errorf("error creating directory: %w", err)
47 | }
48 |
49 | // Download the file
50 | resp, err := http.Get(url)
51 | if err != nil {
52 | return DownloadInfo{}, fmt.Errorf("error downloading file: %w", err)
53 | }
54 | defer resp.Body.Close()
55 |
56 | // Check for successful download
57 | if resp.StatusCode != http.StatusOK {
58 | return DownloadInfo{}, fmt.Errorf("error downloading file: %s", resp.Status)
59 | }
60 |
61 | // Create the destination file
62 | out, err := os.Create(fullPath)
63 | if err != nil {
64 | return DownloadInfo{}, fmt.Errorf("error creating file: %w", err)
65 | }
66 | defer out.Close()
67 |
68 | // Write downloaded data to the file
69 | downloaded, err := io.Copy(out, resp.Body)
70 | if err != nil {
71 | return DownloadInfo{}, fmt.Errorf("error writing file: %w", err)
72 | }
73 |
74 | // Calculate and set file size in MB
75 | size := float64(downloaded) / (1 << 20) // Conversion to megabytes (MB)
76 |
77 | return DownloadInfo{
78 | URL: url,
79 | FileName: fileName,
80 | Directory: directory,
81 | Size: int64(size), // Convert to int64
82 | }, nil
83 | }
84 |
85 | func (dl Downloader) isValidURL(url string) bool {
86 | return strings.HasPrefix(url, "http://") || strings.HasPrefix(url, "https://")
87 | }
88 |
89 | func (dl Downloader) extractNameFromURL(url string) string {
90 | return filepath.Base(url)
91 | }
--------------------------------------------------------------------------------
/filemanager/file_info.go:
--------------------------------------------------------------------------------
1 | package filemanager
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "os"
7 | "io/ioutil"
8 | "path/filepath"
9 | "github.com/ortizdavid/go-nopain/collections"
10 | )
11 |
12 | type FileInfo struct {}
13 |
14 | // GetFileInfo prints information about a file.
15 | func (finfo *FileInfo) GetFileInfo(fileName string) {
16 | fileInfo, err := os.Stat(fileName)
17 | if err != nil {
18 | log.Fatal(err.Error())
19 | }
20 | fmt.Println("\nFile Name:", fileInfo.Name())
21 | fmt.Println("Extension:", finfo.GetFileExt(fileName))
22 | fmt.Println("Size:", fileInfo.Size(), " bytes")
23 | fmt.Println("Type:", finfo.GetFileType(finfo.GetFileExt(fileName)))
24 | fmt.Println("Last Modified:", fileInfo.ModTime())
25 | fmt.Println("Permissions:", fileInfo.Mode())
26 | }
27 |
28 | // ListFiles lists all files in a directory.
29 | func (finfo *FileInfo) ListFiles(dir string) {
30 | files, err := ioutil.ReadDir(dir)
31 | if err != nil {
32 | log.Fatal(err.Error())
33 | }
34 | fmt.Printf("\nAll Files in '%s':\n", dir)
35 | printChar("-", 125)
36 | fmt.Println("NAME\t\t\t\tTYPE\t\t\tSIZE(Bytes)\t\t\tLAST MODIFIED")
37 | printChar("-", 125)
38 | for _, file := range files {
39 | ext := finfo.GetFileExt((file.Name()))
40 | fmt.Printf("%s\t\t\t%s\t\t\t%d\t\t\t%s\n", file.Name(), finfo.GetFileType(ext), file.Size(), file.ModTime())
41 | }
42 | }
43 |
44 | // GetFileExt returns the extension of a file.
45 | func (finfo *FileInfo) GetFileExt(path string) string {
46 | return filepath.Ext(path)
47 | }
48 |
49 | // GetFileType returns the type of a file based on its extension.
50 | func (finfo *FileInfo) GetFileType(extension string) string {
51 | imageExts := []string{".png", ".gif", ".jpg", ".jiff"}
52 | documentExts := []string{".txt", ".pdf", ".docx", ".ppt", ".pptx"}
53 | audioExts := []string{".mp3", ".aac", ".wav", ".flac"}
54 | videoExts := []string{".mp4", ".mkv", ".avi", ".flv"}
55 | fileType := ""
56 |
57 | if collections.ContainsString(imageExts, extension) {
58 | fileType = "Image"
59 | } else if collections.ContainsString(documentExts, extension) {
60 | fileType = "Document"
61 | } else if collections.ContainsString(audioExts, extension) {
62 | fileType = "Audio"
63 | } else if collections.ContainsString(documentExts, extension) {
64 | fileType = "Document"
65 | } else if collections.ContainsString(videoExts, extension) {
66 | fileType = "Video"
67 | } else {
68 | fileType = "Other"
69 | }
70 | return fileType
71 | }
72 |
73 | // IsDir checks if a path is a directory.
74 | func (finfo *FileInfo) IsDir(path string) bool {
75 | fileInfo, err := os.Stat(path)
76 | if err != nil {
77 | log.Fatal(err.Error())
78 | }
79 | if fileInfo.IsDir() {
80 | return true
81 | } else {
82 | return false
83 | }
84 | }
85 |
86 | // ExistsDir checks if a directory exists at the specified path.
87 | func (finfo *FileInfo) ExistsDir(path string) bool {
88 | if fileInfo, err := os.Stat(path); err != nil {
89 | if os.IsNotExist(err) {
90 | return false
91 | }
92 | } else {
93 | return fileInfo.IsDir()
94 | }
95 | return false
96 | }
97 |
98 | // FileExists checks if a file exists in a folder.
99 | func (finfo *FileInfo) FileExists(folder string, fileName string) bool {
100 | filePath := folder + "/" + fileName
101 | if _, err := os.Stat(filePath); err == nil {
102 | return true
103 | } else if os.IsNotExist(err) {
104 | return false
105 | } else {
106 | return false
107 | }
108 | }
109 |
110 |
111 | // Current dir
112 | func (finfo *FileInfo) CurrentFolder() (string, error) {
113 | cwd, err := os.Getwd()
114 | if err != nil {
115 | return "", nil
116 | }
117 | folderName := filepath.Base(cwd)
118 | return folderName, nil
119 | }
120 |
121 | // printChar prints a character multiple times.
122 | func printChar(ch string, chSize int) {
123 | for i := 0; i < chSize; i++ {
124 | fmt.Print(ch)
125 | }
126 | fmt.Println()
127 | }
128 |
--------------------------------------------------------------------------------
/filemanager/file_info_test.go:
--------------------------------------------------------------------------------
1 | package filemanager
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func Test_FileInfo(t *testing.T) {
8 |
9 | }
--------------------------------------------------------------------------------
/filemanager/file_zip.go:
--------------------------------------------------------------------------------
1 | package filemanager
2 |
3 | import (
4 | "io"
5 | "os"
6 | "archive/zip"
7 | "path/filepath"
8 | )
9 |
10 | type FileZip struct {}
11 |
12 | // ZipFileOrFolder compresses a file or folder into a ZIP archive.
13 | // It takes a source file or folder path and a target ZIP file path as input.
14 | // Returns an error if any occurs during the compression process.
15 | func (fzip *FileZip) ZipFileOrFolder(source string, target string) error {
16 | // 1. Create a ZIP file and zip.Writer
17 | f, err := os.Create(target)
18 | if err != nil {
19 | return err
20 | }
21 | defer f.Close()
22 |
23 | writer := zip.NewWriter(f)
24 | defer writer.Close()
25 |
26 | // Go through all the files of the source
27 | return filepath.Walk(source, func(path string, info os.FileInfo, err error) error {
28 | if err != nil {
29 | return err
30 | }
31 | // Create a local file header
32 | header, err := zip.FileInfoHeader(info)
33 | if err != nil {
34 | return err
35 | }
36 | // set compression
37 | header.Method = zip.Deflate
38 |
39 | // Set relative path of a file as the header name
40 | header.Name, err = filepath.Rel(filepath.Dir(source), path)
41 | if err != nil {
42 | return err
43 | }
44 | if info.IsDir() {
45 | header.Name += "/"
46 | }
47 | // Create writer for the file header and save content of the file
48 | headerWriter, err := writer.CreateHeader(header)
49 | if err != nil {
50 | return err
51 | }
52 | if info.IsDir() {
53 | return nil
54 | }
55 | f, err := os.Open(path)
56 | if err != nil {
57 | return err
58 | }
59 | defer f.Close()
60 | _, err = io.Copy(headerWriter, f)
61 | return err
62 | })
63 | }
64 |
--------------------------------------------------------------------------------
/filemanager/file_zip_test.go:
--------------------------------------------------------------------------------
1 | package filemanager
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | type fileOrFolder struct {
8 | source string
9 | target string
10 | expected any
11 | }
12 |
13 | var fileZip FileZip
14 |
15 | func Test_ZipFile(t *testing.T) {
16 | testCases := [] fileOrFolder {
17 | { source: "Files/file.csv", target: "Files/file.csv.zip", expected: nil },
18 | { source: "Files/file.txt", target: "Files/file.txt.zip", expected: nil },
19 | { source: "Files/file.json", target: "Files/file.json.zip", expected: nil },
20 | { source: "Files/file.log", target: "Files/file.log.zip", expected: nil },
21 | }
22 | for _, test := range testCases {
23 | got := fileZip.ZipFileOrFolder(test.source, test.target)
24 | if test.expected != got {
25 | t.Error(test.source, " not Zipped. Got: ", got, "Expected: ", test.expected)
26 | }
27 | }
28 | }
29 |
30 | func Test_ZipFolder(t *testing.T) {
31 | testCases := [] fileOrFolder {
32 | { source: "Documents", target: "Documents.zip", expected: nil },
33 | { source: "Images", target: "Images.zip", expected: nil },
34 | { source: "Musics", target: "Musics.zip", expected: nil },
35 | { source: "Others", target: "Others.zip", expected: nil },
36 | }
37 | for _, test := range testCases {
38 | got := fileZip.ZipFileOrFolder(test.source, test.target)
39 | if test.expected != got {
40 | t.Error(test.source, " not Zipped. Got: ", got, "Expected: ", test.expected)
41 | }
42 | }
43 | }
--------------------------------------------------------------------------------
/filemanager/sample3.txt:
--------------------------------------------------------------------------------
1 | Quod equidem non reprehendo;
2 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quibus natura iure responderit non esse verum aliunde finem beate vivendi, a se principia rei gerendae peti; Quae enim adhuc protulisti, popularia sunt, ego autem a te elegantiora desidero. Duo Reges: constructio interrete. Tum Lucius: Mihi vero ista valde probata sunt, quod item fratri puto. Bestiarum vero nullum iudicium puto. Nihil enim iam habes, quod ad corpus referas; Deinde prima illa, quae in congressu solemus: Quid tu, inquit, huc? Et homini, qui ceteris animantibus plurimum praestat, praecipue a natura nihil datum esse dicemus?
3 |
4 | Iam id ipsum absurdum, maximum malum neglegi. Quod ea non occurrentia fingunt, vincunt Aristonem; Atqui perspicuum est hominem e corpore animoque constare, cum primae sint animi partes, secundae corporis. Fieri, inquam, Triari, nullo pacto potest, ut non dicas, quid non probes eius, a quo dissentias. Equidem e Cn. An dubium est, quin virtus ita maximam partem optineat in rebus humanis, ut reliquas obruat?
5 |
6 | Quis istum dolorem timet?
7 | Summus dolor plures dies manere non potest? Dicet pro me ipsa virtus nec dubitabit isti vestro beato M. Tubulum fuisse, qua illum, cuius is condemnatus est rogatione, P. Quod si ita sit, cur opera philosophiae sit danda nescio.
8 |
9 | Ex eorum enim scriptis et institutis cum omnis doctrina liberalis, omnis historia.
10 | Quod si ita est, sequitur id ipsum, quod te velle video, omnes semper beatos esse sapientes. Cum enim fertur quasi torrens oratio, quamvis multa cuiusque modi rapiat, nihil tamen teneas, nihil apprehendas, nusquam orationem rapidam coerceas. Ita redarguitur ipse a sese, convincunturque scripta eius probitate ipsius ac moribus. At quanta conantur! Mundum hunc omnem oppidum esse nostrum! Incendi igitur eos, qui audiunt, vides. Vide, ne magis, inquam, tuum fuerit, cum re idem tibi, quod mihi, videretur, non nova te rebus nomina inponere. Qui-vere falsone, quaerere mittimus-dicitur oculis se privasse; Si ista mala sunt, in quae potest incidere sapiens, sapientem esse non esse ad beate vivendum satis. At vero si ad vitem sensus accesserit, ut appetitum quendam habeat et per se ipsa moveatur, quid facturam putas?
11 |
12 | Quem si tenueris, non modo meum Ciceronem, sed etiam me ipsum abducas licebit.
13 | Stulti autem malorum memoria torquentur, sapientes bona praeterita grata recordatione renovata delectant.
14 | Esse enim quam vellet iniquus iustus poterat inpune.
15 | Quae autem natura suae primae institutionis oblita est?
16 | Verum tamen cum de rebus grandioribus dicas, ipsae res verba rapiunt;
17 | Hoc est non modo cor non habere, sed ne palatum quidem.
18 | Voluptatem cum summum bonum diceret, primum in eo ipso parum vidit, deinde hoc quoque alienum; Sed tu istuc dixti bene Latine, parum plane. Nam haec ipsa mihi erunt in promptu, quae modo audivi, nec ante aggrediar, quam te ab istis, quos dicis, instructum videro. Fatebuntur Stoici haec omnia dicta esse praeclare, neque eam causam Zenoni desciscendi fuisse. Non autem hoc: igitur ne illud quidem. Ratio quidem vestra sic cogit. Cum audissem Antiochum, Brute, ut solebam, cum M. An quod ita callida est, ut optime possit architectari voluptates?
19 |
20 | Idemne, quod iucunde?
21 | Haec mihi videtur delicatior, ut ita dicam, molliorque ratio, quam virtutis vis gravitasque postulat. Sed quoniam et advesperascit et mihi ad villam revertendum est, nunc quidem hactenus; Cuius ad naturam apta ratio vera illa et summa lex a philosophis dicitur. Neque solum ea communia, verum etiam paria esse dixerunt. Sed nunc, quod agimus; A mene tu?
--------------------------------------------------------------------------------
/formatter/README.md:
--------------------------------------------------------------------------------
1 | # formatter
2 | Package that handles formatting
3 |
4 | ## Formatters
5 | - Currency
6 | - Padding (Left and Right)
--------------------------------------------------------------------------------
/formatter/currency.go:
--------------------------------------------------------------------------------
1 | package formatter
2 |
3 | // FormatCurrency formats the given amount as currency with the specified symbol.
4 | // It takes a float64 amount and a string symbol as input and returns the formatted currency string.
5 | func FormatCurrency(amount float64, symbol string) string {
6 | return ""
7 | }
8 |
--------------------------------------------------------------------------------
/formatter/currency_test.go:
--------------------------------------------------------------------------------
1 | package formatter
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | func Test_Currency(t *testing.T) {
9 | fmt.Println(FormatCurrency(6598721324, "USD"))
10 | }
--------------------------------------------------------------------------------
/formatter/padding.go:
--------------------------------------------------------------------------------
1 | package formatter
2 |
3 | import "strings"
4 |
5 | // Add repeated chars to the left of a string value
6 | // Returns the concatenation of Padding and Original value
7 | // Padding: char reptead N times
8 | func LeftPadWithChar(originalValue string, allowedLenght int, char string) string {
9 | originalLength := len(originalValue)
10 | if allowedLenght <= 0 || char == "" || originalLength == 0 {
11 | return ""
12 | }
13 | if originalLength >= allowedLenght {
14 | return originalValue
15 | }
16 | paddingLength := allowedLenght - originalLength
17 | padding := strings.Repeat(char, paddingLength)
18 | return padding + originalValue
19 | }
20 |
21 |
22 | // Add repeated chars to the right of a string value
23 | // Returns the concatenation of Padding and Original value
24 | // Padding: char reptead N times
25 | func RightPadWithChar(originalValue string, allowedLenght int, char string) string {
26 | originalLength := len(originalValue)
27 | if allowedLenght <= 0 || char == "" || originalLength == 0 {
28 | return ""
29 | }
30 | if originalLength >= allowedLenght {
31 | return originalValue
32 | }
33 | paddingLength := allowedLenght - originalLength
34 | padding := strings.Repeat(char, paddingLength)
35 | return originalValue + padding
36 | }
37 |
38 |
39 |
--------------------------------------------------------------------------------
/formatter/padding_test.go:
--------------------------------------------------------------------------------
1 | package formatter
2 |
3 | import "testing"
4 |
5 |
6 | func Test_LeftPadding(t *testing.T) {
7 | original := "123"
8 | allowedLength := 8
9 | char := "0"
10 | expected := "00000123"
11 | result := LeftPadWithChar(original, allowedLength, char)
12 | if result != expected {
13 | t.Errorf("\nLeftPadWitchar(%s, %d, %s) = %s. got: %s", original, allowedLength, char, result, expected)
14 | }
15 | }
16 |
17 |
18 | func Test_RightPadding(t *testing.T) {
19 | original := "test"
20 | allowedLength := 12
21 | char := "*"
22 | expected := "test********"
23 | result := RightPadWithChar(original, allowedLength, char)
24 | if result != expected {
25 | t.Errorf("\nLeftPadWitchar(%s, %d, %s) = %s. got: %s", original, allowedLength, char, result, expected)
26 | }
27 | }
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/ortizdavid/go-nopain
2 |
3 | go 1.22.4
4 |
5 | require (
6 | github.com/google/uuid v1.4.0
7 | golang.org/x/crypto v0.31.0
8 | )
9 |
10 | require (
11 | github.com/SebastiaanKlippert/go-wkhtmltopdf v1.9.2
12 | github.com/gorilla/sessions v1.2.2
13 | github.com/rabbitmq/amqp091-go v1.9.0
14 | github.com/tealeg/xlsx v1.0.5
15 | )
16 |
17 | require (
18 | github.com/klauspost/compress v1.17.9 // indirect
19 | github.com/nats-io/nats.go v1.39.1 // indirect
20 | github.com/nats-io/nkeys v0.4.9 // indirect
21 | github.com/nats-io/nuid v1.0.1 // indirect
22 | golang.org/x/sys v0.28.0 // indirect
23 | )
24 |
25 | require (
26 | github.com/gorilla/securecookie v1.1.2 // indirect
27 | golang.org/x/time v0.5.0
28 | )
29 |
--------------------------------------------------------------------------------
/httputils/README.md:
--------------------------------------------------------------------------------
1 | # httputils
2 |
3 | Package for http common needs
4 |
5 | ## Featuares
6 | - [x] Response customization (JSON and XML)
7 | - [ ] Response with HATEOAS
8 | - [x] HTTP Authentication (Basic and API Key) middlewares
9 | - [x] HTTP Client
10 | - [x] Pagination JSON and XML
11 |
--------------------------------------------------------------------------------
/httputils/apikey_middleware.go:
--------------------------------------------------------------------------------
1 | package httputils
2 |
3 | import (
4 | "errors"
5 | "log"
6 | "net/http"
7 | "sync"
8 | )
9 |
10 | // ApiKeyMiddleware represents middleware for API key authentication.
11 | // It allows setting a default API key, which can be overridden by the X-API-Key header in the request.
12 | type ApiKeyMiddleware struct {
13 | defaultKey string
14 | mu sync.RWMutex
15 | }
16 |
17 | // Return a ApiKeyMiddleware object, and initialize default key with the apiKey passed
18 | func NewApiKeyMiddleWare(apiKey string) ApiKeyMiddleware {
19 | return ApiKeyMiddleware{
20 | defaultKey: apiKey,
21 | }
22 | }
23 |
24 | // Apply applies the API key middleware to a handler. It wraps the handler function.
25 | // This method should be used with mux.Handle instead of mux.HandleFunc.
26 | // Example usage: mux.Handle("GET /protected", middleware.Apply(protectedHandler)).
27 | func (apiMid *ApiKeyMiddleware) Apply(handler func(w http.ResponseWriter, r *http.Request)) http.Handler {
28 | return apiMid.applyMiddleware(http.HandlerFunc(handler))
29 | }
30 |
31 | // applyMiddleware applies the API key middleware to a handler function.
32 | // It retrieves the API key from the request header and validates it against the default key.
33 | func (apiMid *ApiKeyMiddleware) applyMiddleware(next http.Handler) http.Handler {
34 | validApiKey := apiMid.getDefaultKey()
35 | return http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) {
36 | apiKey := r.Header.Get("X-API-Key")
37 | if apiKey == "" {
38 | log.Printf("Unauthorized request. API Key missing, URL: %s", r.URL.Path)
39 | http.Error(w, "Unauthorized. API Key missing", http.StatusUnauthorized)
40 | return
41 | }
42 | if apiKey != validApiKey {
43 | log.Printf("Unauthorized request. Invalid API Key, URL: %s", r.URL.Path)
44 | http.Error(w, "Unauthorized. Invalid API Key", http.StatusUnauthorized)
45 | return
46 | }
47 | next.ServeHTTP(w, r)
48 | })
49 | }
50 |
51 | // SetDefaultKey sets the default API key.
52 | // It returns an error if the provided key is empty.
53 | func (apiMid *ApiKeyMiddleware) SetDefaultKey(value string) error {
54 | if value == "" {
55 | return errors.New("API key cannot be empty")
56 | }
57 | apiMid.mu.Lock()
58 | apiMid.defaultKey = value
59 | apiMid.mu.Unlock()
60 | return nil
61 | }
62 |
63 | // getDefaultKey returns the default API key.
64 | func (apiMid *ApiKeyMiddleware) getDefaultKey() string {
65 | apiMid.mu.Lock()
66 | defer apiMid.mu.Unlock()
67 | return apiMid.defaultKey
68 | }
69 |
--------------------------------------------------------------------------------
/httputils/apikey_middleware_test.go:
--------------------------------------------------------------------------------
1 | package httputils
--------------------------------------------------------------------------------
/httputils/apikey_user_middleware.go:
--------------------------------------------------------------------------------
1 | package httputils
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "net/http"
7 | "sync"
8 | )
9 |
10 | // ApiKeyUserMiddleware represents middleware for API key authentication.
11 | // It maintains a list of user IDs and their corresponding API keys.
12 | type ApiKeyUserMiddleware struct {
13 | userApikeys map[string]string // List of user IDs and API keys
14 | mu sync.RWMutex // Mutex for thread safety
15 | }
16 |
17 |
18 | // UserApiKey represents a user ID and its associated API key.
19 | // This struct is used to store user credentials.
20 | type UserApiKey struct {
21 | UserId string
22 | ApiKey string
23 | }
24 |
25 |
26 | // NewApiKeyUserMiddleware creates a new instance of ApiKeyUserMiddleware with the provided list of user keys.
27 | func NewApiKeyUserMiddleware(userKeyList []UserApiKey) *ApiKeyUserMiddleware {
28 | apiKeyMap := make(map[string]string)
29 | for _, uk:= range userKeyList {
30 | apiKeyMap[uk.UserId] = uk.ApiKey
31 | }
32 | return &ApiKeyUserMiddleware{
33 | userApikeys: apiKeyMap,
34 | mu: sync.RWMutex{},
35 | }
36 | }
37 |
38 | // Apply applies the API key middleware to a handler. It wraps the handler function.
39 | // This method should be used with mux.Handle instead of mux.HandleFunc.
40 | // Example usage: mux.Handle("GET /protected", middleware.Apply(protectedHandler)).
41 | func (apiMid *ApiKeyUserMiddleware) Apply(handler func(w http.ResponseWriter, r *http.Request)) http.Handler {
42 | return apiMid.applyMiddleware(http.HandlerFunc(handler))
43 | }
44 |
45 |
46 | // applyMiddleware applies the API key middleware to a handler function.
47 | // It retrieves the API key from the request header and validates it.
48 | // The X-API-Key header must be valid and non-empty for authentication to succeed.
49 | func (apiMid *ApiKeyUserMiddleware) applyMiddleware(next http.Handler) http.Handler {
50 | return http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) {
51 | userId := r.Header.Get("X-User-Id")
52 | if userId == "" {
53 | log.Printf("Unauthorized request. User ID missing. URL: %s", r.URL.Path)
54 | http.Error(w, "Unauthorized. User Id missing", http.StatusUnauthorized)
55 | return
56 | }
57 | requestedAPIKey := r.Header.Get("X-API-KEY")
58 | if requestedAPIKey == "" {
59 | log.Printf("Unauthorized request. API Key missing. User ID: %s, URL: %s", userId, r.URL.Path)
60 | http.Error(w, "Unauthorized. API Key missing", http.StatusUnauthorized)
61 | return
62 | }
63 | apiKey, err := apiMid.GetApiKey(userId)
64 | if err != nil {
65 | log.Printf("Unauthorized request. User ID: %s, Error: %s, URL: %s", userId, err.Error(), r.URL.Path)
66 | http.Error(w, err.Error(), http.StatusUnauthorized)
67 | return
68 | }
69 | if requestedAPIKey != apiKey {
70 | log.Printf("Unauthorized request. Invalid API Key. User ID: %s, URL: %s", userId, r.URL.Path)
71 | http.Error(w, "Unauthorized. Invalid API Key", http.StatusUnauthorized)
72 | return
73 | }
74 | next.ServeHTTP(w, r)
75 | })
76 | }
77 |
78 |
79 | // GetApiKey retrieves the API key for a specific user ID.
80 | // It searches the list of user API keys maintained by the middleware.
81 | func (apiMid *ApiKeyUserMiddleware) GetApiKey(userId string) (string, error) {
82 | apiMid.mu.RLock()
83 | defer apiMid.mu.RUnlock()
84 | apiKey, exists := apiMid.userApikeys[userId]
85 | if !exists {
86 | return "", fmt.Errorf("key not found for user %s", userId)
87 | }
88 | return apiKey, nil
89 | }
90 |
91 |
92 |
--------------------------------------------------------------------------------
/httputils/apikey_user_middleware_test.go:
--------------------------------------------------------------------------------
1 | package httputils
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 | "net/http/httptest"
7 | "testing"
8 | )
9 |
10 | type endpointTest struct {
11 | path string
12 | statusCode int
13 | }
14 |
15 | func TestApiKeyUser(t *testing.T) {
16 | // Setup
17 | userApiKeys := getAllUserKeys()
18 | middleware := NewApiKeyUserMiddleware(userApiKeys)
19 | mux := http.NewServeMux()
20 | mux.HandleFunc("/", indexHandler2)
21 | mux.HandleFunc("/public", publicHandler2)
22 | mux.Handle("/protected", middleware.Apply(protectedHandler3))
23 | mux.Handle("/protected-2", middleware.Apply(protectedHandler4))
24 |
25 | // Start test server
26 | srv := httptest.NewServer(mux)
27 | defer srv.Close()
28 |
29 | // Test cases
30 | cases := []endpointTest{
31 | {path: "/", statusCode: http.StatusOK}, // indexHandler2 should return 200 OK
32 | {path: "/public", statusCode: http.StatusOK}, // publicHandler2 should return 200 OK
33 | {path: "/protected", statusCode: http.StatusUnauthorized}, // protectedHandler3 should return 401 Unauthorized without API key
34 | // Add more test cases as needed
35 | }
36 |
37 | for _, tc := range cases {
38 | url := srv.URL + tc.path
39 | req, err := http.NewRequest("GET", url, nil)
40 | if err != nil {
41 | t.Fatalf("could not create request: %v", err)
42 | }
43 |
44 | // Send request without API key
45 | resp, err := http.DefaultClient.Do(req)
46 | if err != nil {
47 | t.Fatalf("request failed: %v", err)
48 | }
49 | defer resp.Body.Close()
50 |
51 | if resp.StatusCode != tc.statusCode {
52 | t.Errorf("expected status code %d, got %d", tc.statusCode, resp.StatusCode)
53 | }
54 | }
55 | }
56 |
57 | func indexHandler2(w http.ResponseWriter, r *http.Request) {
58 | fmt.Fprintln(w, "Index")
59 | }
60 |
61 | func publicHandler2(w http.ResponseWriter, r *http.Request) {
62 | fmt.Fprintln(w, "Public Content")
63 | }
64 |
65 | func protectedHandler3(w http.ResponseWriter, r *http.Request) {
66 | fmt.Fprintln(w, "Protected Content")
67 | }
68 |
69 | func protectedHandler4(w http.ResponseWriter, r *http.Request) {
70 | fmt.Fprintln(w, "Protected 4 Content")
71 | }
72 |
73 | func getAllUserKeys() []UserApiKey {
74 | return []UserApiKey{
75 | {UserId: "user1", ApiKey: "key1"},
76 | {UserId: "user2", ApiKey: "key2"},
77 | {UserId: "user3", ApiKey: "key3"},
78 | {UserId: "user4", ApiKey: "key4"},
79 | }
80 | }
81 |
82 |
--------------------------------------------------------------------------------
/httputils/basic_auth_middleware.go:
--------------------------------------------------------------------------------
1 | package httputils
2 |
3 | import (
4 | "encoding/base64"
5 | "fmt"
6 | "net/http"
7 | "strings"
8 | )
9 |
10 | // BasicAuthMiddleware provides middleware for basic authentication
11 | type BasicAuthMiddleware struct {
12 | users []UserBasicAuth
13 | }
14 |
15 | // UserBasicAuth represents a user with username and password
16 | type UserBasicAuth struct {
17 | Username string
18 | Password string
19 | }
20 |
21 |
22 | // NewBasicAuthMiddleware returns a new BasicAuthMiddleware object with the provided username and password.
23 | func NewBasicAuthMiddleware(allowedUsers []UserBasicAuth) *BasicAuthMiddleware {
24 | return &BasicAuthMiddleware{
25 | users: allowedUsers,
26 | }
27 | }
28 |
29 |
30 | // NewBasicAuthMiddleware returns a new BasicAuthMiddleware object with the provided username and password.
31 | func (ba *BasicAuthMiddleware) Apply(handler func(w http.ResponseWriter, r *http.Request)) http.Handler {
32 | return ba.applyMiddleware(http.HandlerFunc(handler))
33 | }
34 |
35 |
36 | // applyMiddleware applies the basic authentication middleware.
37 | func (ba *BasicAuthMiddleware) applyMiddleware(next http.Handler) http.Handler {
38 | return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
39 | // Get the Authorization header
40 | authHeader := r.Header.Get("Authorization")
41 | // Check if the Authorization header is set
42 | if authHeader == "" {
43 | // No Authorization header provided, request authentication
44 | w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
45 | w.WriteHeader(http.StatusUnauthorized)
46 | fmt.Fprintln(w, "Unauthorized access")
47 | return
48 | }
49 | // Check if the Authorization header starts with "Basic"
50 | if !strings.HasPrefix(authHeader, "Basic ") {
51 | // Invalid Authorization header
52 | w.WriteHeader(http.StatusBadRequest)
53 | fmt.Fprintln(w, "Invalid Authorization header")
54 | return
55 | }
56 | // Decode the base64-encoded credentials
57 | credentials, err := base64.StdEncoding.DecodeString(authHeader[len("Basic "):])
58 | if err != nil {
59 | w.WriteHeader(http.StatusBadRequest)
60 | fmt.Fprintf(w, "Error decoding credentials: %v\n", err)
61 | return
62 | }
63 | // Split the credentials into username and password
64 | parts := strings.SplitN(string(credentials), ":", 2)
65 | if len(parts) != 2 {
66 | // Malformed credentials
67 | w.WriteHeader(http.StatusBadRequest)
68 | fmt.Fprintln(w, "Malformed credentials")
69 | return
70 | }
71 | // Check if the provided username and password are valid
72 | username, password := parts[0], parts[1]
73 | if !ba.isValidUser(username, password) {
74 | // Incorrect username or password
75 | w.WriteHeader(http.StatusUnauthorized)
76 | fmt.Fprintln(w, "Unauthorized access")
77 | return
78 | }
79 | next.ServeHTTP(w, r)
80 | })
81 | }
82 |
83 |
84 | // isValidUser checks if the provided username and password are valid
85 | func (ba *BasicAuthMiddleware) isValidUser(username, password string) bool {
86 | for _, user := range ba.users {
87 | if user.Username == username && user.Password == password {
88 | return true
89 | }
90 | }
91 | return false
92 | }
--------------------------------------------------------------------------------
/httputils/basic_auth_middleware_test.go:
--------------------------------------------------------------------------------
1 | package httputils
--------------------------------------------------------------------------------
/httputils/csrf_middleware.go:
--------------------------------------------------------------------------------
1 | package httputils
2 |
3 | import (
4 | "net/http"
5 | "time"
6 | )
7 |
8 | type CsrfMiddleware struct {
9 | tokenLength int
10 | cookieName string
11 | headerName string
12 | expiration time.Duration
13 | }
14 |
15 | func NewCsrfMiddleware() CsrfMiddleware {
16 | return CsrfMiddleware{}
17 | }
18 |
19 | func (csrf CsrfMiddleware) Apply(next http.Handler) http.Handler {
20 | return nil
21 | }
22 |
23 | func (csrf CsrfMiddleware) CreateToken(w http.ResponseWriter) (string, error) {
24 | return "", nil
25 | }
26 |
27 | func (csrf CsrfMiddleware) ValidateToken(w http.ResponseWriter) error {
28 | return nil
29 | }
--------------------------------------------------------------------------------
/httputils/hateoas_response.go:
--------------------------------------------------------------------------------
1 | package httputils
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 | "github.com/ortizdavid/go-nopain/serialization"
7 | )
8 |
9 | // hateoasJson represents the structure of a HATEOAS JSON response.
10 | type hateoasJson struct {
11 | Message *string `json:"message,omitempty"` // Message field for the response (optional)
12 | Status int `json:"status"` // Status code of the response
13 | Count *int `json:"count,omitempty"` // Count field for the response (optional)
14 | Data any `json:"data,omitempty"` // Data field for the response (optional)
15 | Links link `json:"links"` // Links field for HATEOAS
16 | }
17 |
18 | // link represents the structure of a link in a HATEOAS JSON response.
19 | type link struct {
20 | Path string `json:"path"` // Path of the link
21 | Self string `json:"self"` // Self link
22 | Rel string `json:"rel,omitempty"` // Relationship of the link (optional)
23 | }
24 |
25 | // WriteHateoasJson writes a HATEOAS JSON response with the provided status code, data, and count.
26 | func WriteHateoasJson(w http.ResponseWriter, r *http.Request, statusCode int, data any, count int) {
27 | writeJsonHeader(w, statusCode)
28 | basePath := fmt.Sprintf("%s://%s", r.URL.Scheme, r.URL.Host)
29 | selfLink := basePath + r.URL.Path
30 |
31 | response := hateoasJson{
32 | Status: statusCode,
33 | Count: &count,
34 | Data: data,
35 | Links: link{
36 | Path: basePath,
37 | Self: selfLink,
38 | Rel: "",
39 | },
40 | }
41 | encodeHateoas(w, response)
42 | }
43 |
44 | // encodeHateoas encodes the hateoasJson struct to JSON format and writes it to the response writer.
45 | func encodeHateoas(w http.ResponseWriter, response hateoasJson) {
46 | err := serialization.EncodeJson(w, response)
47 | if err != nil {
48 | fmt.Fprintf(w, "%s", err.Error())
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/httputils/hateoas_response_test.go:
--------------------------------------------------------------------------------
1 | package httputils
--------------------------------------------------------------------------------
/httputils/http_client_test.go:
--------------------------------------------------------------------------------
1 | package httputils
--------------------------------------------------------------------------------
/httputils/json_response.go:
--------------------------------------------------------------------------------
1 | package httputils
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 | "github.com/ortizdavid/go-nopain/serialization"
7 | )
8 |
9 | // jsonResponse represents the structure of a JSON response.
10 | type jsonResponse struct {
11 | Message *string `json:"message,omitempty"` // Message field for the response (optional)
12 | Status int `json:"status"` // Status code of the response
13 | Count *int `json:"count,omitempty"` // Count field for the response (optional)
14 | Data any `json:"data,omitempty"` // Data field for the response (optional)
15 | }
16 |
17 | // WriteJson writes a simple JSON response with the provided status code and data.
18 | func WriteJson(w http.ResponseWriter, statusCode int, data any) {
19 | writeJsonHeader(w, statusCode)
20 | response := jsonResponse{
21 | Status: statusCode,
22 | Data: data,
23 | }
24 | encodeJson(w, response)
25 | }
26 |
27 | // WriteJsonSimple writes a simple JSON response with the provided status code
28 | func WriteJsonSimple(w http.ResponseWriter, statusCode int, data any) {
29 | writeJsonHeader(w, statusCode)
30 | encodeJson(w, data)
31 | }
32 |
33 |
34 | // WriteJsonPaginated writes a paginated JSON response to the provided http.ResponseWriter.
35 | // It includes the paginated items, pagination metadata,
36 | // and handles potential errors during pagination or JSON encoding.
37 | func WriteJsonPaginated[T any](w http.ResponseWriter, r *http.Request, items []T, count int64, currentPage int, limit int) {
38 | writeJsonHeader(w, 200)
39 | pagination, err := NewPagination(r, items, count, currentPage, limit)
40 | if err != nil {
41 | WriteJsonError(w, err.Error(), http.StatusInternalServerError)
42 | return
43 | }
44 | if err := serialization.EncodeJson(w, pagination); err != nil {
45 | fmt.Fprintf(w, "%s", err.Error())
46 | }
47 | }
48 |
49 | // WriteJsonError writes a JSON error response with the provided message and status code.
50 | func WriteJsonError(w http.ResponseWriter, message string, statusCode int) {
51 | writeJsonHeader(w, statusCode)
52 | response := jsonResponse{
53 | Message: &message,
54 | Status: statusCode,
55 | }
56 | encodeJson(w, response)
57 | }
58 |
59 | // writeJsonHeader writes the JSON content type header and sets the HTTP status code.
60 | func writeJsonHeader(w http.ResponseWriter, statusCode int) {
61 | w.Header().Set("Content-type", "application/json")
62 | w.WriteHeader(statusCode)
63 | }
64 |
65 | // encodeJson encodes the jsonResponse struct to JSON format and writes it to the response writer.
66 | func encodeJson(w http.ResponseWriter, response interface{}) {
67 | err := serialization.EncodeJson(w, response)
68 | if err != nil {
69 | fmt.Fprintf(w, "%s", err.Error())
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/httputils/json_response_test.go:
--------------------------------------------------------------------------------
1 | package httputils
--------------------------------------------------------------------------------
/httputils/log_request.go:
--------------------------------------------------------------------------------
1 | package httputils
2 |
3 | import (
4 | "log"
5 | "net/http"
6 | )
7 |
8 | func logRequests(handler http.Handler) http.Handler {
9 | return http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) {
10 | log.Printf("%s %s %s", r.Method, r.RequestURI, r.RemoteAddr)
11 | handler.ServeHTTP(w, r)
12 | })
13 | }
14 |
15 | func UseLoggingMiddleware(mux *http.ServeMux) {
16 | logRequests(mux)
17 | }
--------------------------------------------------------------------------------
/httputils/pagination.go:
--------------------------------------------------------------------------------
1 | package httputils
2 |
3 | import (
4 | "encoding/xml"
5 | "fmt"
6 | "math"
7 | "net/http"
8 |
9 | "github.com/ortizdavid/go-nopain/conversion"
10 | )
11 |
12 | type Pagination[T any] struct {
13 | Items []T `json:"items"`
14 | MetaData MetaData `json:"metadata"`
15 | }
16 |
17 | type MetaData struct {
18 | XMLName xml.Name `json:"-" xml:"metadata"`
19 | CurrentPage int `json:"current_page" xml:"currentPage"`
20 | TotalItems int64 `json:"total_items" xml:"totalItems"`
21 | TotalPages int `json:"total_pages" xml:"totalPages"`
22 | FirstPageUrl string `json:"first_page_url" xml:"firstPageUrl"`
23 | PreviousPageUrl string `json:"previous_page_url" xml:"previousPageUrl"`
24 | NextPageUrl string `json:"next_page_url" xml:"nextPageUrl"`
25 | LastPageUrl string `json:"last_page_url" xml:"lastPageUrl"`
26 | }
27 |
28 | func NewPagination[T any](r *http.Request, items []T, count int64, currentPage int, limit int) (*Pagination[T], error) {
29 | if currentPage < 0 {
30 | return nil, fmt.Errorf("current page must be >= 0")
31 | }
32 | if limit < 1 {
33 | return nil, fmt.Errorf("page size must be >= 1")
34 | }
35 | pagination := Pagination[T]{
36 | Items: items,
37 | MetaData: MetaData{
38 | CurrentPage: currentPage,
39 | TotalItems: count,
40 | TotalPages: int(math.Ceil(float64(count) / float64(limit))),
41 | FirstPageUrl: "",
42 | PreviousPageUrl: "",
43 | NextPageUrl: "",
44 | LastPageUrl: "",
45 | },
46 | }
47 | pagination.calculateUrls(r, currentPage, limit)
48 | return &pagination, nil
49 | }
50 |
51 | func (p *Pagination[T]) HasNextPage() bool {
52 | return p.MetaData.CurrentPage < p.MetaData.TotalPages - 1
53 | }
54 |
55 | func (p *Pagination[T]) HasPreviousPage() bool {
56 | return p.MetaData.CurrentPage > 0
57 | }
58 |
59 | func (p *Pagination[T]) calculateUrls(r *http.Request, currentPage, limit int) {
60 | baseUrl := getRequestBaseUrl(r)
61 |
62 | p.MetaData.FirstPageUrl = getPageUrl(baseUrl, 0, limit)
63 | if currentPage < p.MetaData.TotalPages {
64 | p.MetaData.NextPageUrl = getPageUrl(baseUrl, currentPage+1, limit)
65 | }
66 | if currentPage > 1 {
67 | p.MetaData.PreviousPageUrl = getPageUrl(baseUrl, currentPage-1, limit)
68 | }
69 | p.MetaData.LastPageUrl = getPageUrl(baseUrl, p.MetaData.TotalPages, limit)
70 | }
71 |
72 | func getRequestBaseUrl(r *http.Request) string {
73 | scheme := "http"
74 | if r.TLS != nil {
75 | scheme = "https"
76 | }
77 | return scheme + "://" + r.Host + r.URL.Path
78 | }
79 |
80 | func getPageUrl(baseUrl string, pageNumber, limit int) string {
81 | return baseUrl + "?current_page=" + conversion.IntToString(pageNumber) + "&limit=" + conversion.IntToString(limit)
82 | }
--------------------------------------------------------------------------------
/httputils/pagination_xml.go:
--------------------------------------------------------------------------------
1 | package httputils
2 |
3 | import (
4 | "encoding/xml"
5 | "net/http"
6 | )
7 |
8 | type PaginationXML struct {
9 | XMLName xml.Name `xml:"pagination"`
10 | Items interface{} `xml:"items"`
11 | MetaData MetaData `xml:"metadata"`
12 | }
13 |
14 | // NewPaginationXML creates a Pagination object for XML response.
15 | func NewPaginationXML[T any](r *http.Request, items []T, count int64, currentPage int, limit int) (*PaginationXML, error) {
16 | pagination, err := NewPagination(r, items, count, currentPage, limit)
17 | if err != nil {
18 | return nil, err
19 | }
20 | return pagination.ToXML(), nil
21 | }
22 |
23 | func (p *Pagination[T]) ToXML() *PaginationXML {
24 | xmlData := PaginationXML{
25 | Items: p.Items,
26 | MetaData: p.MetaData,
27 | }
28 | return &xmlData
29 | }
--------------------------------------------------------------------------------
/httputils/session_gorilla.go:
--------------------------------------------------------------------------------
1 | package httputils
2 |
3 | import (
4 | "fmt"
5 | "net/http"
6 | "time"
7 | "github.com/gorilla/sessions"
8 | "github.com/ortizdavid/go-nopain/encryption"
9 | "github.com/ortizdavid/go-nopain/random"
10 | )
11 |
12 | // SessionGorilla handles sessions using gorilla/sessions.
13 | type SessionGorilla struct {
14 | store sessions.Store // Session store.
15 | name string // Session name.
16 | secretKey string // Secret key for the session.
17 | expiration time.Duration // Expiration time of the session.
18 | }
19 |
20 | // NewGorillaStore creates a new session store with a secret key.
21 | func NewGorillaStore(secretKey string) sessions.Store {
22 | return sessions.NewCookieStore([]byte(secretKey))
23 | }
24 |
25 | // NewSessionGorilla creates a new session with custom settings.
26 | func NewSessionGorilla(store sessions.Store, name string, expiration time.Duration) *SessionGorilla {
27 | return &SessionGorilla{
28 | store: store,
29 | name: name,
30 | expiration: expiration,
31 | }
32 | }
33 |
34 | // NewSessionGorillaDefault creates a new session with default settings.
35 | func NewSessionGorillaDefault() *SessionGorilla {
36 | secretKey := encryption.GenerateUUID()
37 | store := sessions.NewCookieStore([]byte(secretKey))
38 | return &SessionGorilla{
39 | store: store,
40 | name: random.String(15),
41 | secretKey: secretKey,
42 | expiration: 15 * time.Minute,
43 | }
44 | }
45 |
46 | // GetSecretKey returns the session's secret key.
47 | func (s *SessionGorilla) GetSecretKey() string {
48 | return s.secretKey
49 | }
50 |
51 | // SetExpiration updates the session expiration time.
52 | func (s *SessionGorilla) SetExpiration(expiration time.Duration) {
53 | s.expiration = expiration
54 | }
55 |
56 | // Set sets a key-value pair in the session
57 | func (s *SessionGorilla) Set(r *http.Request, w http.ResponseWriter, key string, value string) error {
58 | session, err := s.store.Get(r, s.name)
59 | if err != nil {
60 | return err
61 | }
62 | session.Values[key] = value
63 | session.Options.MaxAge = int(s.expiration.Seconds())
64 | return session.Save(r, w)
65 | }
66 |
67 | // Get retrieves a value from the session by key
68 | func (s *SessionGorilla) Get(r *http.Request, key string) (string, error) {
69 | session, err := s.store.Get(r, s.name)
70 | if err != nil {
71 | return "", err
72 | }
73 | value, ok := session.Values[key].(string)
74 | if !ok {
75 | return "", fmt.Errorf("key not found or not a string")
76 | }
77 | return value, nil
78 | }
79 |
80 | // Remove removes a key-value pair from the session
81 | func (s *SessionGorilla) Remove(r *http.Request, w http.ResponseWriter, key string) error {
82 | session, err := s.store.Get(r, s.name)
83 | if err != nil {
84 | return err
85 | }
86 | delete(session.Values, key)
87 | return session.Save(r, w)
88 | }
89 |
90 | // Destroy invalidates the entire session
91 | func (s *SessionGorilla) Destroy(r *http.Request, w http.ResponseWriter) error {
92 | session, err := s.store.Get(r, s.name)
93 | if err != nil {
94 | return err
95 | }
96 | session.Options.MaxAge = -1 // Setting MaxAge to -1 deletes the cookie
97 | return session.Save(r, w)
98 | }
99 |
--------------------------------------------------------------------------------
/httputils/session_http.go:
--------------------------------------------------------------------------------
1 | package httputils
2 |
3 | import (
4 | "net/http"
5 | "time"
6 | )
7 |
8 | // SessionHttp stores session cookie details.
9 | type SessionHttp struct {
10 | cookieName string // Name of the session cookie.
11 | expiration time.Duration // Expiration time of the session.
12 | }
13 |
14 | // NewSessionHttp creates a new session with a custom cookie name and expiration.
15 | func NewSessionHttp(cookieName string, expiration time.Duration) *SessionHttp {
16 | return &SessionHttp{
17 | cookieName: cookieName,
18 | expiration: expiration,
19 | }
20 | }
21 |
22 | // NewSessionHttpDefault creates a new session with default settings.
23 | func NewSessionHttpDefault() *SessionHttp {
24 | return &SessionHttp{
25 | cookieName: "default-session",
26 | expiration: 15 * time.Minute,
27 | }
28 | }
29 |
30 | // SetExpiration updates the session expiration time.
31 | func (s *SessionHttp) SetExpiration(expiration time.Duration) {
32 | s.expiration = expiration
33 | }
34 |
35 | // Set sets a key-value pair in the session.
36 | func (s *SessionHttp) Set(w http.ResponseWriter, key string, value string) error {
37 | cookie := &http.Cookie{
38 | Name: key,
39 | Value: value,
40 | Expires: time.Now().Add(s.expiration),
41 | Path: "/",
42 | }
43 | http.SetCookie(w, cookie)
44 | return nil
45 | }
46 |
47 | // Get retrieves a value from the session by key.
48 | func (s *SessionHttp) Get(r *http.Request, key string) (string, error) {
49 | cookie, err := r.Cookie(key)
50 | if err != nil {
51 | if err != http.ErrNoCookie {
52 | return "", nil // No cookie found, return empty string without error
53 | }
54 | return "", err // Other errors, return the error
55 | }
56 | return cookie.Value, nil
57 | }
58 |
59 | // Remove Session key
60 | func (s *SessionHttp) Remove(w http.ResponseWriter, key string) error {
61 | cookie := &http.Cookie{
62 | Name: key,
63 | Value: "",
64 | Expires: time.Unix(0, 0),
65 | Path: "/",
66 | MaxAge: -1,
67 | }
68 | http.SetCookie(w, cookie)
69 | return nil
70 | }
71 |
72 | // Destroy current session
73 | func (s *SessionHttp) Destroy(r *http.Request, w http.ResponseWriter) error {
74 | for _, cookie := range r.Cookies() {
75 | c := &http.Cookie{
76 | Name: cookie.Name,
77 | Value: "",
78 | Expires: time.Unix(0, 0),
79 | Path: "/",
80 | MaxAge: -1,
81 | }
82 | http.SetCookie(w, c)
83 | }
84 | return nil
85 | }
86 |
--------------------------------------------------------------------------------
/httputils/xml_response.go:
--------------------------------------------------------------------------------
1 | package httputils
2 |
3 | import (
4 | "encoding/xml"
5 | "fmt"
6 | "net/http"
7 |
8 | "github.com/ortizdavid/go-nopain/serialization"
9 | )
10 |
11 | // xmlResponse represents the structure of an XML response.
12 | type xmlResponse struct {
13 | XMLName xml.Name `xml:"response"` // XML root element name
14 | Message *string `xml:"message,omitempty"` // Message field for the response (optional)
15 | Status int `xml:"status"` // Status code of the response
16 | Count *int `xml:"count,omitempty"` // Count field for the response (optional)
17 | Data any `data:"data,omitempty"` // Data field for the response (optional)
18 | }
19 |
20 | // WriteXmlPaginated writes a paginated XML response to the provided http.ResponseWriter.
21 | // It includes the provided status code, paginated items, pagination metadata,
22 | // and handles potential errors during pagination or XML encoding.
23 | func WriteXmlPaginated[T any](w http.ResponseWriter, r *http.Request, items []T, count int64, currentPage int, limit int) {
24 | writeXmlHeader(w, 200)
25 | paginationXML, err := NewPaginationXML(r, items, count, currentPage, limit)
26 | if err != nil {
27 | WriteXmlError(w, err.Error(), http.StatusInternalServerError)
28 | return
29 | }
30 | if err := serialization.EncodeXml(w, paginationXML); err != nil {
31 | fmt.Fprintf(w, "%s", err.Error())
32 | }
33 | }
34 |
35 | // WriteXml writes a simple XML response with the provided status code and data.
36 | func WriteXml(w http.ResponseWriter, statusCode int, data any) {
37 | writeXmlHeader(w, statusCode)
38 | response := xmlResponse{
39 | Status: statusCode,
40 | Data: data,
41 | }
42 | encodeXml(w, response)
43 | }
44 |
45 | // WriteXmlError writes an XML error response with the provided message and status code.
46 | func WriteXmlError(w http.ResponseWriter, message string, statusCode int) {
47 | writeXmlHeader(w, statusCode)
48 | response := xmlResponse{
49 | Message: &message,
50 | Status: statusCode,
51 | }
52 | encodeXml(w, response)
53 | }
54 |
55 | // writeXmlHeader writes the XML content type header and sets the HTTP status code.
56 | func writeXmlHeader(w http.ResponseWriter, statusCode int) {
57 | w.Header().Set("Content-type", "application/xml")
58 | w.WriteHeader(statusCode)
59 | }
60 |
61 | // encodeXml encodes the xmlResponse struct to XML format and writes it to the response writer.
62 | func encodeXml(w http.ResponseWriter, response xmlResponse) {
63 | err := serialization.EncodeXml(w, response)
64 | if err != nil {
65 | fmt.Fprintf(w, "%s", err.Error())
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/httputils/xss_middleware.go:
--------------------------------------------------------------------------------
1 | package httputils
2 |
3 | import (
4 | "net/http"
5 | "time"
6 | )
7 |
8 | type XssMiddleware struct {
9 | tokenLength int
10 | expiration time.Duration
11 | cookieName string
12 | headerName string
13 | }
14 |
15 | func NewXssMiddleware() *XssMiddleware {
16 | return &XssMiddleware{}
17 | }
18 |
19 | func (csrf *XssMiddleware) Apply(next http.Handler) http.Handler {
20 | return nil
21 | }
--------------------------------------------------------------------------------
/logging/README.md:
--------------------------------------------------------------------------------
1 | # logging
2 |
3 | Package that handles logging
4 |
5 | ## Supported loggers
6 | - [x] log/slog package
--------------------------------------------------------------------------------
/logging/logger.go:
--------------------------------------------------------------------------------
1 | package logging
2 |
3 | import (
4 | "os"
5 | "log/slog"
6 | )
7 |
8 |
9 | // NewLogger creates a new logger with the specified folderName and fileName.
10 | // It returns a pointer to the created logger.
11 | func NewLogger(folderName string, fileName string) *slog.Logger {
12 | file, err := os.OpenFile(folderName+"/"+fileName, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
13 | if err != nil {
14 | panic(err)
15 | }
16 | defer file.Close()
17 | logger := slog.New(slog.NewJSONHandler(file, nil))
18 | return logger
19 | }
20 |
--------------------------------------------------------------------------------
/logging/logger_test.go:
--------------------------------------------------------------------------------
1 | package logging
2 |
3 | import (
4 | "io/ioutil"
5 | "os"
6 | "testing"
7 | )
8 |
9 | func Test_NewLogger(t *testing.T) {
10 | // Create a temporary directory for testing
11 | tmpDir, err := ioutil.TempDir("", "test-logging")
12 | if err != nil {
13 | t.Fatalf("Failed to create temporary directory: %v", err)
14 | }
15 | defer os.RemoveAll(tmpDir)
16 | logger := NewLogger(tmpDir, "test.log")
17 | if logger == nil {
18 | t.Error("Expected logger instance, got nil")
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/mailer/README.md:
--------------------------------------------------------------------------------
1 | # mailer
2 | Package that handles mail. Contains mail service
3 |
4 | ## Supported packages
5 | - [x] net/smtp package
--------------------------------------------------------------------------------
/mailer/email_service.go:
--------------------------------------------------------------------------------
1 | package mailer
2 |
3 | import (
4 | "fmt"
5 | "net/smtp"
6 | )
7 |
8 | // EmailService represents an email service configuration.
9 | type EmailService struct {
10 | Username string // Username for authentication with the SMTP server
11 | Password string // Password for authentication with the SMTP server
12 | SMTPHost string // SMTP server host address
13 | SMTPPort string // SMTP server port
14 | }
15 |
16 | // NewEmailService creates a new EmailService instance with the provided configuration.
17 | func NewEmailService(username string, password string, smtpHost string, smtpPort string) EmailService {
18 | return EmailService{
19 | Username: username,
20 | Password: password,
21 | SMTPHost: smtpHost,
22 | SMTPPort: smtpPort,
23 | }
24 | }
25 |
26 | // SendPlainEmail sends a plain text email with the provided subject and body to the specified recipient.
27 | func (es EmailService) SendPlainEmail(to, subject, body string) error {
28 | message := fmt.Sprintf("To: %s\r\nSubject: %s\r\n\r\n%s", to, subject, body)
29 | return es.sendEmail(to, message)
30 | }
31 |
32 | // SendHTMLEmail sends an HTML email with the provided subject and HTML body to the specified recipient.
33 | func (es EmailService) SendHTMLEmail(to, subject, bodyHTML string) error {
34 | message := fmt.Sprintf("To: %s\r\nSubject: %s\r\nContent-Type: text/html\r\n\r\n%s", to, subject, bodyHTML)
35 | return es.sendEmail(to, message)
36 | }
37 |
38 | // sendEmail sends an email with the specified message to the given recipient.
39 | func (es EmailService) sendEmail(to, message string) error {
40 | auth := smtp.PlainAuth("", es.Username, es.Password, es.SMTPHost)
41 | addr := fmt.Sprintf("%s:%s", es.SMTPHost, es.SMTPPort)
42 | err := smtp.SendMail(addr, auth, es.Username, []string{to}, []byte(message))
43 | if err != nil {
44 | return fmt.Errorf("error sending email: %v", err)
45 | }
46 | return nil
47 | }
48 |
--------------------------------------------------------------------------------
/mailer/email_service_test.go:
--------------------------------------------------------------------------------
1 | package mailer
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func Test_SendEmail(t *testing.T) {
8 | // Initialize EmailService with test SMTP server details
9 | es := NewEmailService("testuser", "testpassword", "test.smtp.com", "587")
10 |
11 | // Test valid email sending
12 | err := es.SendPlainEmail("recipient@example.com", "Test Subject", "Test Body")
13 | if err != nil {
14 | t.Errorf("Error sending email: %v", err)
15 | }
16 |
17 | // Test sending email to an invalid recipient
18 | err = es.SendPlainEmail("", "Test Subject", "Test Body")
19 | if err == nil {
20 | t.Error("Expected error for empty recipient address, got nil")
21 | }
22 |
23 | // Test sending email with an empty message body
24 | err = es.SendPlainEmail("recipient@example.com", "Test Subject", "")
25 | if err == nil {
26 | t.Error("Expected error for empty message body, got nil")
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/messages/README.md:
--------------------------------------------------------------------------------
1 | # messages
2 |
3 | Handles message formatting
4 |
5 | ## Features
6 | - [x] Coloring messages
7 | - [x] Message types
8 | - [x] Message handling (Fail, Log, Print)
--------------------------------------------------------------------------------
/messages/color.go:
--------------------------------------------------------------------------------
1 | package messages
2 |
3 | import (
4 | "fmt"
5 | )
6 |
7 | // ANSI color escape sequences for text colorization.
8 | const (
9 | COLOR_RESET = "\u001b[0m"
10 | COLOR_BLACK = "\u001b[30m"
11 | COLOR_RED = "\u001b[31m"
12 | COLOR_GREEN = "\u001b[32m"
13 | COLOR_YELLOW = "\u001b[33m"
14 | COLOR_BLUE = "\u001b[34m"
15 | COLOR_MAGENTA = "\u001b[35m" // Corrected typo in color name
16 | COLOR_CYAN = "\u001b[36m"
17 | COLOR_WHITE = "\u001b[37m"
18 | COLOR_BRIGHT_BLACK = "\u001b[30;1m"
19 | COLOR_BRIGHT_RED = "\u001b[31;1m"
20 | COLOR_BRIGHT_GREEN = "\u001b[32;1m"
21 | COLOR_BRIGHT_YELLOW = "\u001b[33;1m"
22 | COLOR_BRIGHT_BLUE = "\u001b[34;1m"
23 | COLOR_BRIGHT_MAGENTA = "\u001b[35;1m" // Corrected typo in color name
24 | COLOR_BRIGHT_CYAN = "\u001b[36;1m"
25 | COLOR_BRIGHT_WHITE = "\u001b[37;1m"
26 | )
27 |
28 | // Colorize prints the given text in the specified color.
29 | func Colorize(color string, text string) {
30 | fmt.Println(string(color), text, string(COLOR_RESET))
31 | }
32 |
33 | // Colorizef prints the formatted text in the specified color.
34 | func Colorizef(color string, formattedText string, values ...interface{}) {
35 | fmt.Printf(string(color), formattedText, values, string(COLOR_RESET))
36 | }
37 |
--------------------------------------------------------------------------------
/messages/color_test.go:
--------------------------------------------------------------------------------
1 | package messages
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | type colorTest struct {
8 | color string
9 | text string
10 | }
11 |
12 |
13 | func Test_PrintColorizeText(t *testing.T) {
14 | testCases := []colorTest {
15 | { color: COLOR_BLACK, text: "Black Text" },
16 | { color: COLOR_WHITE, text: "White Text" },
17 | { color: COLOR_CYAN, text: "Cyan Text" },
18 | { color: COLOR_YELLOW, text: "Yellow Text" },
19 | { color: COLOR_RED, text: "Red Text" },
20 | { color: COLOR_GREEN, text: "Green Text" },
21 | { color: COLOR_MAGENTA, text: "Mangenta Text" },
22 | { color: COLOR_BRIGHT_BLACK, text: "Bright Black Text" },
23 | { color: COLOR_BRIGHT_WHITE, text: "Bright White Text" },
24 | { color: COLOR_BRIGHT_CYAN, text: "Bright Cyan Text" },
25 | { color: COLOR_BRIGHT_YELLOW, text: "Bright Yellow Text" },
26 | { color: COLOR_BRIGHT_RED, text: "Bright Red Text" },
27 | { color: COLOR_GREEN, text: "Bright Green Text" },
28 | { color: COLOR_BRIGHT_MAGENTA, text: "Bright Mangenta Text" },
29 | }
30 | for _, test := range testCases {
31 | Colorize(test.color, test.text)
32 | }
33 | }
--------------------------------------------------------------------------------
/messages/message_handler.go:
--------------------------------------------------------------------------------
1 | package messages
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | )
7 |
8 | // PrintOnError prints the error message if it's not nil.
9 | func PrintOnError(err error) {
10 | if err != nil {
11 | fmt.Println(err)
12 | }
13 | }
14 |
15 | // LogFailOnError logs the error message and terminates the program if it's not nil.
16 | func LogFailOnError(err error) {
17 | if err != nil {
18 | log.Fatal(err)
19 | }
20 | }
21 |
22 | // PanicOnError panics with the error message if it's not nil.
23 | func PanicOnError(err error) {
24 | if err != nil {
25 | panic(err)
26 | }
27 | }
28 |
29 | // FailOnError logs the error message along with a custom message and terminates the program if the error is not nil.
30 | func FailOnError(err error, msg string) {
31 | if err != nil {
32 | log.Panicf("%s: %s", err, msg)
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/messages/message_handler_test.go:
--------------------------------------------------------------------------------
1 | package messages
2 |
3 | import (
4 | "errors"
5 | "testing"
6 | )
7 |
8 | type messageHandlerTest struct {
9 | err error
10 | }
11 |
12 | func Test_LogFailOnError(t *testing.T) {
13 | testCases := [] messageHandlerTest {
14 | { err: errors.New("Error Loading Application") },
15 | { err: errors.New("Application Crashed") },
16 | { err: errors.New("Error.. Please refresh application") },
17 | }
18 | for _, test := range testCases {
19 | LogFailOnError(test.err)
20 | }
21 | }
22 |
23 | func Test_PrintOnError(t *testing.T) {
24 | testCases := [] messageHandlerTest {
25 | { err: errors.New("Error Loading Application") },
26 | { err: errors.New("Your Application Crashed") },
27 | { err: errors.New("Error.. Please refresh application!") },
28 | { err: errors.New("Unknown Error") },
29 | }
30 | for _, test := range testCases {
31 | PrintOnError(test.err)
32 | }
33 | }
34 |
35 |
36 | func Test_PanicOnError(t *testing.T) {
37 | testCases := [] messageHandlerTest {
38 | { err: errors.New("Error Loading Application") },
39 | { err: errors.New("Your Application Crashed") },
40 | { err: errors.New("Error.. Please refresh application!") },
41 | { err: errors.New("Unknown Error") },
42 | }
43 | for _, test := range testCases {
44 | PanicOnError(test.err)
45 | }
46 | }
--------------------------------------------------------------------------------
/messages/message_type.go:
--------------------------------------------------------------------------------
1 | package messages
2 |
3 | // Error prints the given message in red color to indicate an error.
4 | func Error(message string) {
5 | Colorize(COLOR_RED, message)
6 | }
7 |
8 | // Info prints the given message in cyan color to provide informational messages.
9 | func Info(message string) {
10 | Colorize(COLOR_CYAN, message)
11 | }
12 |
13 | // Success prints the given message in green color to indicate successful operations.
14 | func Success(message string) {
15 | Colorize(COLOR_GREEN, message)
16 | }
17 |
18 | // Warning prints the given message in yellow color to indicate warnings.
19 | func Warning(message string) {
20 | Colorize(COLOR_YELLOW, message)
21 | }
--------------------------------------------------------------------------------
/messages/message_type_test.go:
--------------------------------------------------------------------------------
1 | package messages
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | type messageTypeTest struct {
8 | message string
9 | }
10 |
11 | func Test_ErrorMessage(t *testing.T) {
12 | testCases := [] messageTypeTest {
13 | { message: "Error While Loading Module" },
14 | { message: "Application Crashed" },
15 | { message: "Error.. Please refresh application" },
16 | }
17 | for _, test := range testCases {
18 | Error(test.message)
19 | }
20 | }
21 |
22 | func Test_WarningMessage(t *testing.T) {
23 | testCases := [] messageTypeTest {
24 | { message: "Please, Check your provider!" },
25 | { message: "Warning: unautorized" },
26 | { message: "Application will stay down in 15 mins" },
27 | }
28 | for _, test := range testCases {
29 | Warning(test.message)
30 | }
31 | }
32 |
33 | func Test_InfoMessage(t *testing.T) {
34 | testCases := [] messageTypeTest {
35 | { message: "This Application is starting!" },
36 | { message: "Welcome to Appgen" },
37 | { message: "This Admin profile!" },
38 | }
39 | for _, test := range testCases {
40 | Info(test.message)
41 | }
42 | }
43 |
44 | func Test_SuccessMessage(t *testing.T) {
45 | testCases := [] messageTypeTest {
46 | { message: "Project Created!" },
47 | { message: "User Logged in Successfully!" },
48 | { message: "Application Generated!" },
49 | }
50 | for _, test := range testCases {
51 | Success(test.message)
52 | }
53 | }
--------------------------------------------------------------------------------
/progress/README.md:
--------------------------------------------------------------------------------
1 | # progress
2 |
3 | Package for generate progress to operations
4 |
5 | ## Features
6 | - [x] Spinner
7 | - [ ] Loader
8 | - [ ] Progress Bar
9 | - [ ] Paralell Progress
10 | - [ ] Percentage Progress
--------------------------------------------------------------------------------
/progress/completion_percentage.go:
--------------------------------------------------------------------------------
1 | package progress
2 |
3 | import "time"
4 |
5 | func CompletionPercentage(duration time.Duration) {
6 |
7 | }
--------------------------------------------------------------------------------
/progress/loader.go:
--------------------------------------------------------------------------------
1 | package progress
2 |
3 | import "time"
4 |
5 | func Loader(delay time.Duration) {
6 | for {
7 |
8 | }
9 | }
--------------------------------------------------------------------------------
/progress/loader_test.go:
--------------------------------------------------------------------------------
1 | package progress
--------------------------------------------------------------------------------
/progress/paralell_progress.go:
--------------------------------------------------------------------------------
1 | package progress
2 |
3 | import "time"
4 |
5 | func ParalelProgress(duration time.Duration) {
6 |
7 | }
--------------------------------------------------------------------------------
/progress/percentage_progress.go:
--------------------------------------------------------------------------------
1 | package progress
2 |
3 | import "time"
4 |
5 | func PercentageProgress(duration time.Duration) {
6 |
7 | }
--------------------------------------------------------------------------------
/progress/progressbar.go:
--------------------------------------------------------------------------------
1 | package progress
2 |
3 | import "time"
4 |
5 | func ProgressBar(delay time.Duration) {
6 | for {
7 |
8 | }
9 | }
--------------------------------------------------------------------------------
/progress/spinner_test.go:
--------------------------------------------------------------------------------
1 | package progress
--------------------------------------------------------------------------------
/progress/sprinner.go:
--------------------------------------------------------------------------------
1 | package progress
2 |
3 | import (
4 | "fmt"
5 | "time"
6 | )
7 |
8 | // Spinner displays a spinning animation to indicate progress.
9 | func Spinner(delay time.Duration) {
10 | for {
11 | for _, r := range `-\|/` {
12 | fmt.Printf("\r%c", r) // Print the spinner character
13 | time.Sleep(delay)
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/pubsub/README.md:
--------------------------------------------------------------------------------
1 | # pubsub
2 |
3 | A package that simplifies interaction with message brokers.
4 | Supports **RabbitMQ, Kafka, and NATS** for publishing and subscribing to messages.
5 |
6 | ## Features
7 | - [x] **RabbitMQ**: Queue and Exchange configuration, Publish, Subscribe
8 | - [ ] **Kafka**: Topic-based messaging, High-throughput publishing
9 | - [x] **NATS**: Lightweight pub/sub, Streaming support
10 | - [x] Process messages efficiently with broker-specific optimizations
11 |
12 | ## Supported Brokers
13 | - **RabbitMQ**: Uses queues and exchanges for routing
14 | - **Kafka**: High-performance distributed messaging with topics and partitions
15 | - **NATS**: Lightweight messaging with subjects
--------------------------------------------------------------------------------
/pubsub/kafka/types.go:
--------------------------------------------------------------------------------
1 | package pubsub
--------------------------------------------------------------------------------
/pubsub/nats/publisher.go:
--------------------------------------------------------------------------------
1 | package pubsub
2 |
3 | import (
4 | "bytes"
5 | "compress/gzip"
6 | "encoding/json"
7 | "fmt"
8 | "time"
9 |
10 | "github.com/nats-io/nats.go"
11 | )
12 |
13 | // NatsPublisher represents a NATS publisher for sending messages
14 | type NatsPublisher struct {
15 | conn *nats.Conn
16 | }
17 |
18 | // NewNatsPublisher creates a new instance of NatsPublisher with the specified configuration
19 | func NewNatsPublisher(config NatsConfig) (*NatsPublisher, error) {
20 | opts := []nats.Option{
21 | nats.Timeout(config.Timeout),
22 | nats.ReconnectWait(config.ReconnectWait),
23 | nats.MaxReconnects(config.MaxReconnects),
24 | }
25 |
26 | if config.User != "" && config.Password != "" {
27 | opts = append(opts, nats.UserInfo(config.User, config.Password))
28 | }
29 |
30 | nc, err := nats.Connect(config.URL, opts...)
31 | if err != nil {
32 | return nil, err
33 | }
34 |
35 | return &NatsPublisher{conn: nc}, nil
36 | }
37 |
38 | // NewNatsPublisherDefault creates a NATS publisher with default configurations
39 | func NewNatsPublisherDefault() (*NatsPublisher, error) {
40 | config := NatsConfig{
41 | URL: "nats://localhost:4222",
42 | Timeout: 5 * time.Second,
43 | ReconnectWait: 2 * time.Second,
44 | MaxReconnects: 5,
45 | }
46 | return NewNatsPublisher(config)
47 | }
48 |
49 | // Publish sends a message to a specified subject in NATS
50 | func (pub *NatsPublisher) Publish(subject string, message interface{}) error {
51 | var buf bytes.Buffer
52 | gz := gzip.NewWriter(&buf)
53 | defer gz.Close()
54 |
55 | if err := json.NewEncoder(gz).Encode(message); err != nil {
56 | return fmt.Errorf("[!] error encoding message: %v", err)
57 | }
58 |
59 | err := pub.conn.Publish(subject, buf.Bytes())
60 | if err != nil {
61 | return fmt.Errorf("error publishing to '%s': %v", subject, err)
62 | }
63 | return nil
64 | }
65 |
66 | // Close terminates the connection with the NATS server
67 | func (pub *NatsPublisher) Close() {
68 | if pub.conn != nil {
69 | pub.conn.Close()
70 | }
71 | }
--------------------------------------------------------------------------------
/pubsub/nats/subscriber.go:
--------------------------------------------------------------------------------
1 | package pubsub
2 |
3 | import (
4 | "bytes"
5 | "compress/gzip"
6 | "encoding/json"
7 | "fmt"
8 | "time"
9 |
10 | "github.com/nats-io/nats.go"
11 | )
12 |
13 | // NatsSubscriber represents a NATS subscriber instance
14 | type NatsSubscriber struct {
15 | conn *nats.Conn
16 | }
17 |
18 | // NewNatsSubscriber creates a new NATS subscriber with the given configuration
19 | func NewNatsSubscriber(config NatsConfig) (*NatsSubscriber, error) {
20 | opts := []nats.Option{
21 | nats.Timeout(config.Timeout),
22 | nats.MaxReconnects(config.MaxReconnects),
23 | nats.MaxReconnects(config.MaxReconnects),
24 | }
25 |
26 | if config.User != "" && config.Password != "" {
27 | opts = append(opts, nats.UserInfo(config.User, config.Password))
28 | }
29 |
30 | nc, err := nats.Connect(config.URL, opts...)
31 | if err != nil {
32 | return nil, err
33 | }
34 |
35 | return &NatsSubscriber{conn: nc}, nil
36 | }
37 |
38 | // NewNatsSubscriberDefault creates a subscriber with default configuration
39 | func NewNatsSubscriberDefault() (*NatsSubscriber, error) {
40 | config := NatsConfig{
41 | URL: "nats://localhost:4222",
42 | Timeout: 5 * time.Second,
43 | ReconnectWait: 2 * time.Second,
44 | MaxReconnects: 5,
45 | }
46 | return NewNatsSubscriber(config)
47 | }
48 |
49 | // Subscribe listens to messages on a given subject
50 | func (sub *NatsSubscriber) Subscribe(subject string) error {
51 | fmt.Printf("Awaiting messages on subject '%s ...\n", subject)
52 | _, err := sub.conn.Subscribe(subject, func(msg *nats.Msg) {
53 | fmt.Printf("[x] Received message on '%s': %s\n", subject, string(msg.Data))
54 | })
55 | return err
56 | }
57 |
58 | // SubscribeQueue listens to messages on a subject using a queue group
59 | func (sub *NatsSubscriber) SubscribeQueue(subject string, queue string) error {
60 | fmt.Printf("Awaiting messages on subject '%s, queue '%s' ...\n", subject, queue)
61 | _, err := sub.conn.QueueSubscribe(subject, queue, func(msg *nats.Msg) {
62 | fmt.Printf("[x] Received message on subject '%s', queue '%s': %s\n", subject, queue, string(msg.Data))
63 | })
64 | return err
65 | }
66 |
67 | // ProcessMessage processes incoming messages and applies a handler function
68 | func ProcessMessage[T any](subscriber *NatsSubscriber, subject string, fn func(T) error) error {
69 | _, err := subscriber.conn.Subscribe(subject, func(msg *nats.Msg) {
70 | reader, err := gzip.NewReader(bytes.NewReader(msg.Data))
71 | if err != nil {
72 | fmt.Printf("[!] Error decompressing message on '%s': %v\n", subject, err)
73 | return
74 | }
75 | defer reader.Close()
76 |
77 | var message T
78 | if err := json.NewDecoder(reader).Decode(&message); err != nil {
79 | fmt.Printf("[!] Error decoding message on '%s': %v", subject, err)
80 | return
81 | }
82 | err = fn(message)
83 | if err != nil {
84 | fmt.Printf("[!] Error processing message on '%s': %v", subject, err)
85 | }
86 | })
87 | return err
88 | }
89 |
90 | // ProcessMessageQueue processes messages from a queue and applies a handler function
91 | func ProcessMessageQueue[T any](subscriber *NatsSubscriber, subject string, queue string, fn func(T) error) error {
92 | _, err := subscriber.conn.QueueSubscribe(subject, queue, func(msg *nats.Msg) {
93 | reader, err := gzip.NewReader(bytes.NewReader(msg.Data))
94 | if err != nil {
95 | fmt.Printf("[!] Error decompressing message on '%s': %v\n", subject, err)
96 | return
97 | }
98 | defer reader.Close()
99 |
100 | var message T
101 | if err := json.NewDecoder(reader).Decode(&message); err != nil {
102 | fmt.Printf("[!] Error decoding message on '%s': %v", subject, err)
103 | return
104 | }
105 | err = fn(message)
106 | if err != nil {
107 | fmt.Printf("[!] Error processing message on '%s': %v", subject, err)
108 | }
109 | })
110 | return err
111 | }
112 |
113 | // Close terminates the NATS subscriber connection
114 | func (pub *NatsSubscriber) Close() {
115 | if pub.conn != nil {
116 | pub.conn.Close()
117 | }
118 | }
--------------------------------------------------------------------------------
/pubsub/nats/types.go:
--------------------------------------------------------------------------------
1 | package pubsub
2 |
3 | import "time"
4 |
5 | // NatsConfig defines the configuration settings for connecting to a NATS server
6 | type NatsConfig struct {
7 | URL string
8 | User string
9 | Password string
10 | Timeout time.Duration
11 | ReconnectWait time.Duration
12 | MaxReconnects int
13 | }
14 |
--------------------------------------------------------------------------------
/pubsub/rabbitmq/producer.go:
--------------------------------------------------------------------------------
1 | package pubsub
2 |
3 | import (
4 | "bytes"
5 | "compress/gzip"
6 | "context"
7 | "encoding/json"
8 | "fmt"
9 | "time"
10 |
11 | amqp "github.com/rabbitmq/amqp091-go"
12 | )
13 |
14 | // RabbitMQProducer represents the producer configuration.
15 | type RabbitMQProducer struct {
16 | config RabbitMQConfig
17 | connection *amqp.Connection
18 | channel *amqp.Channel
19 | }
20 |
21 | // NewRabbitMQProducer creates a new RabbitMQProducer instance with custom server configuration.
22 | func NewRabbitMQProducer(config RabbitMQConfig) (*RabbitMQProducer, error) {
23 | err := validateConfig(config)
24 | if err != nil {
25 | return nil, err
26 | }
27 | // Connection
28 | conn, err := amqp.Dial(connectionString(config))
29 | if err != nil {
30 | return nil, fmt.Errorf("failed to connect to : %w", err)
31 | }
32 | // Open channel
33 | ch, err := conn.Channel()
34 | if err != nil {
35 | return nil, fmt.Errorf("failed to open a channel: %w", err)
36 | }
37 | return &RabbitMQProducer{
38 | config: config,
39 | connection: conn,
40 | channel: ch,
41 | }, nil
42 | }
43 |
44 | // NewRabbitMQProducerDefault creates a new RabbitMQProducer instance with default server configuration.
45 | func NewRabbitMQProducerDefault() (*RabbitMQProducer, error) {
46 | return NewRabbitMQProducer(DefaultRabbitMQConfig)
47 | }
48 |
49 | // Close connection and Channel
50 | func (producer *RabbitMQProducer) Close() {
51 | if producer.connection != nil {
52 | producer.connection.Close()
53 | }
54 | if producer.channel != nil {
55 | producer.channel.Close()
56 | }
57 | }
58 |
59 | // PublishToQueue publishes a message to a queue.
60 | func (producer *RabbitMQProducer) PublishToQueue(queue Queue, objMessage interface{}) error {
61 | ch := producer.channel
62 | // Declare queue
63 | q, err := declareQueue(ch, queue)
64 | if err != nil {
65 | return err
66 | }
67 | // Encode message to JSON
68 | var buf bytes.Buffer
69 | gz := gzip.NewWriter(&buf)
70 | defer gz.Close()
71 |
72 | if err := json.NewEncoder(gz).Encode(objMessage); err != nil {
73 | return fmt.Errorf("[!] failed to encode message to JSON: %w", err)
74 | }
75 | // Create context
76 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
77 | defer cancel()
78 | // Publish message
79 | err = ch.PublishWithContext(ctx,
80 | "",
81 | q.Name,
82 | false,
83 | false,
84 | amqp.Publishing{
85 | ContentType: "application/json",
86 | Body: buf.Bytes(),
87 | })
88 | if err != nil {
89 | return fmt.Errorf("[!] failed to publish message: %w", err)
90 | }
91 |
92 | return nil
93 | }
94 |
95 | // PublishToExchange publishes a message to a exchange.
96 | func (producer *RabbitMQProducer) PublishToExchange(exchange Exchange, routingKey string, objMessage interface{}) error {
97 | ch := producer.channel
98 | // Declare exchange
99 | err := declareExchange(ch, exchange)
100 | if err != nil {
101 | return err
102 | }
103 | // Encode message to JSON
104 | var buf bytes.Buffer
105 | gz := gzip.NewWriter(&buf)
106 | defer gz.Close()
107 |
108 | if err := json.NewEncoder(gz).Encode(objMessage); err != nil {
109 | return fmt.Errorf("[!] failed to encode message to JSON: %w", err)
110 | }
111 | // Create Context
112 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
113 | defer cancel()
114 | // Determine routing key
115 | var key string
116 | if exchange.ExType == ExchangeFanout {
117 | key = ""
118 | } else {
119 | key = routingKey
120 | }
121 | // Publish message to exchange
122 | err = ch.PublishWithContext(ctx,
123 | exchange.Name,
124 | key,
125 | false,
126 | false,
127 | amqp.Publishing{
128 | ContentType: "application/json",
129 | Body: buf.Bytes(),
130 | })
131 | if err != nil {
132 | return fmt.Errorf("[!] failed to publish message to exchange: %w", err)
133 | }
134 |
135 | return nil
136 | }
137 |
--------------------------------------------------------------------------------
/pubsub/rabbitmq/test.go:
--------------------------------------------------------------------------------
1 | package pubsub
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | type GolangMessage struct {
8 | Text string `json:"text"`
9 | Number int `json:"number"`
10 | Boolean bool `json:"boolean"`
11 | }
12 |
13 | func TestPublishToQueueWithDefault(t *testing.T) {
14 | rmq, _ := NewRabbitMQProducerDefault()
15 |
16 | message := GolangMessage{
17 | Text: "Message with Default",
18 | Number: 1097,
19 | Boolean: true,
20 | }
21 |
22 | if err := rmq.PublishToQueue(DefaultQueue("golang_queue"), message); err != nil {
23 | t.Error(err)
24 | }
25 | }
26 |
27 | func TestPublishToExchangeDefault(t *testing.T) {
28 | rmq, _ := NewRabbitMQProducerDefault()
29 |
30 | message := GolangMessage{
31 | Text: "Message to exchange",
32 | Number: 99,
33 | Boolean: false,
34 | }
35 |
36 | if err := rmq.PublishToExchange(DefaultExchange("golang_exchange"), "golang_key", message); err != nil {
37 | t.Error(err)
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/pubsub/rabbitmq/types.go:
--------------------------------------------------------------------------------
1 | package pubsub
2 |
3 | // RabbitMQConfig contains settings for connecting to the server.
4 | type RabbitMQConfig struct {
5 | Host string
6 | Port int
7 | User string
8 | Password string
9 | VirtualHost string
10 | }
11 |
12 | // Exchange represents the configuration of a exchange.
13 | type Exchange struct {
14 | Name string
15 | ExType ExchangeType
16 | Durable bool
17 | AutoDelete bool
18 | Internal bool
19 | NoWait bool
20 | Arguments map[string]interface{}
21 | }
22 |
23 | // Queue contains settings for configuring queues.
24 | type Queue struct {
25 | Name string
26 | Durable bool
27 | Exclusive bool
28 | AutoDelete bool
29 | NoWait bool
30 | Arguments map[string]interface{}
31 | }
32 |
33 | // Bind contains settings for configuring bindings.
34 | type Bind struct {
35 | Queue string
36 | Exchange string
37 | RoutingKey string
38 | NoWait bool
39 | Arguments map[string]interface{}
40 | }
41 |
42 | // Type of exchanges
43 | type ExchangeType string
44 |
45 | // Constants defining various types of exchanges.
46 | const (
47 | ExchangeFanout ExchangeType = "fanout"
48 | ExchangeDirect ExchangeType = "direct"
49 | ExchangeTopic ExchangeType = "topic"
50 | ExchangeHeaders ExchangeType = "headers"
51 | )
52 |
53 | // Default config
54 | var DefaultRabbitMQConfig = RabbitMQConfig{
55 | Host: "localhost",
56 | Port: 5672,
57 | User: "guest",
58 | Password: "guest",
59 | VirtualHost: "/",
60 | }
61 |
62 | // Default queue
63 | var DefaultQueue = func(name string) Queue{
64 | return Queue{
65 | Name: name,
66 | Durable: false,
67 | Exclusive: false,
68 | AutoDelete: false,
69 | NoWait: false,
70 | Arguments: nil,
71 | }
72 | }
73 |
74 | // Default exchange
75 | var DefaultExchange = func(name string) Exchange{
76 | return Exchange{
77 | Name: name,
78 | ExType: ExchangeDirect,
79 | Durable: false,
80 | AutoDelete: false,
81 | Internal: false,
82 | NoWait: false,
83 | Arguments: nil,
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/random/README.md:
--------------------------------------------------------------------------------
1 | # random
2 |
3 | Package that generates random values
4 |
5 | ## Features
6 | - [x] Number
7 | - [x] String
--------------------------------------------------------------------------------
/random/number.go:
--------------------------------------------------------------------------------
1 | package random
2 |
3 | import (
4 | "math/rand"
5 | )
6 |
7 |
8 | // Int generates a random integer between min (inclusive) and max (exclusive).
9 | func Int(min, max int) int {
10 | if min >= max {
11 | panic("Invalid range. min should be less than max.")
12 | }
13 | seedRandom()
14 | return rand.Intn(max-min) + min
15 | }
16 |
17 | // Int32 generates a random 32-bit integer between min (inclusive) and max (exclusive).
18 | func Int32(min, max int32) int32 {
19 | if min >= max {
20 | panic("Invalid range. min should be less than max.")
21 | }
22 | seedRandom()
23 | return rand.Int31n(max-min) + min
24 | }
25 |
26 | // Int64 generates a random 64-bit integer between min (inclusive) and max (exclusive).
27 | func Int64(min, max int64) int64 {
28 | if min >= max {
29 | panic("Invalid range. min should be less than max.")
30 | }
31 | seedRandom()
32 | return rand.Int63n(max-min) + min
33 | }
34 |
35 | // Float32 generates a random float32 between min (inclusive) and max (exclusive).
36 | func Float32(min, max float32) float32 {
37 | if min >= max {
38 | panic("Invalid range. min should be less than max.")
39 | }
40 | seedRandom()
41 | return min + rand.Float32()*(max-min)
42 | }
43 |
44 | // Float64 generates a random float64 between min (inclusive) and max (exclusive).
45 | func Float64(min, max float64) float64 {
46 | if min >= max {
47 | panic("Invalid range. min should be less than max.")
48 | }
49 | seedRandom()
50 | return min + rand.Float64()*(max-min)
51 | }
52 |
--------------------------------------------------------------------------------
/random/number_test.go:
--------------------------------------------------------------------------------
1 | package random
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func TestInt(t *testing.T) {
8 | min, max := 0, 10
9 | result := Int(min, max)
10 | if result < min || result >= max {
11 | t.Errorf("Int(%d, %d) returned %d, which is out of range", min, max, result)
12 | }
13 | }
14 |
15 | func TestInt32(t *testing.T) {
16 | min, max := int32(0), int32(10)
17 | result := Int32(min, max)
18 | if result < min || result >= max {
19 | t.Errorf("Int32(%d, %d) returned %d, which is out of range", min, max, result)
20 | }
21 | }
22 |
23 | func TestInt64(t *testing.T) {
24 | min, max := int64(0), int64(10)
25 | result := Int64(min, max)
26 | if result < min || result >= max {
27 | t.Errorf("Int64(%d, %d) returned %d, which is out of range", min, max, result)
28 | }
29 | }
30 |
31 | func TestFloat32(t *testing.T) {
32 | min, max := float32(0), float32(10)
33 | result := Float32(min, max)
34 | if result < min || result >= max {
35 | t.Errorf("Float32(%f, %f) returned %f, which is out of range", min, max, result)
36 | }
37 | }
38 |
39 | func TestFloat64(t *testing.T) {
40 | min, max := float64(0), float64(10)
41 | result := Float64(min, max)
42 | if result < min || result >= max {
43 | t.Errorf("Float64(%f, %f) returned %f, which is out of range", min, max, result)
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/random/random.go:
--------------------------------------------------------------------------------
1 | package random
2 |
3 | import (
4 | "math/rand"
5 | "time"
6 | )
7 |
8 |
9 | // seedRandom seeds the random number generator with the current time.
10 | func seedRandom() {
11 | rand.Seed(time.Now().UnixNano())
12 | }
13 |
--------------------------------------------------------------------------------
/random/string.go:
--------------------------------------------------------------------------------
1 | package random
2 |
3 | import (
4 | "math/rand"
5 | )
6 |
7 | // charset defines the characters used for generating random strings.
8 | const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
9 |
10 | // String generates a random string of the specified length using characters from the charset.
11 | func String(length int) string {
12 | seedRandom()
13 | if length <= 0 {
14 | panic("Invalid string length. Length should be greater than zero.")
15 | }
16 | randomString := make([]byte, length)
17 | for i := 0; i < length; i++ {
18 | randomString[i] = charset[rand.Intn(len(charset))]
19 | }
20 | return string(randomString)
21 | }
22 |
--------------------------------------------------------------------------------
/random/string_test.go:
--------------------------------------------------------------------------------
1 | package random
2 |
3 | import (
4 | "testing"
5 | )
6 |
7 | func TestString(t *testing.T) {
8 | // Test case for positive length
9 | length := 10
10 | result := String(length)
11 | if len(result) != length {
12 | t.Errorf("String(%d) returned a string of length %d, expected %d", length, len(result), length)
13 | }
14 | // Test case for zero length
15 | length = 0
16 | defer func() {
17 | if r := recover(); r == nil {
18 | t.Errorf("String(%d) did not panic as expected", length)
19 | }
20 | }()
21 | String(length)
22 | // Test case for negative length
23 | length = -5
24 | defer func() {
25 | if r := recover(); r == nil {
26 | t.Errorf("String(%d) did not panic as expected", length)
27 | }
28 | }()
29 | String(length)
30 | }
31 |
--------------------------------------------------------------------------------
/reflection/README.md:
--------------------------------------------------------------------------------
1 | # reflection
2 |
3 | Package for refection handling
4 |
5 | ## Features
6 | - [x] Struct info (Methods, fields, ...)
7 | - [x] Inspection
8 | - [x] Var dump
9 | - [x] Tags
10 | - [x] Name of (Function, Interface and Struct)
--------------------------------------------------------------------------------
/reflection/inspection.go:
--------------------------------------------------------------------------------
1 | package reflection
2 |
3 | import (
4 | "fmt"
5 | "reflect"
6 | )
7 |
8 | // InspectStruct prints information about the given object, including its kind, type, name, attributes, and public methods.
9 | func InspectStruct(obj interface{}) {
10 | fmt.Println("Name: ", NameOfType(obj))
11 | fmt.Println("Kind: ", reflect.TypeOf(obj).Kind())
12 | fmt.Println("Type: ", reflect.TypeOf(obj))
13 | fmt.Println("Type Name: ", reflect.TypeOf(obj).Name())
14 | fmt.Println("Attributes: ", GetFields(obj))
15 | fmt.Println("Public Methods: ", GetPublicMethods(obj))
16 | fmt.Println()
17 | }
18 |
19 | // InspectStructFields prints information about the fields of the given object, including their names, values, and kinds.
20 | func InspectStructFields(obj interface{}) {
21 | objType := reflect.TypeOf(obj)
22 | objVal := reflect.ValueOf(obj)
23 | numFields := objType.NumField()
24 | fmt.Println("Number of fields: ", numFields)
25 | fmt.Println("Field\tValue\tKind")
26 | for i := 0; i < numFields; i++ {
27 | fmt.Printf("%s\t%#v\t%v\n", objType.Field(i).Name, objVal.Field(i), objVal.Field(i).Kind())
28 | }
29 | }
30 |
31 | // CountTypes counts the occurrences of different types among the provided elements.
32 | func CountTypes(elements ...interface{}) {
33 | var (
34 | countFloats int
35 | countInts int
36 | countStrings int
37 | countBools int
38 | countMaps int
39 | countStructs int
40 | )
41 |
42 | for _, elem := range elements {
43 | switch elem.(type) {
44 | case float32:
45 | case float64:
46 | countFloats++
47 | case int:
48 | case int16:
49 | case int32:
50 | case int64:
51 | countInts++
52 | case string:
53 | countStrings++
54 | case bool:
55 | countBools++
56 | case map[string]interface{}:
57 | countMaps++
58 | case struct{}:
59 | countStructs++
60 | }
61 | }
62 | fmt.Println("Integers: ", countInts)
63 | fmt.Println("Floats: ", countFloats)
64 | fmt.Println("Strings: ", countStrings)
65 | fmt.Println("Booleans: ", countBools)
66 | fmt.Println("Maps: ", countMaps)
67 | fmt.Println("Structs: ", countStructs)
68 | }
69 |
--------------------------------------------------------------------------------
/reflection/inspection_test.go:
--------------------------------------------------------------------------------
1 | package reflection
2 |
3 | import "testing"
4 |
5 |
6 | type structType struct {
7 | id int
8 | name string
9 | height float32
10 | }
11 |
12 | type mapType map[string]interface{}
13 |
14 |
15 | func TestVarDump(t *testing.T) {
16 |
17 | sliceStr := []string{"A", "B", "c"}
18 | structObj := structType {
19 | id: 1,
20 | name: "John",
21 | height: 12.8,
22 | }
23 | mapObj := mapType{
24 | "name": "Anna",
25 | "Age": 12,
26 | }
27 | sliceStruct := []structType {
28 | {id: 2, name: "Cl1", height: 12.3},
29 | {id: 3, name: "Cl2", height: 19.3},
30 | }
31 |
32 | VarDump(sliceStr)
33 | VarDump(structObj)
34 | VarDump(mapObj)
35 | VarDump(sliceStruct)
36 | }
--------------------------------------------------------------------------------
/reflection/name_of.go:
--------------------------------------------------------------------------------
1 | package reflection
2 |
3 | import (
4 | "reflect"
5 | "runtime"
6 | "strings"
7 | )
8 |
9 | // Name of Function -> includes package
10 | func NameOfFunc(fn interface{}) string {
11 | return runtime.FuncForPC(reflect.ValueOf(fn).Pointer()).Name()
12 | }
13 |
14 | // Short Name of Function -> only name
15 | func NameOfFuncShort(fn interface{}) string {
16 | parts := strings.Split(NameOfFunc(fn), ".")
17 | return parts[len(parts) - 1]
18 | }
19 |
20 | // Name of Interface or Struct
21 | func NameOfType(v interface{}) string {
22 | return reflect.TypeOf(v).Name()
23 | }
24 |
--------------------------------------------------------------------------------
/reflection/reflection.go:
--------------------------------------------------------------------------------
1 | package reflection
2 |
3 | import (
4 | "reflect"
5 | )
6 |
7 |
8 | func GetPublicMethods(obj interface{}) []string {
9 | objType := reflect.TypeOf(obj)
10 | var methods []string
11 | for i := 0; i < objType.NumMethod(); i++ {
12 | methods = append(methods, objType.Method(i).Name)
13 | }
14 | return methods
15 | }
16 |
17 | func GetFields(obj interface{}) []string {
18 | objType := reflect.TypeOf(obj)
19 | if objType.Kind() == reflect.Ptr {
20 | objType = objType.Elem()
21 | }
22 | var fields []string
23 | for i := 0; i < objType.NumField(); i++ {
24 | fields = append(fields, objType.Field(i).Name)
25 | }
26 | return fields
27 | }
28 |
29 | func ExistsField(obj interface{}, fieldName string) bool {
30 | field := reflect.ValueOf(obj).FieldByName(fieldName)
31 | return field.IsValid()
32 | }
33 |
34 | func ExistsMethod(obj interface{}, methodName string) bool {
35 | method := reflect.ValueOf(obj).MethodByName(methodName)
36 | return method.IsValid()
37 | }
38 |
39 | func CallMethod(any interface{}, methodName string, args... interface{}) {
40 | inputs := make([]reflect.Value, len(args))
41 | for i := range args {
42 | inputs[i] = reflect.ValueOf(args[i])
43 | }
44 | reflect.ValueOf(any).MethodByName(methodName).Call(inputs)
45 | }
46 |
--------------------------------------------------------------------------------
/reflection/reflection_test.go:
--------------------------------------------------------------------------------
1 | package reflection
2 |
3 |
--------------------------------------------------------------------------------
/reflection/tags.go:
--------------------------------------------------------------------------------
1 | package reflection
2 |
3 | import "reflect"
4 |
5 | // GetTags returns a map with field names and associated tags
6 | func GetTags(obj interface{}, tagKey string) map[string]string {
7 | objType := reflect.TypeOf(obj)
8 | if objType.Kind() == reflect.Ptr {
9 | objType = objType.Elem()
10 | }
11 |
12 | tags := make(map[string]string)
13 | for i := 0; i < objType.NumField(); i++ {
14 | field := objType.Field(i)
15 | tags[field.Name] = field.Tag.Get(tagKey)
16 | }
17 | return tags
18 | }
19 |
20 | // HasTag verify if a field has a tag
21 | func HasTag(obj interface{}, fieldName string, tagKey string) bool {
22 | objType := reflect.TypeOf(obj)
23 | if objType.Kind() == reflect.Ptr {
24 | objType = objType.Elem()
25 | }
26 |
27 | field, found := objType.FieldByName(fieldName)
28 | if !found {
29 | return false
30 | }
31 |
32 | tag := field.Tag.Get(tagKey)
33 | return tag != ""
34 | }
35 |
36 | // GetTag retrieves the value of a specific tag key for a given field
37 | func GetTag(obj interface{}, fieldName string, tagKey string) string {
38 | objType := reflect.TypeOf(obj)
39 | if objType.Kind() == reflect.Ptr {
40 | objType = objType.Elem()
41 | }
42 |
43 | if objType.Kind() != reflect.Struct {
44 | return ""
45 | }
46 |
47 | field, found := objType.FieldByName(fieldName)
48 | if !found {
49 | return ""
50 | }
51 |
52 | return field.Tag.Get(tagKey)
53 | }
54 |
55 |
56 | // GetFieldsTag returns names of fields that contains a given tag
57 | func GetFieldsWithTag(obj interface{}, tagKey string, tagValue string) []string {
58 | objType := reflect.TypeOf(obj)
59 | if objType.Kind() == reflect.Ptr {
60 | objType = objType.Elem()
61 | }
62 |
63 | var fields []string
64 | for i := 0; i < objType.NumField(); i++ {
65 | field := objType.Field(i)
66 | if field.Tag.Get(tagKey) == tagValue {
67 | fields = append(fields, field.Name)
68 | }
69 | }
70 | return fields
71 | }
--------------------------------------------------------------------------------
/reflection/var_dump.go:
--------------------------------------------------------------------------------
1 | package reflection
2 |
3 | import (
4 | "fmt"
5 | "reflect"
6 | )
7 |
8 | // VarDump prints the type and value of multiple variables.
9 | func VarDump(variables ...interface{}) {
10 | for _, variable := range variables {
11 |
12 | if variable == nil {
13 | fmt.Printf("Type: \nValue: \n\n")
14 | continue
15 | }
16 |
17 | v := reflect.ValueOf(variable)
18 | t := reflect.TypeOf(variable)
19 |
20 | fmt.Printf("Type: %s\n", t)
21 |
22 | switch v.Kind() {
23 | case reflect.Struct:
24 | fmt.Println("Value:")
25 | for i := 0; i < v.NumField(); i++ {
26 | fmt.Printf(" %s: %v\n", t.Field(i).Name, v.Field(i).Interface())
27 | }
28 |
29 | case reflect.Slice, reflect.Array:
30 | fmt.Println("Value: [")
31 | for i := 0; i < v.Len(); i++ {
32 | fmt.Printf(" %v\n", v.Index(i).Interface())
33 | }
34 | fmt.Println("]")
35 |
36 | case reflect.Map:
37 | fmt.Println("Value: {")
38 | for _, key := range v.MapKeys() {
39 | fmt.Printf(" %v: %v\n", key.Interface(), v.MapIndex(key).Interface())
40 | }
41 | fmt.Println("}")
42 |
43 | default:
44 | fmt.Printf("Value: %v\n", v.Interface())
45 | }
46 |
47 | fmt.Println()
48 | }
49 | }
50 |
51 | // VarDumpBasic prints the type and value of multiple variables in a simple format.
52 | func VarDumpBasic(variables ...interface{}) {
53 | for _, variable := range variables {
54 | if variable == nil {
55 | fmt.Printf("Type: \nValue: \n")
56 | continue
57 | }
58 |
59 | t := reflect.TypeOf(variable)
60 | v := reflect.ValueOf(variable)
61 |
62 | fmt.Printf("Type: %s\nValue: %#v\n\n", t, v)
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/serialization/README.md:
--------------------------------------------------------------------------------
1 | # serialization
2 |
3 | Package for data serialization
4 |
5 |
6 | ## Features
7 | - [x] JSON
8 | - [x] XML
9 | - [x] ASN1
10 | - [x] Serialization from file
11 | - [x] Data Transformation
--------------------------------------------------------------------------------
/serialization/asn1.go:
--------------------------------------------------------------------------------
1 | package serialization
2 |
3 | import (
4 | "encoding/asn1"
5 | "io"
6 | )
7 |
8 |
9 | // SerializeAsn1 serializes the given object to ASN.1 format.
10 | func SerializeAsn1(obj interface{}) ([]byte, error) {
11 | asn1Data, err := asn1.Marshal(obj)
12 | if err != nil {
13 | return nil, err
14 | }
15 | return asn1Data, nil
16 | }
17 |
18 |
19 | // UnserializeAsn1 deserializes ASN.1 data into the given data structure.
20 | func UnserializeAsn1(asn1Data []byte, obj interface{}) error {
21 | _, err := asn1.Unmarshal(asn1Data, obj)
22 | if err != nil {
23 | return err
24 | }
25 | return nil
26 | }
27 |
28 | // EncodeAsn1 encodes the given object as ASN.1 and writes it to the writer.
29 | func EncodeAsn1(writer io.Writer, obj interface{}) error {
30 | asn1Data, err := asn1.Marshal(obj)
31 | if err != nil {
32 | return err
33 | }
34 | _, err = writer.Write(asn1Data)
35 | return err
36 | }
37 |
38 | // DecodeAsn1 decodes ASN.1 from the reader into the given data structure.
39 | func DecodeAsn1(reader io.Reader, v interface{}) error {
40 | asn1Data, err := io.ReadAll(reader)
41 | if err != nil {
42 | return err
43 | }
44 | _, err = asn1.Unmarshal(asn1Data, v)
45 | return err
46 | }
47 |
--------------------------------------------------------------------------------
/serialization/asn1_test.go:
--------------------------------------------------------------------------------
1 | package serialization
2 |
3 | import (
4 | "bytes"
5 | "reflect"
6 | "testing"
7 | )
8 |
9 | type testAsn1Struct struct {
10 | Name string
11 | Age int
12 | Email string
13 | }
14 |
15 | func Test_SerializeAsn1(t *testing.T) {
16 |
17 | obj := testAsn1Struct{Name: "John", Age: 30, Email: "john@example.com"}
18 |
19 | asn1Data, err := SerializeAsn1(obj)
20 | if err != nil {
21 | t.Errorf("SerializeAsn1() error = %v", err)
22 | return
23 | }
24 |
25 | var newObj testAsn1Struct
26 | err = UnserializeAsn1(asn1Data, &newObj)
27 | if err != nil {
28 | t.Errorf("UnserializeAsn1() error = %v", err)
29 | return
30 | }
31 |
32 | if !reflect.DeepEqual(obj, newObj) {
33 | t.Errorf("Deserialized object is not equal to the original object")
34 | }
35 | }
36 |
37 | func Test_UnserializeAsn1(t *testing.T) {
38 |
39 | obj := testAsn1Struct{Name: "John", Age: 30, Email: "john@example.com"}
40 | asn1Data, err := SerializeAsn1(obj)
41 | if err != nil {
42 | t.Errorf("SerializeAsn1() error = %v", err)
43 | return
44 | }
45 |
46 | var newObj testAsn1Struct
47 | err = UnserializeAsn1(asn1Data, &newObj)
48 | if err != nil {
49 | t.Errorf("UnserializeAsn1() error = %v", err)
50 | return
51 | }
52 |
53 | if !reflect.DeepEqual(obj, newObj) {
54 | t.Errorf("Deserialized object is not equal to the original object")
55 | }
56 | }
57 |
58 | func Test_EncodeAsn1(t *testing.T) {
59 |
60 | obj := testAsn1Struct{Name: "John", Age: 30, Email: "john@example.com"}
61 |
62 | var buf bytes.Buffer
63 | err := EncodeAsn1(&buf, obj)
64 | if err != nil {
65 | t.Errorf("EncodeAsn1() error = %v", err)
66 | return
67 | }
68 |
69 | var newObj testAsn1Struct
70 | err = DecodeAsn1(&buf, &newObj)
71 | if err != nil {
72 | t.Errorf("DecodeAsn1() error = %v", err)
73 | return
74 | }
75 |
76 | if !reflect.DeepEqual(obj, newObj) {
77 | t.Errorf("Decoded object is not equal to the original object")
78 | }
79 | }
80 |
81 | func Test_DecodeAsn1(t *testing.T) {
82 |
83 | obj := testAsn1Struct{Name: "John", Age: 30, Email: "john@example.com"}
84 | var buf bytes.Buffer
85 | err := EncodeAsn1(&buf, obj)
86 | if err != nil {
87 | t.Errorf("EncodeAsn1() error = %v", err)
88 | return
89 | }
90 |
91 | var newObj testAsn1Struct
92 | err = DecodeAsn1(&buf, &newObj)
93 | if err != nil {
94 | t.Errorf("DecodeAsn1() error = %v", err)
95 | return
96 | }
97 |
98 | if !reflect.DeepEqual(obj, newObj) {
99 | t.Errorf("Decoded object is not equal to the original object")
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/serialization/conversion_test.go:
--------------------------------------------------------------------------------
1 | package serialization
--------------------------------------------------------------------------------
/serialization/from_file.go:
--------------------------------------------------------------------------------
1 | package serialization
2 |
3 | import (
4 | "os"
5 | )
6 |
7 |
8 | // FromJsonFile deserializes JSON data from the specified file into the given object.
9 | func FromJsonFile(jsonFile string, obj interface{}) error {
10 | // Read JSON file
11 | data, err := os.ReadFile(jsonFile)
12 | if err != nil {
13 | return err
14 | }
15 | // Deserialize JSON into object
16 | err = UnserializeJson(data, obj)
17 | if err != nil {
18 | return err
19 | }
20 | return nil
21 | }
22 |
23 |
24 | // FromXmlFile deserializes XML data from the specified file into the given object.
25 | func FromXmlFile(xmlFile string, obj interface{}) error {
26 | // Read XML file
27 | data, err := os.ReadFile(xmlFile)
28 | if err != nil {
29 | return err
30 | }
31 | // Deserialize XML into object
32 | err = UnserializeXml(data, obj)
33 | if err != nil {
34 | return err
35 | }
36 | return nil
37 | }
38 |
39 |
40 | // FromAsnFile deserializes ASN.1 encoded data from the specified file into the given object.
41 | func FromAsn1File(asnFile string, obj interface{}) error {
42 | // Read ASN.1 file
43 | data, err := os.ReadFile(asnFile)
44 | if err != nil {
45 | return err
46 | }
47 | // Unmarshal ASN.1 data into object
48 | err = UnserializeAsn1(data, obj)
49 | if err != nil {
50 | return err
51 | }
52 | return nil
53 | }
54 |
55 |
56 | // FromCsvFileToJson reads data from the specified CSV file and deserializes it into JSON format.
57 | func FromCsvFileToJson(csvFile string, obj interface{}) error {
58 | // Open the CSV file
59 | file, err := os.Open(csvFile)
60 | if err != nil {
61 | return err
62 | }
63 | defer file.Close()
64 | return nil
65 | }
66 |
67 |
68 | // FromCsvFileToXml reads data from the specified CSV file and deserializes it into XML format.
69 | func FromCsvFileToXml(csvFile string, obj interface{}) error {
70 | // Open the CSV file
71 | file, err := os.Open(csvFile)
72 | if err != nil {
73 | return err
74 | }
75 | defer file.Close()
76 | return nil
77 | }
--------------------------------------------------------------------------------
/serialization/from_file_test.go:
--------------------------------------------------------------------------------
1 | package serialization
2 |
3 | import (
4 | "os"
5 | "reflect"
6 | "testing"
7 | )
8 |
9 | func Test_FromJsonFile(t *testing.T) {
10 | type TestStruct struct {
11 | Name string
12 | Age int
13 | Email string
14 | }
15 |
16 | // Create a temporary JSON file for testing
17 | testData := []byte(`{"Name": "John", "Age": 30, "Email": "john@example.com"}`)
18 | tmpfile := createTempFile(t, "test.json", testData)
19 | defer os.Remove(tmpfile.Name())
20 |
21 | var obj TestStruct
22 | err := FromJsonFile(tmpfile.Name(), &obj)
23 | if err != nil {
24 | t.Errorf("FromJsonFile() error = %v", err)
25 | return
26 | }
27 |
28 | expected := TestStruct{Name: "John", Age: 30, Email: "john@example.com"}
29 | if !reflect.DeepEqual(obj, expected) {
30 | t.Errorf("Deserialized object is not equal to the expected object")
31 | }
32 | }
33 |
34 | func Test_FromXmlFile(t *testing.T) {
35 | type TestStruct struct {
36 | Name string
37 | Age int
38 | Email string
39 | }
40 |
41 | // Create a temporary XML file for testing
42 | testData := []byte(`John30john@example.com`)
43 | tmpfile := createTempFile(t, "test.xml", testData)
44 | defer os.Remove(tmpfile.Name())
45 |
46 | var obj TestStruct
47 | err := FromXmlFile(tmpfile.Name(), &obj)
48 | if err != nil {
49 | t.Errorf("FromXmlFile() error = %v", err)
50 | return
51 | }
52 |
53 | expected := TestStruct{Name: "John", Age: 30, Email: "john@example.com"}
54 | if !reflect.DeepEqual(obj, expected) {
55 | t.Errorf("Deserialized object is not equal to the expected object")
56 | }
57 | }
58 |
59 | func Test_FromAsnFile(t *testing.T) {
60 | type TestStruct struct {
61 | Name string
62 | Age int
63 | Email string
64 | }
65 |
66 | // Create a temporary ASN.1 file for testing
67 | testData, err := SerializeAsn1(TestStruct{Name: "John", Age: 30, Email: "john@example.com"})
68 | if err != nil {
69 | t.Errorf("SerializeAsn1() error = %v", err)
70 | return
71 | }
72 | tmpfile := createTempFile(t, "test.asn", testData)
73 | defer os.Remove(tmpfile.Name())
74 |
75 | var obj TestStruct
76 | err = FromAsn1File(tmpfile.Name(), &obj)
77 | if err != nil {
78 | t.Errorf("FromAsnFile() error = %v", err)
79 | return
80 | }
81 |
82 | expected := TestStruct{Name: "John", Age: 30, Email: "john@example.com"}
83 | if !reflect.DeepEqual(obj, expected) {
84 | t.Errorf("Deserialized object is not equal to the expected object")
85 | }
86 | }
87 |
88 | func createTempFile(t *testing.T, filename string, data []byte) *os.File {
89 | tmpfile, err := os.CreateTemp("", filename)
90 | if err != nil {
91 | t.Fatalf("Failed to create temporary file: %v", err)
92 | }
93 | if _, err := tmpfile.Write(data); err != nil {
94 | t.Fatalf("Failed to write data to temporary file: %v", err)
95 | }
96 | return tmpfile
97 | }
98 |
--------------------------------------------------------------------------------
/serialization/json.go:
--------------------------------------------------------------------------------
1 | package serialization
2 |
3 | import (
4 | "encoding/json"
5 | "io"
6 | )
7 |
8 |
9 | // SerializeJson serializes the given object to JSON
10 | func SerializeJson(obj interface{}) ([]byte, error) {
11 | jsonData, err := json.Marshal(obj)
12 | if err != nil {
13 | return nil, err
14 | }
15 | return jsonData, nil
16 | }
17 |
18 | // UnserializeJson deserializes JSON data into the given data structure
19 | func UnserializeJson(jsonData []byte, obj interface{}) error {
20 | err := json.Unmarshal(jsonData, obj)
21 | if err != nil {
22 | return err
23 | }
24 | return nil
25 | }
26 |
27 | // EncodeJson encodes the given object as JSON and writes it to the writer
28 | func EncodeJson(writer io.Writer, obj interface{}) error {
29 | encoder := json.NewEncoder(writer)
30 | err := encoder.Encode(obj)
31 | if err != nil {
32 | return err
33 | }
34 | return nil
35 | }
36 |
37 | // DecodeJson decodes JSON from the reader into the given data structure
38 | func DecodeJson(reader io.Reader, v interface{}) error {
39 | decoder := json.NewDecoder(reader)
40 | err := decoder.Decode(v)
41 | if err != nil {
42 | return err
43 | }
44 | return nil
45 | }
--------------------------------------------------------------------------------
/serialization/json_test.go:
--------------------------------------------------------------------------------
1 | package serialization
2 |
3 | import (
4 | "bytes"
5 | "reflect"
6 | "testing"
7 | )
8 |
9 | type testJsonStruct struct {
10 | Name string
11 | Age int
12 | Email string
13 | }
14 |
15 | func Test_SerializeJson(t *testing.T) {
16 | obj := testJsonStruct{Name: "John", Age: 30, Email: "john@example.com"}
17 |
18 | jsonData, err := SerializeJson(obj)
19 | if err != nil {
20 | t.Errorf("SerializeJson() error = %v", err)
21 | return
22 | }
23 |
24 | var newObj testJsonStruct
25 | err = UnserializeJson(jsonData, &newObj)
26 | if err != nil {
27 | t.Errorf("UnserializeJson() error = %v", err)
28 | return
29 | }
30 |
31 | if !reflect.DeepEqual(obj, newObj) {
32 | t.Errorf("Deserialized object is not equal to the original object")
33 | }
34 | }
35 |
36 | func Test_UnserializeJson(t *testing.T) {
37 |
38 | obj := testJsonStruct{Name: "John", Age: 30, Email: "john@example.com"}
39 | jsonData, err := SerializeJson(obj)
40 | if err != nil {
41 | t.Errorf("SerializeJson() error = %v", err)
42 | return
43 | }
44 |
45 | var newObj testJsonStruct
46 | err = UnserializeJson(jsonData, &newObj)
47 | if err != nil {
48 | t.Errorf("UnserializeJson() error = %v", err)
49 | return
50 | }
51 |
52 | if !reflect.DeepEqual(obj, newObj) {
53 | t.Errorf("Deserialized object is not equal to the original object")
54 | }
55 | }
56 |
57 | func Test_EncodeJson(t *testing.T) {
58 | obj := testJsonStruct{Name: "John", Age: 30, Email: "john@example.com"}
59 |
60 | var buf bytes.Buffer
61 | err := EncodeJson(&buf, obj)
62 | if err != nil {
63 | t.Errorf("EncodeJson() error = %v", err)
64 | return
65 | }
66 |
67 | var newObj testJsonStruct
68 | err = DecodeJson(&buf, &newObj)
69 | if err != nil {
70 | t.Errorf("DecodeJson() error = %v", err)
71 | return
72 | }
73 |
74 | if !reflect.DeepEqual(obj, newObj) {
75 | t.Errorf("Decoded object is not equal to the original object")
76 | }
77 | }
78 |
79 | func Test_DecodeJson(t *testing.T) {
80 |
81 | obj := testJsonStruct{Name: "John", Age: 30, Email: "john@example.com"}
82 | var buf bytes.Buffer
83 | err := EncodeJson(&buf, obj)
84 | if err != nil {
85 | t.Errorf("EncodeJson() error = %v", err)
86 | return
87 | }
88 |
89 | var newObj testJsonStruct
90 | err = DecodeJson(&buf, &newObj)
91 | if err != nil {
92 | t.Errorf("DecodeJson() error = %v", err)
93 | return
94 | }
95 |
96 | if !reflect.DeepEqual(obj, newObj) {
97 | t.Errorf("Decoded object is not equal to the original object")
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/serialization/xml.go:
--------------------------------------------------------------------------------
1 | package serialization
2 |
3 | import (
4 | "encoding/xml"
5 | "io"
6 | )
7 |
8 |
9 | // SerializeXml serializes the given object to JSON
10 | func SerializeXml(obj interface{}) ([]byte, error) {
11 | xmlData, err := xml.MarshalIndent(obj, "", " ")
12 | if err != nil {
13 | return nil, err
14 | }
15 | return xmlData, nil
16 | }
17 |
18 | // UnserializeXml deserializes JSON data into the given data structure
19 | func UnserializeXml(xmlData []byte, obj interface{}) error {
20 | err := xml.Unmarshal(xmlData, obj)
21 | if err != nil {
22 | return err
23 | }
24 | return nil
25 | }
26 |
27 | // EncodeXml encodes the given object as JSON and writes it to the writer
28 | func EncodeXml(writer io.Writer, obj interface{}) error {
29 | encoder := xml.NewEncoder(writer)
30 | err := encoder.Encode(obj)
31 | if err != nil {
32 | return err
33 | }
34 | return nil
35 | }
36 |
37 | // DecodeXml decodes JSON from the reader into the given data structure
38 | func DecodeXml(reader io.Reader, v interface{}) error {
39 | decoder := xml.NewDecoder(reader)
40 | err := decoder.Decode(v)
41 | if err != nil {
42 | return err
43 | }
44 | return nil
45 | }
--------------------------------------------------------------------------------
/serialization/xml_test.go:
--------------------------------------------------------------------------------
1 | package serialization
2 |
3 | import (
4 | "bytes"
5 | "reflect"
6 | "testing"
7 | )
8 |
9 | type testXmlStruct struct {
10 | Name string
11 | Age int
12 | Email string
13 | }
14 |
15 |
16 | func Test_SerializeXml(t *testing.T) {
17 | obj := testXmlStruct{Name: "John", Age: 30, Email: "john@example.com"}
18 |
19 | xmlData, err := SerializeXml(obj)
20 | if err != nil {
21 | t.Errorf("SerializeXml() error = %v", err)
22 | return
23 | }
24 |
25 | var newObj testXmlStruct
26 | err = UnserializeXml(xmlData, &newObj)
27 | if err != nil {
28 | t.Errorf("UnserializeXml() error = %v", err)
29 | return
30 | }
31 |
32 | if !reflect.DeepEqual(obj, newObj) {
33 | t.Errorf("Deserialized object is not equal to the original object")
34 | }
35 | }
36 |
37 | func Test_UnserializeXml(t *testing.T) {
38 |
39 | obj := testXmlStruct{Name: "John", Age: 30, Email: "john@example.com"}
40 | xmlData, err := SerializeXml(obj)
41 | if err != nil {
42 | t.Errorf("SerializeXml() error = %v", err)
43 | return
44 | }
45 |
46 | var newObj testXmlStruct
47 | err = UnserializeXml(xmlData, &newObj)
48 | if err != nil {
49 | t.Errorf("UnserializeXml() error = %v", err)
50 | return
51 | }
52 |
53 | if !reflect.DeepEqual(obj, newObj) {
54 | t.Errorf("Deserialized object is not equal to the original object")
55 | }
56 | }
57 |
58 | func Test_EncodeXml(t *testing.T) {
59 |
60 | obj := testXmlStruct{Name: "John", Age: 30, Email: "john@example.com"}
61 |
62 | var buf bytes.Buffer
63 | err := EncodeXml(&buf, obj)
64 | if err != nil {
65 | t.Errorf("EncodeXml() error = %v", err)
66 | return
67 | }
68 |
69 | var newObj testXmlStruct
70 | err = DecodeXml(&buf, &newObj)
71 | if err != nil {
72 | t.Errorf("DecodeXml() error = %v", err)
73 | return
74 | }
75 |
76 | if !reflect.DeepEqual(obj, newObj) {
77 | t.Errorf("Decoded object is not equal to the original object")
78 | }
79 | }
80 |
81 | func Test_DecodeXml(t *testing.T) {
82 |
83 | obj := testXmlStruct{Name: "John", Age: 30, Email: "john@example.com"}
84 | var buf bytes.Buffer
85 | err := EncodeXml(&buf, obj)
86 | if err != nil {
87 | t.Errorf("EncodeXml() error = %v", err)
88 | return
89 | }
90 |
91 | var newObj testXmlStruct
92 | err = DecodeXml(&buf, &newObj)
93 | if err != nil {
94 | t.Errorf("DecodeXml() error = %v", err)
95 | return
96 | }
97 |
98 | if !reflect.DeepEqual(obj, newObj) {
99 | t.Errorf("Decoded object is not equal to the original object")
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/units/README.md:
--------------------------------------------------------------------------------
1 | # units
2 |
3 | Contains Units of measurement
4 |
5 | ## Units
6 | - [x] Capacity
7 | - [x] Storage
8 | - [x] Length
9 | - [x] Weight
--------------------------------------------------------------------------------
/units/length.go:
--------------------------------------------------------------------------------
1 | package units
2 |
3 | // Constants for various length units, all based on the meter.
4 | const (
5 | METER float64 = 1
6 | DECA_METER float64 = METER * 10
7 | HECTO_METER float64 = METER * 100
8 | KILO_METER float64 = METER * 1000
9 | PETA_METER float64 = METER * 1_000_000_000_000_000
10 |
11 | DECI_METER float64 = METER / 10
12 | CENTI_METER float64 = METER / 100
13 | MILI_METER float64 = METER / 1000
14 | MICRO_METER float64 = METER / 1_000_000
15 | NANO_METER float64 = METER / 1_000_000_000
16 | PICO_METER float64 = METER / 1_000_000_000_000
17 | FEMTO_METER float64 = METER / 1_000_000_000_000_000
18 |
19 | INCH float64 = CENTI_METER / 2.54
20 | FOOT float64 = CENTI_METER / 30.48
21 | YARD float64 = METER / 0.9144
22 | MILES float64 = KILO_METER / 1.60934
23 | )
24 |
--------------------------------------------------------------------------------
/units/length_test.go:
--------------------------------------------------------------------------------
1 | package units
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | func Test_LenghtUnits(t *testing.T) {
9 | fmt.Println(1 * METER) //1 m
10 | fmt.Println(5 * KILO_METER) //5 Km
11 | fmt.Println(0.7 * HECTO_METER) //0.7Hm
12 | fmt.Println(PETA_METER)
13 | fmt.Println(DECI_METER) // 1 dm
14 | fmt.Println(50 * CENTI_METER) // 50 cm
15 | fmt.Println(100 * MILI_METER) // 100 mm
16 | fmt.Println(0.5 * MICRO_METER) // 0.5 um
17 | fmt.Println(10 * NANO_METER) // 10 nm
18 | fmt.Println(10 * FEMTO_METER)
19 | }
20 |
--------------------------------------------------------------------------------
/units/storage.go:
--------------------------------------------------------------------------------
1 | package units
2 |
3 | // Constants for various data storage units, all based on the byte.
4 | const (
5 | BIT float64 = BYTE / 8
6 | BYTE float64 = 1
7 | KILO_BYTE float64 = BYTE * 1024
8 | MEGA_BYTE float64 = KILO_BYTE * 1024
9 | GIGA_BYTE float64 = MEGA_BYTE * 1024
10 | TERA_BYTE float64 = GIGA_BYTE * 1024
11 | PETA_BYTE float64 = TERA_BYTE * 1024
12 | EXA_BYTE float64 = PETA_BYTE * 1024
13 | ZETTA_BYTE float64 = EXA_BYTE * 1024
14 | YOTTA_BYTE float64 = ZETTA_BYTE * 1024
15 | )
16 |
--------------------------------------------------------------------------------
/units/storage_test.go:
--------------------------------------------------------------------------------
1 | package units
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | func Test_StorageUnits(t *testing.T) {
9 | fmt.Println(8 * BIT) // 8 bits
10 | fmt.Println(4 * BYTE) // 4 B
11 | fmt.Println(45 * KILO_BYTE) // 45 KB
12 | fmt.Println(20 * MEGA_BYTE)
13 | fmt.Println(3 * GIGA_BYTE) // 3 GB
14 | fmt.Println(9 * TERA_BYTE) // 9 TB
15 | fmt.Println(PETA_BYTE)
16 | fmt.Println(EXA_BYTE)
17 | fmt.Println(ZETTA_BYTE)
18 | fmt.Println(YOTTA_BYTE)
19 | }
20 |
--------------------------------------------------------------------------------
/units/volume.go:
--------------------------------------------------------------------------------
1 | package units
2 |
3 | // Constants for various volume units, all based on the liter.
4 | const (
5 | LITER float64 = 1
6 | DECI_LITER float64 = LITER / 10
7 | CENTI_LITER float64 = LITER / 100
8 | MILI_LITER float64 = LITER / 1000
9 |
10 | DECA_LITER float64 = LITER * 10
11 | HECTO_LITER float64 = LITER * 100
12 | KILO_LITER float64 = LITER * 1000
13 |
14 | FLUID_OUNCE float64 = 0.0295735 * LITER // U.S. fluid ounce
15 | CUP float64 = 0.236588 * LITER // U.S. customary cup
16 | PINT float64 = 0.473176 * LITER // U.S. liquid pint
17 | QUART float64 = 0.946353 * LITER // U.S. liquid quart
18 | GALLON float64 = 3.78541 * LITER // U.S. liquid gallon
19 | )
20 |
--------------------------------------------------------------------------------
/units/volume_test.go:
--------------------------------------------------------------------------------
1 | package units
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | func Test_CapacityUnits(t *testing.T) {
9 | fmt.Println(1 * LITER) // 1 l
10 | fmt.Println(DECI_LITER)
11 | fmt.Println(33 * CENTI_LITER) // 33 cl
12 | fmt.Println(100 * MILI_LITER) // 100 ml
13 | fmt.Println(DECA_LITER)
14 | fmt.Println(HECTO_LITER)
15 | fmt.Println(KILO_LITER)
16 | }
17 |
--------------------------------------------------------------------------------
/units/weight.go:
--------------------------------------------------------------------------------
1 | package units
2 |
3 | // Constants for various mass units, all based on the gram.
4 | const (
5 | GRAM float64 = 1
6 | MILI_GRAM float64 = GRAM / 1000
7 | HECTO_GRAM float64 = GRAM * 100
8 | KILO_GRAM float64 = GRAM * 1000
9 | TONNE float64 = KILO_GRAM * 1000
10 |
11 | OUNCE float64 = GRAM / 28.3495 // International avoirdupois ounce
12 | POUND float64 = KILO_GRAM / 0.453592 // International avoirdupois pound
13 | )
14 |
--------------------------------------------------------------------------------
/units/weight_test.go:
--------------------------------------------------------------------------------
1 | package units
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 |
9 | func Test_WeigthUnits(t *testing.T) {
10 | fmt.Println(1 * GRAM) // 1 g
11 | fmt.Println(2 * MILI_GRAM) // 2 mg
12 | fmt.Println(HECTO_GRAM)
13 | fmt.Println(10 * KILO_GRAM) // 10 Kg
14 | fmt.Println(16 * TONNE) // 16 t
15 | fmt.Println(8 * POUND)
16 | fmt.Println(OUNCE)
17 | }
18 |
--------------------------------------------------------------------------------