Merge branch 'develop' into feature/order

This commit is contained in:
hossein 2026-04-08 06:25:13 +00:00
commit 9776db70b5
285 changed files with 44100 additions and 45670 deletions

View File

@ -0,0 +1,74 @@
package command
import (
"log"
"os"
"path/filepath"
"git.gocasts.ir/ebhomengo/niki/repository/migrator"
"git.gocasts.ir/ebhomengo/niki/repository/mysql"
"github.com/spf13/cobra"
)
var up bool
var down bool
var migrateCmd = &cobra.Command{
Use: "migrate",
Short: "Run database migrations",
Long: `This command runs the database migrations for the product service.`,
Run: func(cmd *cobra.Command, args []string) {
migrate()
},
}
func migrate() {
workingDir, err := os.Getwd()
if err != nil {
log.Fatalf("Error getting working directory: %v", err)
}
migrationPath := filepath.Join(workingDir, "productapp", "repository", "migrations")
// to run migrations when you want to run product service locally
if path := os.Getenv("MIGRATION_PATH"); path != "" {
migrationPath = path
log.Printf("Using override migration path: %s", migrationPath)
} else {
log.Printf("Using default migration path: %s", migrationPath)
}
// TODO: Load config from environment or config file
mgr := migrator.New(migrator.Config{
MysqlConfig: mysql.Config{
Username: getEnv("DB_USERNAME", "root"),
Password: getEnv("DB_PASSWORD", ""),
Port: 3306,
Host: getEnv("DB_HOST", "localhost"),
DBName: getEnv("DB_NAME", "niki_db"),
},
MigrationPath: migrationPath,
MigrationDBName: "product_migrations",
})
if up {
mgr.Up()
} else if down {
mgr.Down()
} else {
log.Println("Please specify a migration direction with --up or --down")
}
}
func getEnv(key, defaultValue string) string {
if value := os.Getenv(key); value != "" {
return value
}
return defaultValue
}
func init() {
migrateCmd.Flags().BoolVar(&up, "up", false, "Run migrations up")
migrateCmd.Flags().BoolVar(&down, "down", false, "Run migrations down")
RootCmd.AddCommand(migrateCmd)
}

View File

@ -0,0 +1,10 @@
package command
import "github.com/spf13/cobra"
var RootCmd = &cobra.Command{
Use: "product_service",
Short: "A CLI for Product Service",
Long: `Product Service CLI is a tool to manage and run
the product service, including migrations and server startup.`,
}

View File

@ -0,0 +1,54 @@
package command
import (
"fmt"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"github.com/spf13/cobra"
)
var port string
var serveCmd = &cobra.Command{
Use: "serve",
Short: "Start the product service",
Long: `This command starts the main product service.`,
Run: func(cmd *cobra.Command, args []string) {
serve()
},
}
func serve() {
log.Println("Product Service Starting...")
// TODO: Initialize database connection
// TODO: Initialize service dependencies
// TODO: Setup HTTP server with routes
// Setup graceful shutdown
go func() {
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
<-sigCh
log.Println("Shutting down Product Service gracefully...")
os.Exit(0)
}()
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Product Service OK!")
})
log.Printf("Product Service listening on port %s", port)
if err := http.ListenAndServe(":"+port, nil); err != nil {
log.Fatalf("Failed to start server: %v", err)
}
}
func init() {
serveCmd.Flags().StringVarP(&port, "port", "p", "8080", "Port to run the server on")
RootCmd.AddCommand(serveCmd)
}

13
cmd/productapp/main.go Normal file
View File

@ -0,0 +1,13 @@
package main
import (
"os"
"git.gocasts.ir/ebhomengo/niki/cmd/productapp/command"
)
func main() {
if err := command.RootCmd.Execute(); err != nil {
os.Exit(1)
}
}

View File

@ -1,15 +1,143 @@
package main package main
import ( import (
"encoding/json"
"fmt" "fmt"
"log" "log"
"net/http" "net/http"
"strconv"
"git.gocasts.ir/ebhomengo/niki/staffapp/repository/database"
"git.gocasts.ir/ebhomengo/niki/staffapp/service"
) )
func main() { func main() {
fmt.Println(" Staffapp Server Starting...")
staffDb := database.New()
staffService := service.NewStaffService(staffDb)
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Staffapp OK!") fmt.Fprintf(w, "Staffapp OK!")
}) })
http.HandleFunc("/staff", func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
var newStaff service.Staff
decoder := json.NewDecoder(r.Body)
err := decoder.Decode(&newStaff)
if err != nil {
http.Error(w, "Invalid request payload: "+err.Error(), http.StatusBadRequest)
return
}
defer r.Body.Close()
createdStaff, err := staffService.RegisterStaff(newStaff.Name, newStaff.LastName, newStaff.PhoneNumber)
if err != nil {
http.Error(w, "Failed to register staff: "+err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(createdStaff)
})
http.HandleFunc("/staff/", func(w http.ResponseWriter, r *http.Request) {
idStr := r.URL.Path[len("/staff/"):]
if idStr == "" {
http.Error(w, "Missing staff ID", http.StatusBadRequest)
return
}
id, err := strconv.Atoi(idStr)
if err != nil {
http.Error(w, "Invalid staff ID format", http.StatusBadRequest)
return
}
switch r.Method {
case http.MethodGet:
st, err := staffService.Get(id)
if err != nil {
if err.Error() == "staff not found" {
http.Error(w, "Staff not found", http.StatusNotFound)
} else {
http.Error(w, "Failed to fetch staff: "+err.Error(), http.StatusInternalServerError)
}
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(st)
case http.MethodPut:
var staffData struct {
Name string `json:"Name"`
LastName string `json:"LastName"`
PhoneNumber string `json:"PhoneNumber"`
}
decoder := json.NewDecoder(r.Body)
if err := decoder.Decode(&staffData); err != nil {
http.Error(w, "Invalid request payload: "+err.Error(), http.StatusBadRequest)
return
}
defer r.Body.Close()
updatedStaff, err := staffService.Update(id, staffData.Name, staffData.LastName, staffData.PhoneNumber)
if err != nil {
if err.Error() == "staff not found" {
http.Error(w, "Staff not found", http.StatusNotFound)
} else {
http.Error(w, "Failed to update staff: "+err.Error(), http.StatusInternalServerError)
}
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(updatedStaff)
case http.MethodDelete:
err = staffService.Remove(id)
if err != nil {
if err.Error() == "staff not found" {
http.Error(w, "Staff not found", http.StatusNotFound)
} else {
http.Error(w, "Failed to remove staff: "+err.Error(), http.StatusInternalServerError)
}
return
}
w.WriteHeader(http.StatusNoContent)
default:
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
}
})
http.HandleFunc("/staffs", func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
list, err := staffService.List()
if err != nil {
http.Error(w, "Failed to fetch staff list: "+err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(list)
})
log.Fatal(http.ListenAndServe(":8080", nil)) log.Fatal(http.ListenAndServe(":8080", nil))
} }

View File

@ -18,8 +18,8 @@ type TestContainer struct {
dockerPool *dockertest.Pool // the connection pool to Docker. dockerPool *dockertest.Pool // the connection pool to Docker.
mariaResource *dockertest.Resource // MariaDB Docker container resource. mariaResource *dockertest.Resource // MariaDB Docker container resource.
redisResource *dockertest.Resource // Redis Docker container resource. redisResource *dockertest.Resource // Redis Docker container resource.
mariaDBConn *mysql.DB // Connection to the MariaDB database. mariaDBConn *mysql.DB // Connection to the MariaDB mysql.
redisDBConn *redisadapter.Adapter // Connection to the Redis database. redisDBConn *redisadapter.Adapter // Connection to the Redis mysql.
containerExpiryInSeconds uint containerExpiryInSeconds uint
} }
@ -158,7 +158,7 @@ func (t *TestContainer) Start() {
return nil return nil
}); err != nil { }); err != nil {
log.Fatalf("Could not connect to database: %s", err) log.Fatalf("Could not connect to mysql: %s", err)
} }
} }

View File

@ -0,0 +1,8 @@
package entity
type Channel struct {
ID int8
Type NotificationType
Provider string
Config string
}

View File

@ -0,0 +1,51 @@
package entity
type Notification struct {
ID int8
Type NotificationType
Recipinet string
Body string
Status NotificationStatus
}
type NotificationType uint8
const (
Email NotificationType = iota + 1
SMS
Push
)
func (t NotificationType) String() string {
switch t {
case Email:
return "Email"
case SMS:
return "SMS"
case Push:
return "Push"
default:
return "Unknown"
}
}
type NotificationStatus uint8
const (
Pending NotificationStatus = iota + 1
Success
Failed
)
func (t NotificationStatus) String() string {
switch t {
case Pending:
return "Pending"
case Success:
return "Success"
case Failed:
return "Failed"
default:
return "Unknown"
}
}

View File

@ -0,0 +1,17 @@
package messagebroker
import "git.gocasts.ir/ebhomengo/niki/domain/notification/entity"
type redis struct {
}
func (r *redis) AddItem(notification entity.Notification) error {
return nil
}
func (r *redis) RemoveItem(notification entity.Notification) error {
return nil
}
func (r *redis) HealthCheck() error {
return nil
}

View File

@ -0,0 +1,67 @@
package service
import (
_ "time"
"git.gocasts.ir/ebhomengo/niki/domain/notification/entity"
)
type Notifservice struct {
MessageBroker MessageBroker
Repository Repository
Channel Channel
}
type MessageBroker interface {
AddItem(notification entity.Notification) error
RemoveItem(notification entity.Notification) error
HealthCheck() error
}
type Repository interface {
AddItem(notification entity.Notification) error
}
type Channel interface {
SendMessage(notification entity.Notification) error
}
type NotificationServiceRequest struct {
Body string
Type entity.NotificationType
Recipinet string
}
func (n *Notifservice) NewNotification(r NotificationServiceRequest) *entity.Notification {
// TODO add validation of notification properties
return &entity.Notification{
Type: r.Type,
Recipinet: r.Recipinet,
Body: r.Body,
Status: entity.Pending,
}
}
func (n *Notifservice) Send(notification entity.Notification) error {
if err := n.Channel.SendMessage(notification); err != nil {
return err
}
return nil
}
func (n *Notifservice) Add(notification *entity.Notification) error {
err := n.MessageBroker.AddItem(*notification)
if err != nil {
return err
}
return nil
}
func (n *Notifservice) Archive(notification *entity.Notification) error {
if err := n.MessageBroker.RemoveItem(*notification); err != nil {
return err
}
if err := n.Repository.AddItem(*notification); err != nil {
return err
}
return nil
}

View File

@ -0,0 +1,7 @@
package service
import "git.gocasts.ir/ebhomengo/niki/domain/notification/entity"
type sender interface {
Send(notification entity.Notification) error
}

View File

@ -1 +0,0 @@
package donate

View File

@ -1,5 +0,0 @@
package donateapp
type Config struct{
}

View File

@ -1,8 +0,0 @@
package donate_server
type Handler struct {
}
func NewHandler() Handler {
return Handler{}
}

View File

