From 3d02c5b72eec8fe4a6906dde011623a7342c4830 Mon Sep 17 00:00:00 2001
From: Valera Shaitorov <shaitorov@perx.ru>
Date: Fri, 16 Jun 2023 17:29:50 +0700
Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B5=D0=BD=D0=B5=D1=81?=
 =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20util=20=D0=B8=D0=B7=20perxis,=20=D0=BF?=
 =?UTF-8?q?=D0=B5=D1=80=D0=B5=D0=BD=D0=B5=D1=81=D0=B5=D0=BD=20=D0=BA=D0=BE?=
 =?UTF-8?q?=D0=B4=20dowloader'a?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 pkg/cli/gracefull.go      | 40 +++++++++++++++++++++++++++++
 pkg/data/translit.go      | 54 +++++++++++++++++++++++++++++++++++++++
 pkg/data/translit_test.go | 45 ++++++++++++++++++++++++++++++++
 pkg/files/downloader.go   | 24 +++++++++++++++++
 4 files changed, 163 insertions(+)
 create mode 100644 pkg/cli/gracefull.go
 create mode 100644 pkg/data/translit.go
 create mode 100644 pkg/data/translit_test.go

diff --git a/pkg/cli/gracefull.go b/pkg/cli/gracefull.go
new file mode 100644
index 00000000..8fd64a3d
--- /dev/null
+++ b/pkg/cli/gracefull.go
@@ -0,0 +1,40 @@
+package cli
+
+import (
+	"os"
+	"os/signal"
+	"syscall"
+
+	"go.uber.org/zap"
+)
+
+func WaitForQuit(logger *zap.Logger, finailizer func()) {
+	donec := make(chan struct{})
+	sigc := make(chan os.Signal, 1)
+	signal.Notify(sigc, syscall.SIGTERM, os.Interrupt)
+	var signalsReceived uint
+	go func() {
+		for {
+			select {
+			case s := <-sigc:
+				logger.Info("Signal received. Exiting", zap.String("Signal", s.String()))
+				signalsReceived++
+
+				if signalsReceived < 2 {
+					// After first Ctrl+C start quitting the worker gracefully
+					go func() {
+						finailizer()
+						close(donec)
+					}()
+				} else {
+					// Abort the program when user hits Ctrl+C second time in a row
+					logger.Info("Force exit")
+					close(donec)
+				}
+			}
+		}
+	}()
+
+	<-donec
+	logger.Info("Quit")
+}
diff --git a/pkg/data/translit.go b/pkg/data/translit.go
new file mode 100644
index 00000000..654ae90d
--- /dev/null
+++ b/pkg/data/translit.go
@@ -0,0 +1,54 @@
+package data
+
+import (
+	"bytes"
+	"strings"
+	"unicode"
+)
+
+var mapRuEn = map[rune]string{
+	'Р°': "a", 'Р±': "b", 'РІ': "v", 'Рі': "g", 'Рґ': "d", 'Рµ': "e", 'С‘': "yo", 'Р¶': "zh", 'Р·': "z", 'Рё': "i", 'Р№': "j",
+	'Рє': "k", 'Р»': "l", 'Рј': "m", 'РЅ': "n", 'Рѕ': "o", 'Рї': "p", 'СЂ': "r", 'СЃ': "s", 'С‚': "t", 'Сѓ': "u", 'С„': "f",
+	'С…': "h", 'С†': "c", 'С‡': "ch", 'С€': "sh", 'С‰': "shh", 'СЉ': "", 'С‹': "y", 'СЊ': "", 'СЌ': "e", 'СЏ': "ya",
+}
+
+func mapSpecial(r rune) rune {
+	if r <= unicode.MaxASCII && (unicode.IsLetter(r) || unicode.IsNumber(r) || r == ':' || r == '/') {
+		return r
+	}
+	return '-'
+}
+
+func StringTransform(s string, lowercase bool) string {
+	s = TableEncode(s, mapRuEn)
+	s = strings.Map(mapSpecial, s)
+	if lowercase {
+		s = strings.ToLower(s)
+	}
+	return s
+}
+
+func TableEncode(s string, tlm map[rune]string) string {
+	in := bytes.NewBufferString(s)
+	out := bytes.NewBuffer(nil)
+
+	for {
+		r, _, err := in.ReadRune()
+		if err != nil {
+			break // EOF
+		}
+
+		tr, ok := tlm[unicode.ToLower(r)]
+		if !ok {
+			out.WriteRune(r)
+			continue
+		}
+
+		if unicode.IsUpper(r) {
+			tr = strings.Title(tr)
+		}
+
+		out.WriteString(tr)
+	}
+	return out.String()
+}
diff --git a/pkg/data/translit_test.go b/pkg/data/translit_test.go
new file mode 100644
index 00000000..94b4f426
--- /dev/null
+++ b/pkg/data/translit_test.go
@@ -0,0 +1,45 @@
+package data
+
+import (
+	"testing"
+)
+
+func TestTranslit(t *testing.T) {
+	type args struct {
+		s         string
+		lowercase bool
+	}
+	tests := []struct {
+		name string
+		args args
+		want string
+	}{
+		{
+			name: "test translit cyrillic",
+			args: args{"РЃР¶РёРє 2019", true},
+			want: "yozhik-2019",
+		},
+		{
+			name: "test translit cyrillic",
+			args: args{"РЃР¶РёРє", false},
+			want: "Yozhik",
+		},
+		{
+			name: "test translit english",
+			args: args{"Hello world,sample", true},
+			want: "hello-world-sample",
+		},
+		{
+			name: "test translit english",
+			args: args{"Специальные...символы", true},
+			want: "specialnye---simvoly",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if got := StringTransform(tt.args.s, tt.args.lowercase); got != tt.want {
+				t.Errorf("Translit() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
diff --git a/pkg/files/downloader.go b/pkg/files/downloader.go
index 79acfeed..74aa9102 100644
--- a/pkg/files/downloader.go
+++ b/pkg/files/downloader.go
@@ -1,6 +1,7 @@
 package files
 
 import (
+	"encoding/base64"
 	"errors"
 	"io"
 	"net/http"
@@ -30,3 +31,26 @@ func (d *downloader) Download(dst io.Writer, file *File) error {
 	_, err = io.Copy(dst, r.Body)
 	return err
 }
+
+type noopDownloader struct{}
+
+func NewNoopDownloader() Downloader {
+	return &noopDownloader{}
+}
+
+func (d *noopDownloader) Download(dst io.Writer, file *File) error {
+	return nil
+}
+
+type dummyDownloader struct{}
+
+func NewDummyDownloader() Downloader {
+	return &dummyDownloader{}
+}
+
+func (d *dummyDownloader) Download(dst io.Writer, file *File) error {
+	// png pixel 10x10
+	pixel, err := base64.StdEncoding.DecodeString("iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAFUlEQVR42mNkYPhfz0AEYBxVSF+FAP5FDvcfRYWgAAAAAElFTkSuQmCC")
+	_, err = dst.Write(pixel)
+	return err
+}
-- 
GitLab