├── .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 | 35 | 36 | 37 | 38 | {{ range .ProductList }} 39 | 40 | 41 | 42 | 43 | 44 | {{ end }} 45 |
IdName Price
{{ .Id }}{{ .Name }}{{ .Price }}
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 |
11 | 12 | 13 |
14 | 15 | 16 | -------------------------------------------------------------------------------- /_examples/uploader/templates/upload.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | File Upload 7 | 8 | 9 |

Upload single File

10 |
11 | 12 | 13 |
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 | --------------------------------------------------------------------------------