@ -1,11 +0,0 @@
-- +migrate Up
CREATE TABLE `donates` (
`id` bigint UNSIGNED NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`) USING BTREE,
UNIQUE INDEX `id`(`id` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 84 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_persian_ci ROW_FORMAT = Dynamic;
-- +migrate Down
DROP TABLE IF EXISTS `donates`;

View File

@ -1 +0,0 @@
package mysql

View File

@ -1 +0,0 @@
package service

View File

@ -1 +0,0 @@
package service

View File

@ -1,4 +1,4 @@
package doanteApp package donate_app
import "net/http" import "net/http"
@ -6,3 +6,5 @@ type Application struct {
Config Config Config Config
HTTPServer *http.Server HTTPServer *http.Server
} }

17
donate_app/config.go Normal file
View File

@ -0,0 +1,17 @@
package donate_app
import (
"git.gocasts.ir/ebhomengo/niki/repository/mysql"
)
type Config struct {
Mysql mysql.Config `koanf:"mariadb"`
}
type Config struct{
}

View File

@ -0,0 +1,5 @@
package donate_server
type Handler struct{}

4
donate_app/pkg/types.go Normal file
View File

@ -0,0 +1,4 @@
// --- Type Aliases ---
package pkg
type ID uint64

View File

@ -0,0 +1,18 @@
-- +migrate Up
CREATE TABLE `campaigns` (
`id` BIGINT PRIMARY KEY AUTO_INCREMENT,
`title` VARCHAR(255) NOT NULL,
`description` TEXT,
`goal_amount` DECIMAL(15,2) NOT NULL,
`raised_amount` DECIMAL(15,2) DEFAULT 0,
`status` VARCHAR(50) NOT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`deadline_at` TIMESTAMP,
`admin_id` BIGINT NOT NULL,
FOREIGN KEY (`admin_id`) REFERENCES `users`(`id`) ON DELETE RESTRICT
);
-- +migrate Down
DROP TABLE `campaigns`;

View File

@ -0,0 +1,19 @@
-- +migrate Up
CREATE TABLE `campaign_participants` (
`id` BIGINT PRIMARY KEY AUTO_INCREMENT,
`campaign_id` BIGINT NOT NULL,
`user_id` BIGINT NOT NULL,
`amount` DECIMAL(15,2) NOT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (`campaign_id`)
REFERENCES `campaigns`(`id`)
ON DELETE CASCADE,
FOREIGN KEY (`user_id`)
REFERENCES `users`(`id`)
ON DELETE CASCADE,
UNIQUE KEY `unique_campaign_user` (`campaign_id`, `user_id`)
);
-- +migrate Down
DROP TABLE `campaign_participants`;

View File

@ -0,0 +1,90 @@
package mysql
import (
"context"
"git.gocasts.ir/ebhomengo/niki/campaign/entity"
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
"git.gocasts.ir/ebhomengo/niki/repository/mysql"
"git.gocasts.ir/ebhomengo/niki/types"
)
type DB struct {
conn *mysql.DB
}
func New(db *mysql.DB) *DB {
return &DB{conn: db}
}
// CreateCampaign creates a new campaign
func (d *DB) CreateCampaign(ctx context.Context, campaign entity.Campaign) (types.ID, error) {
const Op = "repository.mysql.campaign.create"
tx, err := d.conn.Conn().BeginTx(ctx, nil)
if err != nil {
return 0, richerror.New(Op).WithErr(err)
}
defer tx.Rollback()
query := `INSERT INTO campaigns (title, description, goal_amount, raised_amount,
status, deadline_at ,admin_id , created_at )
VALUES (?, ?, ?, ?, ?, ?, ? , NOW() )`
result, err := tx.ExecContext(ctx, query,
campaign.Title,
campaign.Description,
campaign.GoalAmount,
campaign.RaisedAmount,
campaign.Status,
campaign.DeadlineAt,
campaign.AdminID,
campaign.CreatedAt,
)
if err != nil {
return 0, richerror.New(Op).WithErr(err)
}
campaignID, err := result.LastInsertId()
if err != nil {
return 0, richerror.New(Op).WithErr(err)
}
if err := tx.Commit(); err != nil {
return 0, richerror.New(Op).WithErr(err)
}
return types.ID(campaignID), nil
}
// Create adds a new participant to a campaign
func (d *DB) CreateCampaignParticipants(ctx context.Context, participant entity.CampaignParticipant) (types.ID, error) {
const Op = "repository.mysql.campaign_participant.create"
query := `INSERT INTO campaign_participants (id,campaign_id, user_id, amount , created_at)
VALUES (?, ?, ? , ? , NOW())`
result, err := d.conn.ExecContext(ctx, query,
participant.ID,
participant.CampaignID,
participant.UserID,
participant.Amount,
participant.CreatedAt
)
if err != nil {
return 0, richerror.New(Op).WithErr(err)
}
id, err := result.LastInsertId()
if err != nil {
return 0, richerror.New(Op).WithErr(err)
}
return types.ID(id), nil
}

View File

@ -0,0 +1,14 @@
package service
type ID uint64
type CampaignParticipant struct {
ID ID `json:"id"`
CampaignID ID `json:"campaign_id"`
UserID ID `json:"user_id"`
Amount float64 `json:"amount"`
CreatedAt time.Time `json:"created_at"`
}

View File

@ -0,0 +1,30 @@
package service
import (
"time"
)
// CampaignStatus represents the possible states of a campaign
type CampaignStatus string
const (
StatusActive CampaignStatus = "active"
StatusCompleted CampaignStatus = "completed"
StatusCancelled CampaignStatus = "cancelled"
StatusExpired CampaignStatus = "expired" // New status for deadlines
)
type ID uint64
type Campaign struct {
ID ID `json:"id"`
Title string `json:"title"`
Description string `json:"description"`
GoalAmount float64 `json:"goal_amount"`
RaisedAmount float64 `json:"raised_amount"`
Status CampaignStatus `json:"status"`
CreatedAt time.Time `json:"created_at"`
DeadlineAt *time.Time `json:"deadline_at,omitempty"`
AdminID ID `json:"creator_id"`
}

View File

@ -0,0 +1,91 @@
package service
import (
"context"
"errors"
"time"
"git.gocasts.ir/ebhomengo/niki/campaign/entity"
"git.gocasts.ir/ebhomengo/niki/repository"
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
"git.gocasts.ir/ebhomengo/niki/types"
)
type CampaignService struct {
repo repository.CampaignRepository
participantRepo repository.CampaignParticipantRepository
}
type CampaignServiceInterface interface {
CreateCampaign(ctx context.Context, req CampaignRepository) (types.ID, error)
}
// NewCampaignService creates a new campaign service
func NewCampaignService(
repo repository.CampaignRepository,
participantRepo repository.CampaignParticipantRepository,
) *CampaignService {
return &CampaignService{
repo: repo,
participantRepo: participantRepo,
}
}
// CreateCampaign creates a new campaign
func (s *CampaignService) CreateCampaign(ctx context.Context, req CreateCampaignRequest) (types.ID, error) {
const Op = "service.campaign.create_campaign"
// Business Logic: Validation
if req.Title == "" {
return 0, richerror.New(Op).WithErr(errors.New("title is required"))
}
if req.GoalAmount <= 0 {
return 0, richerror.New(Op).WithErr(errors.New("goal_amount must be greater than 0"))
}
if req.AdminID == 0 {
return 0, richerror.New(Op).WithErr(errors.New("admin_id is required"))
}
// Business Logic: Validate status
validStatuses := map[string]bool{
"draft": true,
"active": true,
"completed": true,
"cancelled": true,
}
if !validStatuses[req.Status] {
return 0, richerror.New(Op).WithErr(errors.New("invalid status"))
}
// Create campaign entity
campaign := entity.Campaign{
Title: req.Title,
Description: req.Description,
GoalAmount: req.GoalAmount,
RaisedAmount: 0, // Initially 0
Status: entity.CampaignStatus(req.Status),
DeadlineAt: req.DeadlineAt,
AdminID: req.AdminID,
CreatedAt: time.Now(),
}
// Call repository
campaignID, err := s.repo.CreateCampaign(ctx, campaign)
if err != nil {
return 0, richerror.New(Op).WithErr(err)
}
return campaignID, nil
}

14
go.mod
View File

@ -18,7 +18,7 @@ require (
github.com/stretchr/testify v1.11.1 github.com/stretchr/testify v1.11.1
github.com/swaggo/echo-swagger v1.5.2 github.com/swaggo/echo-swagger v1.5.2
github.com/swaggo/swag v1.16.6 github.com/swaggo/swag v1.16.6
golang.org/x/crypto v0.46.0 golang.org/x/crypto v0.48.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/natefinch/lumberjack.v2 v2.2.1
) )
@ -77,13 +77,13 @@ require (
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect
go.uber.org/atomic v1.11.0 // indirect go.uber.org/atomic v1.11.0 // indirect
golang.org/x/mod v0.30.0 // indirect golang.org/x/mod v0.33.0 // indirect
golang.org/x/net v0.48.0 // indirect golang.org/x/net v0.50.0 // indirect
golang.org/x/sync v0.19.0 // indirect golang.org/x/sync v0.20.0 // indirect
golang.org/x/sys v0.39.0 // indirect golang.org/x/sys v0.41.0 // indirect
golang.org/x/text v0.32.0 // indirect golang.org/x/text v0.35.0 // indirect
golang.org/x/time v0.14.0 // indirect golang.org/x/time v0.14.0 // indirect
golang.org/x/tools v0.39.0 // indirect golang.org/x/tools v0.42.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect sigs.k8s.io/yaml v1.3.0 // indirect

28
go.sum
View File

@ -404,8 +404,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
@ -415,8 +415,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk= golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc= golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -434,8 +434,8 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -448,8 +448,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -483,8 +483,8 @@ golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
@ -492,8 +492,8 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
@ -508,8 +508,8 @@ golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapK
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ= golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ= golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -43,7 +43,7 @@ func Config() config.Config {
} }
func MariaDB(cfg config.Config) *mysql.DB { func MariaDB(cfg config.Config) *mysql.DB {
migrate := flag.Bool("migrate", false, "perform database migration") migrate := flag.Bool("migrate", false, "perform mysql migration")
flag.Parse() flag.Parse()
if *migrate { if *migrate {
migrator.New(migrator.Config{ migrator.New(migrator.Config{

View File

@ -1 +1,37 @@
package patientapp package patientapp
import (
"git.gocasts.ir/ebhomengo/niki/patientapp/config"
"git.gocasts.ir/ebhomengo/niki/patientapp/delivery/http/analytic"
"git.gocasts.ir/ebhomengo/niki/patientapp/repository/mysql"
"github.com/labstack/echo/v4"
)
type Application struct {
//Config Config
HTTPServer *config.EchoServer
DB *mysql.DataBase
}
func Setup(cfg config.Config, conn *mysql.DataBase) Application {
e := echo.New()
server := config.EchoServer{
Router: e,
Config: cfg,
}
return Application{
//Config: config,
HTTPServer: &server,
DB: conn,
}
}
func (a Application) Start() {
server := analytic.NewServer(a.HTTPServer)
_ = server.Serve()
}

26
patientapp/cmd/main.go Normal file
View File

@ -0,0 +1,26 @@
package main
import (
"time"
"git.gocasts.ir/ebhomengo/niki/patientapp"
"git.gocasts.ir/ebhomengo/niki/patientapp/config"
"git.gocasts.ir/ebhomengo/niki/patientapp/repository/mysql"
)
func main() {
db := mysql.DataBase{}
cfg := config.Config{
Port: 8080,
Cors: config.Cors{
AllowOrigins: []string{"*"},
},
ShutDownCtxTimeout: 5 * time.Second,
}
app := patientapp.Setup(cfg, &db)
app.Start()
}

View File

@ -0,0 +1,22 @@
package config
import (
"time"
"github.com/labstack/echo/v4"
)
type Config struct {
Port int `koanf:"port"`
Cors Cors `koanf:"cors"`
ShutDownCtxTimeout time.Duration `koanf:"shutdown_context_timeout"`
}
type Cors struct {
AllowOrigins []string `koanf:"allow_origins"`
}
type EchoServer struct {
Router *echo.Echo
Config Config
}

View File

@ -0,0 +1,65 @@
package analytic
import (
"net/http"
svc "git.gocasts.ir/ebhomengo/niki/patientapp/service/analytic"
richerror "git.gocasts.ir/ebhomengo/niki/pkg/rich_error"
"github.com/labstack/echo/v4"
)
type Handler struct {
service svc.Service
}
func NewHandler(service svc.Service) *Handler {
return &Handler{
service: service,
}
}
func (h *Handler) Health(e echo.Context) error {
return e.JSON(http.StatusOK, map[string]interface{}{"status": "ok"})
}
func (h *Handler) PatientsAnalytic(e echo.Context) error {
var req svc.ListPatientAnalyticRequest
richErr := richerror.New(richerror.Op("fetchingPatientList.PatientsAnalytic"))
if err := e.Bind(&req); err != nil {
richErr = richErr.WithErr(err)
richErr = richErr.WithKind(1)
return echo.NewHTTPError(http.StatusBadRequest, richErr)
}
response, err := h.service.List(e.Request().Context(), req)
if err != nil {
richErr = richErr.WithErr(err)
richErr = richErr.WithKind(4)
return echo.NewHTTPError(http.StatusBadRequest, richErr)
}
return e.JSON(http.StatusOK, response)
}
func (h *Handler) PatientsMapSummary(e echo.Context) error {
richErr := richerror.New(richerror.Op("fetchingPatientMapSummary.PatientsMapSummary"))
var req svc.GetPatientMapSummaryRequest
if err := e.Bind(&req); err != nil {
richErr = richErr.WithErr(err)
richErr = richErr.WithKind(1)
return echo.NewHTTPError(http.StatusBadRequest, richErr)
}
resp, svcErr := h.service.GetMapSummary(e.Request().Context(), req)
if svcErr != nil {
richErr = richErr.WithErr(svcErr)
richErr = richErr.WithKind(4)
return echo.NewHTTPError(http.StatusBadRequest, richErr)
}
return e.JSON(http.StatusOK, resp)
}

View File

@ -0,0 +1,22 @@
package analytic
import (
"git.gocasts.ir/ebhomengo/niki/patientapp/repository/mysql"
analytic2 "git.gocasts.ir/ebhomengo/niki/patientapp/service/analytic"
"github.com/labstack/echo/v4"
)
func NewPatientAnalyticRouter(s *echo.Group) {
mysqlRepo := mysql.NewPatientRepo()
//rpcRepo := grpc.NewPatientRepo()
analyticService := analytic2.NewPatientAnalyticService(mysqlRepo)
h := NewHandler(analyticService)
s.GET("/patients", h.PatientsAnalytic)
s.GET("/patients-summary", h.PatientsMapSummary)
s.GET("/health", h.Health)
}

View File

@ -0,0 +1,41 @@
package analytic
import (
"context"
"fmt"
"git.gocasts.ir/ebhomengo/niki/patientapp/config"
)
type Server struct {
HTTPServer *config.EchoServer
}
func NewServer(server *config.EchoServer) *Server {
return &Server{
HTTPServer: server,
}
}
func (s Server) Serve() error {
s.RegisterRoutes()
// Start server
return s.HTTPServer.Router.Start(fmt.Sprintf(":%d", s.HTTPServer.Config.Port))
}
func (s Server) Stop(ctx context.Context) error {
return s.HTTPServer.Router.Shutdown(ctx)
}
func (s Server) RegisterRoutes() {
v1 := s.HTTPServer.Router.Group("/v1")
{
// Analytic Group
analyticGroup := v1.Group("/analytic")
NewPatientAnalyticRouter(analyticGroup)
}
}

View File

@ -0,0 +1,35 @@
package grpc
import (
"context"
"git.gocasts.ir/ebhomengo/niki/patientapp/service/analytic"
"git.gocasts.ir/ebhomengo/niki/patientapp/service/entity"
)
type AnalyticRepository struct{}
func NewPatientRepo() *AnalyticRepository {
return &AnalyticRepository{}
}
func (db *AnalyticRepository) GetPatients(ctx context.Context, f analytic.PatientFilter) ([]entity.Patient, error) {
return nil, nil
}
func (db *AnalyticRepository) CountPatients(ctx context.Context, f analytic.PatientFilter) (int, error) {
return 0, nil
}
func (db *AnalyticRepository) SummaryByCity(ctx context.Context, provinceID uint, f analytic.PatientMapFilter) (map[uint][]entity.MapSummaryItem, error) {
return nil, nil
}
func (db *AnalyticRepository) SummaryByProvince(ctx context.Context, f analytic.PatientMapFilter) (map[uint][]entity.MapSummaryItem, error) {
return nil, nil
}

View File

@ -0,0 +1,36 @@
package mysql
import (
"context"
"git.gocasts.ir/ebhomengo/niki/patientapp/service/analytic"
"git.gocasts.ir/ebhomengo/niki/patientapp/service/entity"
)
type DataBase struct{}
func NewPatientRepo() *DataBase {
return &DataBase{}
}
func (db *DataBase) GetPatients(ctx context.Context, f analytic.PatientFilter) ([]entity.Patient, error) {
return nil, nil
}
func (db *DataBase) CountPatients(ctx context.Context, f analytic.PatientFilter) (int, error) {
return 0, nil
}
func (db *DataBase) SummaryByCity(ctx context.Context, provinceID uint, f analytic.PatientMapFilter) (map[uint][]entity.MapSummaryItem, error) {
return nil, nil
}
func (db *DataBase) SummaryByProvince(ctx context.Context, f analytic.PatientMapFilter) (map[uint][]entity.MapSummaryItem, error) {
return nil, nil
}

View File

@ -0,0 +1,43 @@
package analytic
import (
"fmt"
"github.com/jalaali/go-jalaali"
"time"
)
func normalizeLimitOffset(limit, offset int) (int, int) {
if limit <= 0 {
limit = 50
}
if limit > 100 {
limit = 100
}
if offset < 0 {
offset = 0
}
return limit, offset
}
// convert age range -> DOB range
func ageRangeToDOB(minAge, maxAge *int, now time.Time) (dobFrom, dobTo *string) {
if maxAge != nil {
t := now.AddDate(-(*maxAge + 1), 0, 1)
jy, jm, jd, err := jalaali.ToJalaali(t.Year(), t.Month(), t.Day())
if err != nil {
}
s := fmt.Sprintf("%04d/%02d/%02d", jy, jm, jd)
dobFrom = &s
}
if minAge != nil {
t := now.AddDate(-*minAge, 0, 0)
jy, jm, jd, err := jalaali.ToJalaali(t.Year(), t.Month(), t.Day())
if err != nil {
}
s := fmt.Sprintf("%04d/%02d/%02d", jy, jm, jd)
dobTo = &s
}
return
}

View File

@ -0,0 +1,71 @@
package analytic
import (
"git.gocasts.ir/ebhomengo/niki/patientapp/service/entity"
)
type ListPatientAnalyticRequest struct {
// All fields are optional
MinAge *int `query:"minAge,omitempty"`
MaxAge *int `query:"maxAge,omitempty"`
Sex *entity.Sex `query:"sex,omitempty"`
City *int64 `query:"city,omitempty"`
Province *int64 `query:"province,omitempty"`
Search *string `query:"search,omitempty"`
Pagination *Pagination `query:"pagination,omitempty"`
}
type PatientAnalyticItem struct {
ID int64 `json:"id"`
FirstName string `json:"first_name"`
LastName string `json:"Last_name"`
DateOfBirth string `json:"dob,omitempty"`
Sex entity.Sex `json:"sex"`
Phone string `json:"phone"`
Address entity.Address `json:"address"`
}
type PatientAnalyticResponse struct {
Items []PatientAnalyticItem `json:"items"`
Pagination *Pagination `json:"pagination"`
Total int `json:"total"`
}
func ToPatientResponse(patient entity.Patient) PatientAnalyticItem {
return PatientAnalyticItem{
ID: patient.ID,
FirstName: patient.FirstName,
LastName: patient.LastName,
DateOfBirth: patient.DateOfBirth,
Sex: patient.Sex,
Phone: patient.Phone,
Address: entity.Address{
ProvinceID: patient.Address.ProvinceID,
CityID: patient.Address.CityID,
},
}
}
// GetPatientMapSummaryRequest =========================== Map ==================================
type GetPatientMapSummaryRequest struct {
Level entity.MapLevel `query:"level"`
ParentID *int `query:"parentID"`
MinAge *int `query:"minAge,omitempty"`
MaxAge *int `query:"maxAge,omitempty"`
Sex *entity.Sex `query:"sex,omitempty"`
Search *string `query:"search,omitempty"`
}
type GetPatientMapSummaryResponse struct {
Level entity.MapLevel `json:"level"`
Items map[uint][]entity.MapSummaryItem `json:"items"`
}
// Pagination ================================ Pagination =============================
type Pagination struct {
Limit int `query:"limit,omitempty"`
Offset int `query:"offset,omitempty"`
}

View File

@ -0,0 +1,27 @@
package analytic
import (
"git.gocasts.ir/ebhomengo/niki/patientapp/service/entity"
)
type PatientFilter struct {
DOBFrom *string // born after
DOBTo *string // born before
Sex *entity.Sex
City *int64
Province *int64
Country *int64
Search *string
Limit int
Offset int
}
type PatientMapFilter struct {
MinDOB *string
MaxDOB *string
Sex *entity.Sex
Search *string
}

View File

@ -0,0 +1,119 @@
package analytic
import (
"context"
"errors"
"fmt"
"time"
"git.gocasts.ir/ebhomengo/niki/patientapp/service/entity"
)
var (
ErrInvalidProvinceID = errors.New("invalid province id")
ErrInvalidCountryID = errors.New("invalid country id")
ErrInvalidMapLevel = errors.New("invalid map level")
)
type Repository interface {
GetPatients(ctx context.Context, f PatientFilter) ([]entity.Patient, error)
CountPatients(ctx context.Context, f PatientFilter) (int, error)
SummaryByCity(ctx context.Context, provinceID uint, f PatientMapFilter) (map[uint][]entity.MapSummaryItem, error)
SummaryByProvince(ctx context.Context, f PatientMapFilter) (map[uint][]entity.MapSummaryItem, error)
}
type Service struct {
repository Repository
}
func NewPatientAnalyticService(repo Repository) Service {
return Service{
repository: repo,
}
}
func (s Service) List(ctx context.Context, req ListPatientAnalyticRequest) (PatientAnalyticResponse, error) {
limit, offset := normalizeLimitOffset(req.Pagination.Limit, req.Pagination.Offset)
// convert age range
dobFrom, dobTo := ageRangeToDOB(req.MinAge, req.MaxAge, time.Now())
filter := PatientFilter{
DOBFrom: dobFrom,
DOBTo: dobTo,
Sex: req.Sex,
City: req.City,
Province: req.Province,
Search: req.Search,
Limit: limit,
Offset: offset,
}
items, err := s.repository.GetPatients(ctx, filter)
if err != nil {
return PatientAnalyticResponse{}, fmt.Errorf("GetPatients: %w", err)
}
total, err := s.repository.CountPatients(ctx, filter)
if err != nil {
return PatientAnalyticResponse{}, fmt.Errorf("CountPatients: %w", err)
}
// mapping response
out := make([]PatientAnalyticItem, 0, len(items))
for _, value := range items {
out = append(out, ToPatientResponse(value))
}
return PatientAnalyticResponse{
Items: out,
Pagination: &Pagination{
Limit: limit,
Offset: offset,
},
Total: total,
}, nil
}
func (s Service) GetMapSummary(ctx context.Context, req GetPatientMapSummaryRequest) (GetPatientMapSummaryResponse, error) {
dobFrom, dobTo := ageRangeToDOB(req.MinAge, req.MaxAge, time.Now())
filter := PatientMapFilter{
MinDOB: dobFrom,
MaxDOB: dobTo,
Sex: req.Sex,
Search: req.Search,
}
switch req.Level {
case entity.MapLevelCity:
if req.ParentID == nil || *req.ParentID <= 0 {
return GetPatientMapSummaryResponse{}, ErrInvalidProvinceID
}
items, err := s.repository.SummaryByCity(ctx, uint(*req.ParentID), filter)
if err != nil {
return GetPatientMapSummaryResponse{}, fmt.Errorf("SummaryByCity: %w", err)
}
return GetPatientMapSummaryResponse{Level: req.Level, Items: items}, nil
case entity.MapLevelProvince:
if req.ParentID == nil || *req.ParentID <= 0 {
return GetPatientMapSummaryResponse{}, ErrInvalidCountryID
}
items, err := s.repository.SummaryByProvince(ctx, filter)
if err != nil {
return GetPatientMapSummaryResponse{}, fmt.Errorf("SummaryByProvince: %w", err)
}
return GetPatientMapSummaryResponse{Level: req.Level, Items: items}, nil
default:
return GetPatientMapSummaryResponse{}, ErrInvalidMapLevel
}
}

View File

@ -0,0 +1,28 @@
package entity
type Address struct {
ID uint
PostalCode string
Address string
Name string
Lat float64
Lon float64
CityID uint
ProvinceID uint
}
type AddressAggregated struct {
Address Address
Province Province
City City
}
type Province struct {
ID uint
Name string
}
type City struct {
ID uint
Name string
ProvinceID uint
}

View File

@ -0,0 +1,17 @@
package entity
type MapLevel string
const (
MapLevelCity MapLevel = "city"
MapLevelProvince MapLevel = "province"
MapLevelCountry MapLevel = "country"
)
type MapSummaryItem struct {
LocationID int64
Name string
Count int
CentroidLat float64
CentroidLng float64
}

View File

@ -0,0 +1,62 @@
package entity
type Patient struct {
ID int64
FirstName string
LastName string
DateOfBirth string
Sex Sex
Phone string
Address Address
CaseStatus CaseStatus
ReferralSource ReferralSource
AssignedStaffId int64
StartDate string
EndDate string
}
// Sex ================================== Sex type ==========================================
type Sex string
const (
SexUnknown Sex = "unknown"
SexMale Sex = "male"
SexFemale Sex = "female"
SexOther Sex = "other"
)
func (s Sex) SexValidation() bool {
switch s {
case SexUnknown, SexMale, SexFemale, SexOther:
return true
default:
return false
}
}
// CaseStatus =================================== Case Status =======================================
type CaseStatus string
const (
Open CaseStatus = "open"
Close CaseStatus = "close"
InProgress CaseStatus = "inProgress"
)
func (s CaseStatus) CaseStatusValidation() bool {
switch s {
case Open, Close, InProgress:
return true
default:
return false
}
}
// ReferralSource =================================== Referral source =======================================
type ReferralSource string
const (
Hospital ReferralSource = "hospital"
Community ReferralSource = "community"
Other ReferralSource = "other"
)

View File

@ -0,0 +1,19 @@
package date_parser
import (
"fmt"
"time"
)
// ParseDate parses a date string in "YYYY-MM-DD" format and returns a time.Time object
func ParseDate(input string) (time.Time, error) {
const layout = "2006-01-02"
// Parse the input string
convertedDate, err := time.Parse(layout, input)
if err != nil {
return time.Time{}, fmt.Errorf("invalid date format: %v", err)
}
return convertedDate, nil
}

View File

@ -0,0 +1,10 @@
-- +migrate Up
CREATE TABLE `categories` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL,
`slug` VARCHAR(255) NOT NULL UNIQUE,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_persian_ci;
-- +migrate Down
DROP TABLE IF EXISTS `categories`;

View File

@ -0,0 +1,17 @@
-- +migrate Up
CREATE TABLE `products` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`name` VARCHAR(255) NOT NULL,
`slug` VARCHAR(255) NOT NULL UNIQUE,
`description` TEXT,
`price` DECIMAL(10, 2) NOT NULL,
`stock` INT DEFAULT 0,
`is_active` BOOLEAN DEFAULT TRUE,
`features` JSON DEFAULT NULL,
`created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
`deleted_at` TIMESTAMP DEFAULT NULL,
FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE SET NULL
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_persian_ci;
-- +migrate Down
DROP TABLE IF EXISTS `products`;

View File

@ -0,0 +1,11 @@
-- +migrate Up
CREATE TABLE `category_products` (
`category_id` INT NOT NULL,
`product_id` INT NOT NULL,
PRIMARY KEY (`category_id`, `product_id`),
FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE CASCADE,
FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE CASCADE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_persian_ci;
-- +migrate Down
DROP TABLE IF EXISTS `category_products`;

View File

@ -0,0 +1,11 @@
-- +migrate Up
CREATE TABLE `product_images` (
`id` INT PRIMARY KEY AUTO_INCREMENT,
`product_id` INT NOT NULL,
`image_path` VARCHAR(255) NOT NULL,
`is_primary` BOOLEAN DEFAULT FALSE,
FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE CASCADE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_persian_ci;
-- +migrate Down
DROP TABLE IF EXISTS `product_images`;

View File

@ -0,0 +1,10 @@
package category
import "time"
type Category struct {
ID uint
Name string
Slug string
CreatedAt time.Time
}

View File

@ -0,0 +1 @@
package category

View File

@ -0,0 +1 @@
package category

View File

@ -0,0 +1 @@
package category

View File

@ -1 +0,0 @@
package service

View File

@ -1 +0,0 @@
package service

View File

@ -0,0 +1,31 @@
package product
import (
"database/sql"
"time"
)
type Product struct {
ID uint
Name string
Slug string
Description string
Price float64
Stock int
IsActive bool
Features string
CreatedAt time.Time
DeletedAt sql.NullTime
}
type ProductImage struct {
ID uint
ProductID uint
ImagePath string
IsPrimary bool
}
type CategoryProduct struct {
CategoryID uint
ProductID uint
}

View File

@ -0,0 +1 @@
package product

View File

@ -0,0 +1 @@
package product

View File

@ -0,0 +1 @@
package product

View File

@ -1 +0,0 @@
package service

View File

@ -1 +0,0 @@
package service

View File

@ -1,15 +0,0 @@
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
fmt.Println(" Staffapp Server Starting...")
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Staffapp OK!")
})
log.Fatal(http.ListenAndServe(":8080", nil))
}

View File

@ -1,13 +1,64 @@
package database package database
import "git.gocasts.ir/ebhomengo/niki/repository/mysql" import (
"fmt"
"git.gocasts.ir/ebhomengo/niki/staffapp/service"
)
type DB struct { type DB struct {
conn *mysql.DB staffs []service.Staff
nextID int
} }
func New(conn *mysql.DB) *DB { func New() service.StaffRepository {
return &DB{ return &DB{
conn: conn, staffs: []service.Staff{},
nextID: 1,
} }
} }
func (d *DB) Create(staff service.Staff) (service.Staff, error) {
staff.ID = d.nextID
d.staffs = append(d.staffs, staff)
d.nextID++
return staff, nil
}
func (d *DB) Get(id int) (*service.Staff, error) {
for _, v := range d.staffs {
if id == v.ID {
return &v, nil
}
}
return nil, fmt.Errorf("The user not found with id = %d", id)
}
func (d *DB) List() ([]service.Staff, error) {
return d.staffs, nil
}
func (d *DB) Remove(id int) error {
for _, v := range d.staffs {
if id == v.ID {
d.staffs = append(d.staffs[:id], d.staffs[id+1:]...)
return nil
}
}
return fmt.Errorf("User with ID: %d notfound", id)
}
func (d *DB) Update(id int, name, lastName, phoneNumber string) (*service.Staff, error) {
for _, v := range d.staffs {
if id == v.ID {
v.Name = name
v.LastName = lastName
v.PhoneNumber = phoneNumber
return &v, nil
}
}
return nil, fmt.Errorf("User with this Id(%d)couldn't found for updating", id)
}

View File

@ -1 +0,0 @@
package service

View File

@ -1 +0,0 @@
package service

View File

@ -0,0 +1,15 @@
package service
type Staff struct {
ID int
Name string
LastName string
PhoneNumber string
}
type StaffRepository interface {
Create(staff Staff) (Staff, error)
Get(id int) (*Staff, error)
List() ([]Staff, error)
Remove(id int) error
Update(id int, name, lastName, phoneNumber string) (*Staff, error)
}

View File

@ -1 +1,44 @@
package service package service
import (
"fmt"
)
type StaffService struct {
repo StaffRepository
}
func NewStaffService(repo StaffRepository) *StaffService {
return &StaffService{
repo: repo,
}
}
func (s *StaffService) RegisterStaff(name, lastname, phone string) (Staff, error) {
newStaff := Staff{
Name: name,
LastName: lastname,
PhoneNumber: phone,
}
createdStaff, err := s.repo.Create(newStaff)
if err != nil {
return Staff{}, fmt.Errorf("Error in registring staff%w", err)
}
return createdStaff, nil
}
func (s *StaffService) Get(id int) (*Staff, error) {
return s.repo.Get(id)
}
func (s *StaffService) List() ([]Staff, error) {
return s.repo.List()
}
func (s *StaffService) Remove(id int) error {
return s.repo.Remove(id)
}
func (s *StaffService) Update(id int, name, lastName, phoneNumber string) (*Staff, error) {
return s.repo.Update(id, name, lastName, phoneNumber)
}

View File

@ -1 +0,0 @@
package service

21
vendor/github.com/jalaali/go-jalaali/LICENSE generated vendored Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2018 Amir Khazaie
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

23
vendor/github.com/jalaali/go-jalaali/README.md generated vendored Normal file
View File

@ -0,0 +1,23 @@
# Jalaali
Golang implementation of [Jalaali JS](https://github.com/jalaali/jalaali-js) and [Jalaali Python](https://github.com/jalaali/jalaali-python) implementations of Jalaali (Jalali, Persian, Khayyami, Khorshidi, Shamsi) convertion to Gregorian calendar system and vice-versa.
This implementation is based on an [algorithm by Kazimierz M. Borkowski](http://www.astro.uni.torun.pl/~kb/Papers/EMP/PersianC-EMP.htm). Borkowski claims that this algorithm works correctly for 3000 years!
Documentation on API is available [here](https://pkg.go.dev/github.com/jalaali/go-jalaali) at Go official documentation site.
## Installation
Use `go get` on this repository:
```sh
$ go get -u github.com/jalaali/go-jalaali
```
## Usage
* Wrapper around Golang [time package](https://golang.org/pkg/time):
* Call `Jalaali.Now()` to get instance of current time. You can use all function from `time` package with this wrapper.
* Call `Jalaali.From(t)` and pass a `time` instance to it. The you can work with it the same way you work with `time` package.
* Jalaali Formatting:
* Call `JFormat` method of a Jalaali instance and pass it the same formatting options that is used for Golang `time` package. The output will be in Jalaali date and use persian digits and words.

147
vendor/github.com/jalaali/go-jalaali/convertion.go generated vendored Normal file
View File

@ -0,0 +1,147 @@
package jalaali
import (
"fmt"
"time"
)
var (
breaks = [...]int{-61, 9, 38, 199, 426, 686, 756, 818, 1111, 1181, 1210,
1635, 2060, 2097, 2192, 2262, 2324, 2394, 2456, 3178}
)
// ToJalaali converts Gregorian to Jalaali date. Error is not nil if Jalaali
// year passed to function is not valid.
func ToJalaali(gregorianYear int, gregorianMonth time.Month, gregorianDay int) (int, Month, int, error) {
jy, jm, jd, err := d2j(g2d(gregorianYear, int(gregorianMonth), gregorianDay))
return jy, Month(jm), jd, err
}
// ToGregorian converts Jalaali to Gregorian date. Error is not nil if Jalaali
// year passed to function is not valid.
func ToGregorian(jalaaliYear int, jalaaliMonth Month, jalaaliDay int) (int, time.Month, int, error) {
// validate the jalaali date using the utility function
if !IsValidDate(jalaaliYear, int(jalaaliMonth), jalaaliDay) {
return 0, 0, 0, fmt.Errorf("invalid jalaali date: year=%d, month=%d, day=%d", jalaaliYear, jalaaliMonth, jalaaliDay)
}
jdn, err := j2d(jalaaliYear, int(jalaaliMonth), jalaaliDay)
if err != nil {
return 0, 0, 0, err
}
gy, gm, gd := d2g(jdn)
return gy, time.Month(gm), gd, nil
}
func j2d(jy, jm, jd int) (jdn int, err error) {
_, gy, march, err := jalCal(jy)
if err != nil {
return 0, err
}
return g2d(gy, 3, march) + (jm-1)*31 - div(jm, 7)*(jm-7) + jd - 1, nil
}
func d2j(jdn int) (int, int, int, error) {
gy, _, _ := d2g(jdn) // Calculate Gregorian year (gy).
jy := gy - 621
leap, _, march, err := jalCal(jy)
jdn1f := g2d(gy, 3, march)
if err != nil {
return 0, 0, 0, err
}
// Find number of days that passed since 1 Farvardin.
k := jdn - jdn1f
if k >= 0 {
if k <= 185 {
// The first 6 months.
jm := 1 + div(k, 31)
jd := mod(k, 31) + 1
return jy, jm, jd, nil
}
// The remaining months.
k -= 186
} else {
// Previous Jalaali year.
jy--
k += 179
if leap == 1 {
k++
}
}
jm := 7 + div(k, 30)
jd := mod(k, 30) + 1
return jy, jm, jd, nil
}
func jalCal(jy int) (int, int, int, error) {
bl, gy, leapJ, jp := len(breaks), jy+621, -14, breaks[0]
jump := 0
if jy < jp || jy >= breaks[bl-1] {
return 0, 0, 0, &ErrorInvalidYear{jy}
}
// Find the limiting years for the Jalaali year jy.
for i := 1; i < bl; i++ {
jm := breaks[i]
jump = jm - jp
if jy < jm {
break
}
leapJ += div(jump, 33)*8 + div(mod(jump, 33), 4)
jp = jm
}
n := jy - jp
// Find the number of leap years from AD 621 to the beginning
// of the current Jalaali year in the Persian calendar.
leapJ += div(n, 33)*8 + div(mod(n, 33)+3, 4)
if mod(jump, 33) == 4 && jump-n == 4 {
leapJ++
}
// And the same in the Gregorian calendar (until the year gy).
leapG := div(gy, 4) - div((div(gy, 100)+1)*3, 4) - 150
// Determine the Gregorian date of Farvardin the 1st.
march := 20 + leapJ - leapG
// Find how many years have passed since the last leap year.
if jump-n < 6 {
n -= jump + div(jump+4, 33)*33
}
leap := mod(mod(n+1, 33)-1, 4)
if leap == -1 {
leap = 4
}
return leap, gy, march, nil
}
func g2d(gy, gm, gd int) int {
d := div((gy+div(gm-8, 6)+100100)*1461, 4) +
div(153*mod(gm+9, 12)+2, 5) +
gd - 34840408
d = d - div(div(gy+100100+div(gm-8, 6), 100)*3, 4) + 752
return d
}
func d2g(jdn int) (int, int, int) {
j := 4*jdn + 139361631
j = j + div(div(4*jdn+183187720, 146097)*3, 4)*4 - 3908
i := div(mod(j, 1461), 4)*5 + 308
gd := div(mod(i, 153), 5) + 1
gm := mod(div(i, 153), 12) + 1
gy := div(j, 1461) - 100100 + div(8-gm, 6)
return gy, gm, gd
}
func div(a, b int) int {
return a / b
}
func mod(a, b int) int {
return a % b
}

19
vendor/github.com/jalaali/go-jalaali/errors.go generated vendored Normal file
View File

@ -0,0 +1,19 @@
package jalaali
import "fmt"
// ErrorNilReference is happening when a pointer is nil.
type ErrorNilReference struct{}
// ErrorInvalidYear is happening when year passed is is in proper range.
type ErrorInvalidYear struct {
year int
}
func (e *ErrorNilReference) Error() string {
return "jalaali: reference is nil"
}
func (e *ErrorInvalidYear) Error() string {
return fmt.Sprintf("jalaali: %v is invalid year", e.year)
}

318
vendor/github.com/jalaali/go-jalaali/format.go generated vendored Normal file
View File

@ -0,0 +1,318 @@
package jalaali
const (
_ = iota
stdLongMonth = iota + stdNeedDate // "January"
stdMonth // "Jan"
stdNumMonth // "1"
stdZeroMonth // "01"
stdLongWeekDay // "Monday"
stdWeekDay // "Mon"
stdDay // "2"
stdUnderDay // "_2"
stdZeroDay // "02"
stdHour = iota + stdNeedClock // "15"
stdHour12 // "3"
stdZeroHour12 // "03"
stdMinute // "4"
stdZeroMinute // "04"
stdSecond // "5"
stdZeroSecond // "05"
stdLongYear = iota + stdNeedDate // "2006"
stdYear // "06"
stdPM = iota + stdNeedClock // "PM"
stdpm // "pm"
stdFracSecond0 // ".0", ".00", ... , trailing zeros included
stdFracSecond9 // ".9", ".99", ..., trailing zeros omitted
stdNeedDate = 1 << 8 // need month, day, year
stdNeedClock = 2 << 8 // need hour, minute, second
stdArgShift = 16 // extra argument in high bits, above low stdArgShift
stdMask = 1<<stdArgShift - 1 // mask out argument
)
// std0x records the std values for "01", "02", ..., "06".
var std0x = [...]int{stdZeroMonth, stdZeroDay, stdZeroHour12, stdZeroMinute, stdZeroSecond, stdYear}
// JFormat gets default Golang layout string and parse put Jalaali calender information
// into the final string and return it.
func (j Jalaali) JFormat(layout string) (string, error) {
const minBufSize = 64
bufSize := len(layout)
if bufSize < minBufSize { // minimum buffer size
bufSize = minBufSize
}
b := make([]byte, 0, len(layout))
b, err := j.jAppendFormat(b, layout)
return string(b), err
}
// jAppendFormat is like JFormat but appends the textual
// representation to b and returns the extended buffer.
func (j Jalaali) jAppendFormat(b []byte, layout string) ([]byte, error) {
var (
year int = -1
month Month
day int
hour int = -1
min int
sec int
)
// Each iteration generates one std value.
for layout != "" {
prefix, std, suffix := nextStdChunk(layout)
if prefix != "" {
b = append(b, prefix...)
}
if std == 0 {
break
}
layout = suffix
// Compute year, month, day if needed.
if year < 0 && std&stdNeedDate != 0 {
var err error
year, month, day, err = ToJalaali(j.Year(), j.Month(), j.Day())
if err != nil {
return b, err
}
}
// Compute hour, minute, second if needed.
if hour < 0 && std&stdNeedClock != 0 {
hour, min, sec = j.Hour(), j.Minute(), j.Second()
}
switch std & stdMask {
case stdYear:
y := year
if y < 0 {
y = -y
}
b = appendInt(b, y%100, 2)
case stdLongYear:
b = appendInt(b, year, 4)
case stdMonth, stdLongMonth:
m := month.String()
b = append(b, m...)
case stdNumMonth:
b = appendInt(b, int(month), 0)
case stdZeroMonth:
b = appendInt(b, int(month), 2)
case stdWeekDay, stdLongWeekDay:
s := Weekday((int(j.Weekday()) + 1) % 7).String()
b = append(b, s...)
case stdDay:
b = appendInt(b, day, 0)
case stdUnderDay:
if day < 10 {
b = append(b, ' ')
}
b = appendInt(b, day, 0)
case stdZeroDay:
b = appendInt(b, day, 2)
case stdHour:
b = appendInt(b, hour, 2)
case stdHour12:
// Noon is 12PM, midnight is 12AM.
hr := hour % 12
if hr == 0 {
hr = 12
}
b = appendInt(b, hr, 0)
case stdZeroHour12:
// Noon is 12PM, midnight is 12AM.
hr := hour % 12
if hr == 0 {
hr = 12
}
b = appendInt(b, hr, 2)
case stdMinute:
b = appendInt(b, min, 0)
case stdZeroMinute:
b = appendInt(b, min, 2)
case stdSecond:
b = appendInt(b, sec, 0)
case stdZeroSecond:
b = appendInt(b, sec, 2)
case stdPM, stdpm:
if hour >= 12 {
b = append(b, "بعدازظهر"...)
} else {
b = append(b, "قبل‌ازظهر"...)
}
case stdFracSecond0, stdFracSecond9:
b = formatNano(b, uint(j.Nanosecond()), std>>stdArgShift, std&stdMask == stdFracSecond9)
}
}
return b, nil
}
// nextStdChunk finds the first occurrence of a std string in
// layout and returns the text before, the std string, and the text after.
func nextStdChunk(layout string) (prefix string, std int, suffix string) {
for i := 0; i < len(layout); i++ {
switch c := int(layout[i]); c {
case 'J': // January, Jan
if len(layout) >= i+3 && layout[i:i+3] == "Jan" {
if len(layout) >= i+7 && layout[i:i+7] == "January" {
return layout[0:i], stdLongMonth, layout[i+7:]
}
if !startsWithLowerCase(layout[i+3:]) {
return layout[0:i], stdMonth, layout[i+3:]
}
}
case 'M': // Monday, Mon
if layout[i:i+3] == "Mon" {
if len(layout) >= i+6 && layout[i:i+6] == "Monday" {
return layout[0:i], stdLongWeekDay, layout[i+6:]
}
if !startsWithLowerCase(layout[i+3:]) {
return layout[0:i], stdWeekDay, layout[i+3:]
}
}
case '0': // 01, 02, 03, 04, 05, 06
if len(layout) >= i+2 && '1' <= layout[i+1] && layout[i+1] <= '6' {
return layout[0:i], std0x[layout[i+1]-'1'], layout[i+2:]
}
case '1': // 15, 1
if len(layout) >= i+2 && layout[i+1] == '5' {
return layout[0:i], stdHour, layout[i+2:]
}
return layout[0:i], stdNumMonth, layout[i+1:]
case '2': // 2006, 2
if len(layout) >= i+4 && layout[i:i+4] == "2006" {
return layout[0:i], stdLongYear, layout[i+4:]
}
return layout[0:i], stdDay, layout[i+1:]
case '_': // _2, _2006
if len(layout) >= i+2 && layout[i+1] == '2' {
//_2006 is really a literal _, followed by stdLongYear
if len(layout) >= i+5 && layout[i+1:i+5] == "2006" {
return layout[0 : i+1], stdLongYear, layout[i+5:]
}
return layout[0:i], stdUnderDay, layout[i+2:]
}
case '3':
return layout[0:i], stdHour12, layout[i+1:]
case '4':
return layout[0:i], stdMinute, layout[i+1:]
case '5':
return layout[0:i], stdSecond, layout[i+1:]
case 'P': // PM
if len(layout) >= i+2 && layout[i+1] == 'M' {
return layout[0:i], stdPM, layout[i+2:]
}
case 'p': // pm
if len(layout) >= i+2 && layout[i+1] == 'm' {
return layout[0:i], stdpm, layout[i+2:]
}
case '.': // .000 or .999 - repeated digits for fractional seconds.
if i+1 < len(layout) && (layout[i+1] == '0' || layout[i+1] == '9') {
ch := layout[i+1]
j := i + 1
for j < len(layout) && layout[j] == ch {
j++
}
// String of digits must end here - only fractional second is all digits.
if !isDigit(layout, j) {
std := stdFracSecond0
if layout[i+1] == '9' {
std = stdFracSecond9
}
std |= (j - (i + 1)) << stdArgShift
return layout[0:i], std, layout[j:]
}
}
}
}
return layout, 0, ""
}
// startsWithLowerCase reports whether the string has a lower-case letter at the beginning.
// Its purpose is to prevent matching strings like "Month" when looking for "Mon".
func startsWithLowerCase(str string) bool {
if len(str) == 0 {
return false
}
c := str[0]
return 'a' <= c && c <= 'z'
}
// isDigit reports whether s[i] is in range and is a decimal digit.
func isDigit(s string, i int) bool {
if len(s) <= i {
return false
}
c := s[i]
return '0' <= c && c <= '9'
}
// appendInt appends the decimal form of x to b and returns the result.
// If the decimal form (excluding sign) is shorter than width, the result is padded with leading 0's.
// Duplicates functionality in strconv, but avoids dependency.
func appendInt(b []byte, x int, width int) []byte {
u := uint(x)
if x < 0 {
b = append(b, '-')
u = uint(-x)
}
// Assemble decimal in reverse order.
var buf [20]rune
i := len(buf)
for u >= 10 {
i--
q := u / 10
buf[i] = rune('۰' + u - q*10)
u = q
}
i--
buf[i] = rune('۰' + u)
// Add 0-padding.
for w := len(buf) - i; w < width; w++ {
b = append(b, []byte("۰")...)
}
return append(b, []byte(string(buf[i:]))...)
}
// formatNano appends a fractional second, as nanoseconds, to b
// and returns the result.
func formatNano(b []byte, nanosec uint, n int, trim bool) []byte {
u := nanosec
var buf [9]rune
for start := len(buf); start > 0; {
start--
buf[start] = rune(u%10 + '۰')
u /= 10
}
if n > 9 {
n = 9
}
if trim {
for n > 0 && buf[n-1] == '۰' {
n--
}
if n == 0 {
return b
}
}
b = append(b, '.')
return append(b, []byte(string(buf[:n]))...)
}

3
vendor/github.com/jalaali/go-jalaali/go.mod generated vendored Normal file
View File

@ -0,0 +1,3 @@
module github.com/jalaali/go-jalaali
go 1.13

79
vendor/github.com/jalaali/go-jalaali/jalaali.go generated vendored Normal file
View File

@ -0,0 +1,79 @@
package jalaali
import (
"strconv"
"time"
)
// A simple wrapper around Golang default time package. You have all the functionality of
// default time package and functionalities needed for Jalaali calender.
type Jalaali struct {
time.Time
}
// From initialize new instance of Jalaali from a time instance.
func From(t time.Time) Jalaali {
return Jalaali{t}
}
// Now with return Jalaali instance of current time.
func Now() Jalaali {
return From(time.Now())
}
// A Month specifies a month of the year (Farvardin = 1, ...).
type Month int
const (
Farvardin Month = 1 + iota
Ordibehesht
Khordad
Tir
Mordad
Shahrivar
Mehr
Aban
Azar
Dey
Bahman
Esfand
)
var months = []string{
"فروردین", "اردیبهشت", "خرداد",
"تیر", "مرداد", "شهریور",
"مهر", "آبان", "آذر",
"دی", "بهمن", "اسفند",
}
func (m Month) String() string {
if Farvardin <= m && m <= Esfand {
return months[m-1]
}
return "%!Month(" + strconv.Itoa(int(m)) + ")"
}
// A Weekday specifies a day of the week (Shanbe = 0, ...).
type Weekday int
const (
Shanbe Weekday = iota
IekShanbe
DoShanbe
SeShanbe
ChaharShanbe
PanjShanbe
Jome
)
var days = []string{
"شنبه", "یک‌شنبه", "دوشنبه", "سه‌شنبه", "چهارشنبه", "پنج‌شنبه", "جمعه",
}
func (d Weekday) String() string {
if Shanbe <= d && d <= Jome {
return days[d]
}
return "%!Weekday(" + strconv.Itoa(int(d)) + ")"
}

188
vendor/github.com/jalaali/go-jalaali/jalaali_test.go generated vendored Normal file
View File

@ -0,0 +1,188 @@
package jalaali
import (
"testing"
"time"
)
func TestFromYMD(t *testing.T) {
tests := []struct {
gy, gm, gd, jy, jm, jd int
}{
{1981, 8, 17, 1360, 5, 26},
{2013, 1, 10, 1391, 10, 21},
{2014, 8, 4, 1393, 5, 13},
}
for _, test := range tests {
y, m, d, err := ToJalaali(test.gy, time.Month(test.gm), test.gd)
if err != nil {
t.Errorf("%v", err)
} else if y != test.jy || m != Month(test.jm) || d != test.jd {
t.Errorf("Expected %v/%v/%v got %v/%v%v.", test.jy, test.jm, test.jd, y, m, d)
}
}
}
func TestToGregorian(t *testing.T) {
tests := []struct {
jy, jm, jd, gy, gm, gd int
}{
{1360, 5, 26, 1981, 8, 17},
{1391, 10, 21, 2013, 1, 10},
{1393, 5, 13, 2014, 8, 4},
}
for _, test := range tests {
y, m, d, err := ToGregorian(test.jy, Month(test.jm), test.jd)
if err != nil {
t.Errorf("%v", err)
} else if y != test.gy || m != time.Month(test.gm) || d != test.gd {
t.Errorf("Expected %v/%v/%v got %v/%v%v.", test.gy, test.gm, test.gd, y, m, d)
}
}
}
func TestIsValidDate(t *testing.T) {
tests := []struct {
y, m, d int
ok bool
}{
{-62, 12, 29, false},
{-61, 1, 1, true},
{3178, 1, 1, false},
{3177, 12, 29, true},
{1393, 0, 1, false},
{1393, 13, 1, false},
{1393, 1, 0, false},
{1393, 1, 32, false},
{1393, 1, 31, true},
{1393, 11, 31, false},
{1393, 11, 30, true},
{1393, 12, 30, false},
{1393, 12, 29, true},
{1395, 12, 30, true},
}
for _, test := range tests {
valid := IsValidDate(test.y, test.m, test.d)
if valid != test.ok {
calculated, actual := "", " not"
if test.ok {
calculated, actual = " not", ""
}
t.Errorf("%v/%v/%v is%v valid date but considered%v valid.",
test.y, test.m, test.d, actual, calculated)
}
}
}
func TestIsLeapYear(t *testing.T) {
tests := []struct {
year int
leap bool
}{
{1393, false},
{1394, false},
{1395, true},
{1396, false},
}
for _, test := range tests {
leap, err := IsLeapYear(test.year)
if err != nil {
t.Errorf("%v", err)
} else if leap != test.leap {
calculated, actual := "", " not"
if leap {
calculated, actual = " not", ""
}
t.Errorf("%v is%v leap but considered%v leap.", test.year, actual, calculated)
}
}
}
func TestMonthLength(t *testing.T) {
tests := []struct {
y, m, ml int
}{
{1393, 1, 31},
{1393, 4, 31},
{1393, 6, 31},
{1393, 7, 30},
{1393, 10, 30},
{1393, 12, 29},
{1394, 12, 29},
{1395, 12, 30},
}
for _, test := range tests {
calculated, err := MonthLength(test.y, test.m)
if err != nil {
t.Errorf("%v", err)
} else if calculated != test.ml {
t.Errorf("Length of %v/%v month is %v but considered %v.",
test.y, test.m, test.ml, calculated)
}
}
}
func TestJFormat(t *testing.T) {
iran, _ := time.LoadLocation("Asia/Tehran")
tests := []struct {
time time.Time
format []string
result []string
}{
{
time.Date(2001, 1, 1, 1, 1, 1, 1, iran),
[]string{
"2006 06", // Year formatting
"January Jan 1 01", // Month formatting
"Monday Mon 2 _2 02", // Day formatting
"15 3 03 4 04 5 05 PM pm", // Hour, Minute, Second formatting
".0 .00 .000 .000000 .000000000 .9 .99 .999 .999999 .999999999", // Nanosecond formatting
},
[]string{
"۱۳۷۹ ۷۹", // Year formatting
"دی دی ۱۰ ۱۰", // Month formatting
"دوشنبه دوشنبه ۱۲ ۱۲ ۱۲", // Day formatting
"۰۱ ۱ ۰۱ ۱ ۰۱ ۱ ۰۱ قبل‌ازظهر قبل‌ازظهر", // Hour, Minute, Second formatting
".۰ .۰۰ .۰۰۰ .۰۰۰۰۰۰ .۰۰۰۰۰۰۰۰۱ .۰۰۰۰۰۰۰۰۱", // Nanosecond formatting
},
}, {
time.Date(2001, 2, 3, 15, 17, 1, 999999999, iran),
[]string{
"2006 06", // Year formatting
"January Jan 1 01", // Month formatting
"Monday Mon 2 _2 02", // Day formatting
"15 3 03 4 04 5 05 PM pm", // Hour, Minute, Second formatting
".0 .00 .000 .000000 .000000000 .9 .99 .999 .999999 .999999999", // Nanosecond formatting
},
[]string{
"۱۳۷۹ ۷۹", // Year formatting
"بهمن بهمن ۱۱ ۱۱", // Month formatting
"شنبه شنبه ۱۵ ۱۵ ۱۵", // Day formatting
"۱۵ ۳ ۰۳ ۱۷ ۱۷ ۱ ۰۱ بعدازظهر بعدازظهر", // Hour, Minute, Second formatting
".۹ .۹۹ .۹۹۹ .۹۹۹۹۹۹ .۹۹۹۹۹۹۹۹۹ .۹ .۹۹ .۹۹۹ .۹۹۹۹۹۹ .۹۹۹۹۹۹۹۹۹", // Nanosecond formatting
},
},
}
for i, test := range tests {
j := From(test.time)
for f := range test.format {
result, err := j.JFormat(test.format[f])
if err != nil {
t.Error(err)
}
if result != test.result[f] {
t.Error("Bad formatting for test as index: ", i, "\nWanted: ", test.result[f], "\nGot: ", result)
}
}
}
}

53
vendor/github.com/jalaali/go-jalaali/utils.go generated vendored Normal file
View File

@ -0,0 +1,53 @@
package jalaali
import "strings"
var enToFa = strings.NewReplacer(
"0", "۰",
"1", "۱",
"2", "۲",
"3", "۳",
"4", "۴",
"5", "۵",
"6", "۶",
"7", "۷",
"8", "۸",
"9", "۹",
)
// IsValidDate take Jalaali date and return true if it is valid,
// otherwise false.
func IsValidDate(jy, jm, jd int) bool {
d, err := MonthLength(jy, jm)
if err != nil {
return false
}
return -61 <= jy && jy <= 3177 &&
1 <= jm && jm <= 12 &&
1 <= jd && jd <= d
}
// MonthLength take Jalaali date and return length of that specific
// month. Error is not nil if Jalaali year passed to function is not valid.
func MonthLength(jy, jm int) (int, error) {
if jm <= 6 {
return 31, nil
} else if jm <= 11 {
return 30, nil
}
leap, err := IsLeapYear(jy)
if err != nil {
return 0, err
} else if leap {
return 30, nil
}
return 29, nil
}
// IsLeapYear take a Jalaali year and return true if it is leap year. Error
// is not nil if Jalaali year passed to function is not valid.
func IsLeapYear(jy int) (bool, error) {
leap, _, _, err := jalCal(jy)
return leap == 0, err
}

12
vendor/github.com/spf13/cobra/.github/dependabot.yml generated vendored Normal file
View File

@ -0,0 +1,12 @@
version: 2
updates:
- package-ecosystem: gomod
directory: /
schedule:
interval: weekly
open-pull-requests-limit: 99
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
open-pull-requests-limit: 99

24
vendor/github.com/spf13/cobra/.github/labeler.yml generated vendored Normal file
View File

@ -0,0 +1,24 @@
# changes to documentation generation
"area/docs-generation":
- changed-files:
- any-glob-to-any-file: 'doc/**'
# changes to the core cobra command
"area/cobra-command":
- changed-files:
- any-glob-to-any-file: ['./cobra.go', './cobra_test.go', './*command*.go']
# changes made to command flags/args
"area/flags":
- changed-files:
- any-glob-to-any-file: './args*.go'
# changes to Github workflows
"area/github":
- changed-files:
- any-glob-to-any-file: '.github/**'
# changes to shell completions
"area/shell-completion":
- changed-files:
- any-glob-to-any-file: './*completions*'

View File

@ -0,0 +1,18 @@
name: "Pull Request Labeler"
on:
- pull_request_target
permissions:
contents: read
jobs:
triage:
permissions:
contents: read # for actions/labeler to determine modified files
pull-requests: write # for actions/labeler to add labels to PRs
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v5
with:
repo-token: "${{ github.token }}"

View File

@ -0,0 +1,125 @@
name: Test
on:
push:
pull_request:
workflow_dispatch:
env:
GO111MODULE: on
permissions:
contents: read
jobs:
lic-headers:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: >-
docker run
-v $(pwd):/wrk -w /wrk
ghcr.io/google/addlicense
-c 'The Cobra Authors'
-y '2013-2023'
-l apache
-ignore '.github/**'
-check
.
golangci-lint:
permissions:
contents: read # for actions/checkout to fetch code
pull-requests: read # for golangci/golangci-lint-action to fetch pull requests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '^1.22'
check-latest: true
cache: true
- uses: golangci/golangci-lint-action@v8.0.0
with:
version: latest
args: --verbose
test-unix:
strategy:
fail-fast: false
matrix:
platform:
- ubuntu
- macOS
go:
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
name: '${{ matrix.platform }} | 1.${{ matrix.go }}.x'
runs-on: ${{ matrix.platform }}-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: 1.${{ matrix.go }}.x
cache: true
- run: |
export GOBIN=$HOME/go/bin
go install github.com/kyoh86/richgo@latest
go install github.com/mitchellh/gox@latest
- run: RICHGO_FORCE_COLOR=1 PATH=$HOME/go/bin/:$PATH make richtest
test-win:
name: MINGW64
defaults:
run:
shell: msys2 {0}
runs-on: windows-latest
steps:
- shell: bash
run: git config --global core.autocrlf input
- uses: msys2/setup-msys2@v2
with:
msystem: MINGW64
update: true
install: >
git
make
unzip
mingw-w64-x86_64-go
- uses: actions/checkout@v4
- uses: actions/cache@v4
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-${{ matrix.go }}-${{ hashFiles('**/go.sum') }}
restore-keys: ${{ runner.os }}-${{ matrix.go }}-
- run: |
export GOBIN=$HOME/go/bin
go install github.com/kyoh86/richgo@latest
go install github.com/mitchellh/gox@latest
- run: RICHGO_FORCE_COLOR=1 PATH=$HOME/go/bin:$PATH make richtest

39
vendor/github.com/spf13/cobra/.gitignore generated vendored Normal file
View File

@ -0,0 +1,39 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
# Vim files https://github.com/github/gitignore/blob/master/Global/Vim.gitignore
# swap
[._]*.s[a-w][a-z]
[._]s[a-w][a-z]
# session
Session.vim
# temporary
.netrwhist
*~
# auto-generated tag files
tags
*.exe
cobra.test
bin
.idea/
*.iml

59
vendor/github.com/spf13/cobra/.golangci.yml generated vendored Normal file
View File

@ -0,0 +1,59 @@
# Copyright 2013-2023 The Cobra Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
version: "2"
run:
timeout: 5m
formatters:
enable:
- gofmt
- goimports
linters:
default: none
enable:
#- bodyclose
#- depguard
#- dogsled
#- dupl
- errcheck
#- exhaustive
#- funlen
#- gochecknoinits
- goconst
- gocritic
#- gocyclo
#- goprintffuncname
- gosec
- govet
- ineffassign
#- lll
- misspell
#- mnd
#- nakedret
#- noctx
- nolintlint
#- rowserrcheck
- staticcheck
- unconvert
#- unparam
- unused
#- whitespace
exclusions:
presets:
- common-false-positives
- legacy
- std-error-handling

3
vendor/github.com/spf13/cobra/.mailmap generated vendored Normal file
View File

@ -0,0 +1,3 @@
Steve Francia <steve.francia@gmail.com>
Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>
Fabiano Franz <ffranz@redhat.com> <contact@fabianofranz.com>

37
vendor/github.com/spf13/cobra/CONDUCT.md generated vendored Normal file
View File

@ -0,0 +1,37 @@
## Cobra User Contract
### Versioning
Cobra will follow a steady release cadence. Non breaking changes will be released as minor versions quarterly. Patch bug releases are at the discretion of the maintainers. Users can expect security patch fixes to be released within relatively short order of a CVE becoming known. For more information on security patch fixes see the CVE section below. Releases will follow [Semantic Versioning](https://semver.org/). Users tracking the Master branch should expect unpredictable breaking changes as the project continues to move forward. For stability, it is highly recommended to use a release.
### Backward Compatibility
We will maintain two major releases in a moving window. The N-1 release will only receive bug fixes and security updates and will be dropped once N+1 is released.
### Deprecation
Deprecation of Go versions or dependent packages will only occur in major releases. To reduce the change of this taking users by surprise, any large deprecation will be preceded by an announcement in the [#cobra slack channel](https://gophers.slack.com/archives/CD3LP1199) and an Issue on Github.
### CVE
Maintainers will make every effort to release security patches in the case of a medium to high severity CVE directly impacting the library. The speed in which these patches reach a release is up to the discretion of the maintainers. A low severity CVE may be a lower priority than a high severity one.
### Communication
Cobra maintainers will use GitHub issues and the [#cobra slack channel](https://gophers.slack.com/archives/CD3LP1199) as the primary means of communication with the community. This is to foster open communication with all users and contributors.
### Breaking Changes
Breaking changes are generally allowed in the master branch, as this is the branch used to develop the next release of Cobra.
There may be times, however, when master is closed for breaking changes. This is likely to happen as we near the release of a new version.
Breaking changes are not allowed in release branches, as these represent minor versions that have already been released. These version have consumers who expect the APIs, behaviors, etc, to remain stable during the lifetime of the patch stream for the minor release.
Examples of breaking changes include:
- Removing or renaming exported constant, variable, type, or function.
- Updating the version of critical libraries such as `spf13/pflag`, `spf13/viper` etc...
- Some version updates may be acceptable for picking up bug fixes, but maintainers must exercise caution when reviewing.
There may, at times, need to be exceptions where breaking changes are allowed in release branches. These are at the discretion of the project's maintainers, and must be carefully considered before merging.
### CI Testing
Maintainers will ensure the Cobra test suite utilizes the current supported versions of Golang.
### Disclaimer
Changes to this document and the contents therein are at the discretion of the maintainers.
None of the contents of this document are legally binding in any way to the maintainers or the users.

50
vendor/github.com/spf13/cobra/CONTRIBUTING.md generated vendored Normal file
View File

@ -0,0 +1,50 @@
# Contributing to Cobra
Thank you so much for contributing to Cobra. We appreciate your time and help.
Here are some guidelines to help you get started.
## Code of Conduct
Be kind and respectful to the members of the community. Take time to educate
others who are seeking help. Harassment of any kind will not be tolerated.
## Questions
If you have questions regarding Cobra, feel free to ask it in the community
[#cobra Slack channel][cobra-slack]
## Filing a bug or feature
1. Before filing an issue, please check the existing issues to see if a
similar one was already opened. If there is one already opened, feel free
to comment on it.
1. If you believe you've found a bug, please provide detailed steps of
reproduction, the version of Cobra and anything else you believe will be
useful to help troubleshoot it (e.g. OS environment, environment variables,
etc...). Also state the current behavior vs. the expected behavior.
1. If you'd like to see a feature or an enhancement please open an issue with
a clear title and description of what the feature is and why it would be
beneficial to the project and its users.
## Submitting changes
1. CLA: Upon submitting a Pull Request (PR), contributors will be prompted to
sign a CLA. Please sign the CLA :slightly_smiling_face:
1. Tests: If you are submitting code, please ensure you have adequate tests
for the feature. Tests can be run via `go test ./...` or `make test`.
1. Since this is golang project, ensure the new code is properly formatted to
ensure code consistency. Run `make all`.
### Quick steps to contribute
1. Fork the project.
1. Download your fork to your PC (`git clone https://github.com/your_username/cobra && cd cobra`)
1. Create your feature branch (`git checkout -b my-new-feature`)
1. Make changes and run tests (`make test`)
1. Add them to staging (`git add .`)
1. Commit your changes (`git commit -m 'Add some feature'`)
1. Push to the branch (`git push origin my-new-feature`)
1. Create new pull request
<!-- Links -->
[cobra-slack]: https://gophers.slack.com/archives/CD3LP1199

174
vendor/github.com/spf13/cobra/LICENSE.txt generated vendored Normal file
View File

@ -0,0 +1,174 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.

13
vendor/github.com/spf13/cobra/MAINTAINERS generated vendored Normal file
View File

@ -0,0 +1,13 @@
maintainers:
- spf13
- johnSchnake
- jpmcb
- marckhouzam
inactive:
- anthonyfok
- bep
- bogem
- broady
- eparis
- jharshman
- wfernandes

35
vendor/github.com/spf13/cobra/Makefile generated vendored Normal file
View File

@ -0,0 +1,35 @@
BIN="./bin"
SRC=$(shell find . -name "*.go")
ifeq (, $(shell which golangci-lint))
$(warning "could not find golangci-lint in $(PATH), run: curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh")
endif
.PHONY: fmt lint test install_deps clean
default: all
all: fmt test
fmt:
$(info ******************** checking formatting ********************)
@test -z $(shell gofmt -l $(SRC)) || (gofmt -d $(SRC); exit 1)
lint:
$(info ******************** running lint tools ********************)
golangci-lint run -v
test: install_deps
$(info ******************** running tests ********************)
go test -v ./...
richtest: install_deps
$(info ******************** running tests with kyoh86/richgo ********************)
richgo test -v ./...
install_deps:
$(info ******************** downloading dependencies ********************)
go get -v ./...
clean:
rm -rf $(BIN)

133
vendor/github.com/spf13/cobra/README.md generated vendored Normal file
View File

@ -0,0 +1,133 @@
<div align="center">
<a href="https://cobra.dev">
<img width="512" height="535" alt="cobra-logo" src="https://github.com/user-attachments/assets/c8bf9aad-b5ae-41d3-8899-d83baec10af8" />
</a>
</div>
Cobra is a library for creating powerful modern CLI applications.
<a href="https://cobra.dev">Visit Cobra.dev for extensive documentation</a>
Cobra is used in many Go projects such as [Kubernetes](https://kubernetes.io/),
[Hugo](https://gohugo.io), and [GitHub CLI](https://github.com/cli/cli) to
name a few. [This list](site/content/projects_using_cobra.md) contains a more extensive list of projects using Cobra.
[![](https://img.shields.io/github/actions/workflow/status/spf13/cobra/test.yml?branch=main&longCache=true&label=Test&logo=github%20actions&logoColor=fff)](https://github.com/spf13/cobra/actions?query=workflow%3ATest)
[![Go Reference](https://pkg.go.dev/badge/github.com/spf13/cobra.svg)](https://pkg.go.dev/github.com/spf13/cobra)
[![Go Report Card](https://goreportcard.com/badge/github.com/spf13/cobra)](https://goreportcard.com/report/github.com/spf13/cobra)
[![Slack](https://img.shields.io/badge/Slack-cobra-brightgreen)](https://gophers.slack.com/archives/CD3LP1199)
<hr>
<div align="center" markdown="1">
<sup>Supported by:</sup>
<br>
<br>
<a href="https://www.warp.dev/cobra">
<img alt="Warp sponsorship" width="400" src="https://github.com/user-attachments/assets/ab8dd143-b0fd-4904-bdc5-dd7ecac94eae">
</a>
### [Warp, the AI terminal for devs](https://www.warp.dev/cobra)
[Try Cobra in Warp today](https://www.warp.dev/cobra)<br>
</div>
<hr>
# Overview
Cobra is a library providing a simple interface to create powerful modern CLI
interfaces similar to git & go tools.
Cobra provides:
* Easy subcommand-based CLIs: `app server`, `app fetch`, etc.
* Fully POSIX-compliant flags (including short & long versions)
* Nested subcommands
* Global, local and cascading flags
* Intelligent suggestions (`app srver`... did you mean `app server`?)
* Automatic help generation for commands and flags
* Grouping help for subcommands
* Automatic help flag recognition of `-h`, `--help`, etc.
* Automatically generated shell autocomplete for your application (bash, zsh, fish, powershell)
* Automatically generated man pages for your application
* Command aliases so you can change things without breaking them
* The flexibility to define your own help, usage, etc.
* Optional seamless integration with [viper](https://github.com/spf13/viper) for 12-factor apps
# Concepts
Cobra is built on a structure of commands, arguments & flags.
**Commands** represent actions, **Args** are things and **Flags** are modifiers for those actions.
The best applications read like sentences when used, and as a result, users
intuitively know how to interact with them.
The pattern to follow is
`APPNAME VERB NOUN --ADJECTIVE`
or
`APPNAME COMMAND ARG --FLAG`.
A few good real world examples may better illustrate this point.
In the following example, 'server' is a command, and 'port' is a flag:
hugo server --port=1313
In this command we are telling Git to clone the url bare.
git clone URL --bare
## Commands
Command is the central point of the application. Each interaction that
the application supports will be contained in a Command. A command can
have children commands and optionally run an action.
In the example above, 'server' is the command.
[More about cobra.Command](https://pkg.go.dev/github.com/spf13/cobra#Command)
## Flags
A flag is a way to modify the behavior of a command. Cobra supports
fully POSIX-compliant flags as well as the Go [flag package](https://golang.org/pkg/flag/).
A Cobra command can define flags that persist through to children commands
and flags that are only available to that command.
In the example above, 'port' is the flag.
Flag functionality is provided by the [pflag
library](https://github.com/spf13/pflag), a fork of the flag standard library
which maintains the same interface while adding POSIX compliance.
# Installing
Using Cobra is easy. First, use `go get` to install the latest version
of the library.
```
go get -u github.com/spf13/cobra@latest
```
Next, include Cobra in your application:
```go
import "github.com/spf13/cobra"
```
# Usage
`cobra-cli` is a command line program to generate cobra applications and command files.
It will bootstrap your application scaffolding to rapidly
develop a Cobra-based application. It is the easiest way to incorporate Cobra into your application.
It can be installed by running:
```
go install github.com/spf13/cobra-cli@latest
```
For complete details on using the Cobra-CLI generator, please read [The Cobra Generator README](https://github.com/spf13/cobra-cli/blob/main/README.md)
For complete details on using the Cobra library, please read [The Cobra User Guide](site/content/user_guide.md).
# License
Cobra is released under the Apache 2.0 license. See [LICENSE.txt](LICENSE.txt)

105
vendor/github.com/spf13/cobra/SECURITY.md generated vendored Normal file
View File

@ -0,0 +1,105 @@
# Security Policy
## Reporting a Vulnerability
The `cobra` maintainers take security issues seriously and
we appreciate your efforts to _**responsibly**_ disclose your findings.
We will make every effort to swiftly respond and address concerns.
To report a security vulnerability:
1. **DO NOT** create a public GitHub issue for the vulnerability!
2. **DO NOT** create a public GitHub Pull Request with a fix for the vulnerability!
3. Send an email to `cobra-security@googlegroups.com`.
4. Include the following details in your report:
- Description of the vulnerability
- Steps to reproduce
- Potential impact of the vulnerability (to your downstream project, to the Go ecosystem, etc.)
- Any potential mitigations you've already identified
5. Allow up to 7 days for an initial response.
You should receive an acknowledgment of your report and an estimated timeline for a fix.
6. (Optional) If you have a fix and would like to contribute your patch, please work
directly with the maintainers via `cobra-security@googlegroups.com` to
coordinate pushing the patch to GitHub, cutting a new release, and disclosing the change.
## Response Process
When a security vulnerability report is received, the `cobra` maintainers will:
1. Confirm receipt of the vulnerability report within 7 days.
2. Assess the report to determine if it constitutes a security vulnerability.
3. If confirmed, assign the vulnerability a severity level and create a timeline for addressing it.
4. Develop and test a fix.
5. Patch the vulnerability and make a new GitHub release: the maintainers will coordinate disclosure with the reporter.
6. Create a new GitHub Security Advisory to inform the broader Go ecosystem
## Disclosure Policy
The `cobra` maintainers follow a coordinated disclosure process:
1. Security vulnerabilities will be addressed as quickly as possible.
2. A CVE (Common Vulnerabilities and Exposures) identifier will be requested for significant vulnerabilities
that are within `cobra` itself.
3. Once a fix is ready, the maintainers will:
- Release a new version containing the fix.
- Update the security advisory with details about the vulnerability.
- Credit the reporter (unless they wish to remain anonymous).
- Credit the fixer (unless they wish to remain anonymous, this may be the same as the reporter).
- Announce the vulnerability through appropriate channels
(GitHub Security Advisory, mailing lists, GitHub Releases, etc.)
## Supported Versions
Security fixes will typically only be released for the most recent major release.
## Upstream Security Issues
`cobra` generally will not accept vulnerability reports that originate in upstream
dependencies. I.e., if there is a problem in Go code that `cobra` depends on,
it is best to engage that project's maintainers and owners.
This security policy primarily pertains only to `cobra` itself but if you believe you've
identified a problem that originates in an upstream dependency and is being widely
distributed by `cobra`, please follow the disclosure procedure above: the `cobra`
maintainers will work with you to determine the severity and ecosystem impact.
## Security Updates and CVEs
Information about known security vulnerabilities and CVEs affecting `cobra` will
be published as GitHub Security Advisories at
https://github.com/spf13/cobra/security/advisories.
All users are encouraged to watch the repository and upgrade promptly when
security releases are published.
## `cobra` Security Best Practices for Users
When using `cobra` in your CLIs, the `cobra` maintainers recommend the following:
1. Always use the latest version of `cobra`.
2. [Use Go modules](https://go.dev/blog/using-go-modules) for dependency management.
3. Always use the latest possible version of Go.
## Security Best Practices for Contributors
When contributing to `cobra`:
1. Be mindful of security implications when adding new features or modifying existing ones.
2. Be aware of `cobra`'s extremely large reach: it is used in nearly every Go CLI
(like Kubernetes, Docker, Prometheus, etc. etc.)
3. Write tests that explicitly cover edge cases and potential issues.
4. If you discover a security issue while working on `cobra`, please report it
following the process above rather than opening a public pull request or issue that
addresses the vulnerability.
5. Take personal sec-ops seriously and secure your GitHub account: use [two-factor authentication](https://docs.github.com/en/authentication/securing-your-account-with-two-factor-authentication-2fa),
[sign your commits with a GPG or SSH key](https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification),
etc.
## Acknowledgments
The `cobra` maintainers would like to thank all security researchers and
community members who help keep cobra, its users, and the entire Go ecosystem secure through responsible disclosures!!
---
*This security policy is inspired by the [Open Web Application Security Project (OWASP)](https://owasp.org/) guidelines and security best practices.*

60
vendor/github.com/spf13/cobra/active_help.go generated vendored Normal file
View File

@ -0,0 +1,60 @@
// Copyright 2013-2023 The Cobra Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package cobra
import (
"fmt"
"os"
)
const (
activeHelpMarker = "_activeHelp_ "
// The below values should not be changed: programs will be using them explicitly
// in their user documentation, and users will be using them explicitly.
activeHelpEnvVarSuffix = "ACTIVE_HELP"
activeHelpGlobalEnvVar = configEnvVarGlobalPrefix + "_" + activeHelpEnvVarSuffix
activeHelpGlobalDisable = "0"
)
// AppendActiveHelp adds the specified string to the specified array to be used as ActiveHelp.
// Such strings will be processed by the completion script and will be shown as ActiveHelp
// to the user.
// The array parameter should be the array that will contain the completions.
// This function can be called multiple times before and/or after completions are added to
// the array. Each time this function is called with the same array, the new
// ActiveHelp line will be shown below the previous ones when completion is triggered.
func AppendActiveHelp(compArray []Completion, activeHelpStr string) []Completion {
return append(compArray, fmt.Sprintf("%s%s", activeHelpMarker, activeHelpStr))
}
// GetActiveHelpConfig returns the value of the ActiveHelp environment variable
// <PROGRAM>_ACTIVE_HELP where <PROGRAM> is the name of the root command in upper
// case, with all non-ASCII-alphanumeric characters replaced by `_`.
// It will always return "0" if the global environment variable COBRA_ACTIVE_HELP
// is set to "0".
func GetActiveHelpConfig(cmd *Command) string {
activeHelpCfg := os.Getenv(activeHelpGlobalEnvVar)
if activeHelpCfg != activeHelpGlobalDisable {
activeHelpCfg = os.Getenv(activeHelpEnvVar(cmd.Root().Name()))
}
return activeHelpCfg
}
// activeHelpEnvVar returns the name of the program-specific ActiveHelp environment
// variable. It has the format <PROGRAM>_ACTIVE_HELP where <PROGRAM> is the name of the
// root command in upper case, with all non-ASCII-alphanumeric characters replaced by `_`.
func activeHelpEnvVar(name string) string {
return configEnvVar(name, activeHelpEnvVarSuffix)
}

Some files were not shown because too many files have changed in this diff Show More