Ответы пользователя по тегу Go
  • Почему не видна функция определённая в другом файле, но в том же пакете?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Суть проблемы в том, что вы указываете только файл с тестом, а нужно указать еще и файл с декларацией и реализацией (если они разные).

    Вот так будет работать:
    go test -v ./driver_test.go ./driver.go

    Подозреваю, что реально вы хотели сделать чтобы у вас выполнялись не все тесты в пакете, а конкретный один, если так - тогда проще делать вот так (я сам так часто делаю :)))
    go test -v -run ^Test_Deposit$
    Ответ написан
    Комментировать
  • Как удалить письмо через imap в golang?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Нужно сначала отметить сообщения как удалённые (установить флаг), потом вызвать Expunge.
    В документации написано, что если вызвать Close - то сообщения, отмеченные как удалённые, будут удалены, но я не проверял :)

    Вот тут есть пример: https://godocs.io/github.com/emersion/go-imap/clie...
    var c *client.Client
    
    mbox, err := c.Select("INBOX", false)
    if err != nil {
        log.Fatal(err)
    }
    
    if mbox.Messages == 0 {
        log.Fatal("No message in mailbox")
    }
    
    seqset := new(imap.SeqSet)
    for _, message := range mbox.Messages {
         // если сообщение должно быть удалено - тогда вызываете AddNum
         // используем именно message.Uid а не message.SeqNum потому, что ниже мы будем вызывать UidStore, а не Store, так будет явно надёжнее
         seqset.AddNum(message.Uid)
    }
    
    // отмечаем сообщения как удалённые
    item := imap.FormatFlagsOp(imap.AddFlags, true)
    flags := []interface{}{imap.DeletedFlag}
    if err := c.UidStore(seqset, item, flags, nil); err != nil {
        log.Fatal(err)
    }
    
    // удаляем сообщение
    if err := c.Expunge(nil); err != nil {
        log.Fatal(err)
    }
    
    log.Println("Last message has been deleted")
    Ответ написан
  • Какой подход использовать при написании кода для crud в БД с большим количеством таблиц?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Я использую самописный генератор кода.

    Сначала описываю модель данных, запускаю консольную команду и на выходе получаю готовый набор функций для работы с таблицей.

    Есть много готовых генераторов, можете посмотреть в интернете и выбрать какой больше понравится.
    Вот неплохая статья с примерами
    https://medium.com/@zaurio/generator-the-client-to...
    Ответ написан
    1 комментарий
  • Почему приложения на go падает при ошибке postgres?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Из за log.Fatalf
    Ответ написан
  • Как сравнить время?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Вы создаёте время в UTC зоне, а когда вызываете time.Now() - он возвращает вам время в вашей зоне, не в UTC.
    Просто добавьте вызов UTC() в time.Now().UTC()

    var dateStart = time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), 9,  0, 0, 0, time.UTC)
    var dateEnd   = time.Date(time.Now().Year(), time.Now().Month(), time.Now().Day(), 17, 0, 0, 0, time.UTC)
    
      if dateStart.Before(time.Now().UTC() ){
        println("star ok")
      }
      if dateEnd.After(time.Now().UTC()) {
        println("end ok")
      }
    Ответ написан
    2 комментария
  • Как записывать stdout в буфер и считывать оттуда построчно?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Вы всё верно понимаете, горутину можно не использовать.

    Вы можете сделать что-то типа такого
    работающий вариант

    package main
    
    import (
        "bufio"
        "bytes"
        "fmt"
        "io"
        "log"
        "os/exec"
    )
    
    type Executor struct {
        wr io.Writer
    }
    
    func (e *Executor) SetStdout(w io.Writer) {
        e.wr = w
    }
    
    func (e *Executor) Exec() error {
        cmd := exec.Command("ls", "/etc")
        cmd.Stdout = e.wr
        err := cmd.Run()
        return err
    }
    
    type BufferedBrokerWriter struct {
        buffer  bytes.Buffer
        scanner *bufio.Scanner
    }
    
    func NewBufferedBrokerWriter() *BufferedBrokerWriter {
        bw := &BufferedBrokerWriter{}
        bw.scanner = bufio.NewScanner(&bw.buffer)
        return bw
    }
    
    func (bw *BufferedBrokerWriter) Write(p []byte) (int, error) {
        return bw.buffer.Write(p)
    }
    
    func (bw *BufferedBrokerWriter) ReadLines(cnt uint) ([]string, error) {
        lines := make([]string, 0, cnt)
        linesCnt := uint(0)
    
        for bw.scanner.Scan() {
            line := bw.scanner.Text()
            err := bw.scanner.Err()
            if err != nil || line == `` {
                return lines, err
            }
    
            lines = append(lines, line)
            linesCnt++
            if linesCnt >= cnt {
                return lines, nil
            }
        }
    
        return nil, io.EOF
    }
    
    func main() {
        // подключаетесь к вашему брокеру сообщений,
    
        bw := NewBufferedBrokerWriter()
    
        e := &Executor{}
        e.SetStdout(bw)
    
        err := e.Exec()
        if err != nil {
            log.Fatal(err)
        }
    
        for {
            lines, err := bw.ReadLines(2)
            if err == io.EOF {
                break
            }
            fmt.Printf("Lines:\n%+v\n\n", lines)
        }
    }


    если возникнут сложности с реализацией - напишите, помогу
    Ответ написан
  • Можно ли в Golang изменить длину среза напрямую?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Можно попробовать сделать через reflect, например так
    package main
    
    import (
        "fmt"
        "reflect"
    )
    
    func modify(input *[]int, newLen int) {
        val := reflect.Indirect(reflect.ValueOf(input))
        val.SetLen(newLen)
    }
    
    func main() {
        input := []int{1, 2, 3}
    
        fmt.Printf("Before: %p, value: %v\n", &input, input)
    
        modify(&input, len(input) - 1)
    
        fmt.Printf("After: %p, value: %v\n", &input, input)
    }

    Вывод:
    Before: 0xc00000c060, value: [1 2 3]
    After: 0xc00000c060, value: [1 2]
    Ответ написан
  • Go как добавить в поле в структуру которую встраиваю?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Если я правильно понял как нужно сделать - то вот так можно:
    type Response struct {
      Code int `json:"code"`
      Message string `json:"message"`
    }
    
    type LoginResponse struct {
      Response
      Token string
    }


    JSON будет нормально сериализоваться, а инициировать структуру нужно будет вот так
    response := LoginResponse{
           Response : Response {
             Code: 10,
             Message: `error / success message`,
           },
           Token: `token`,
        }
    Ответ написан
  • Почему так происходит и как делать правильно?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Чтобы получить тот результат, который вы ожидаете, нужно использовать select вместо switch.
    Обратите внимание на хороший пример https://tour.golang.org/concurrency/6
    Работающий пример вашей реализации

    package main
    
    import (
    	"log"
    	"time"
    )
    
    func add(ch chan<- string) {
    	for {
    		time.Sleep(time.Second * 2)
    		ch <- "hi"
    	}
    }
    
    func exit(ch chan<- string) {
    	time.Sleep(time.Second * 5)
    	ch <- "close"
    }
    
    func main() {
    	ch := make(chan string, 1000)
    
    	go add(ch)
    
    	go exit(ch)
    
    LOOP:
    	for {
    		select {
    		case val := <- ch:
    			switch val {
    			case "close":
    				log.Println("Close")
    				break LOOP
    			case "hi":
    				log.Println("Hi")
    			}
    		default:
    			log.Println("Default")
    			time.Sleep(time.Second * 1)
    		}
    	}
    }


    https://play.golang.org/p/m1R8CXNKEYq
    Ответ написан
    2 комментария
  • Как сделать одну структуру для разных источников json?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Можно сделать unmarshal в map[string]string и уже извлечь данные от туда.
    А дальше, например, можно сделать такую функцию
    func NewPostFromIncomingPost(incomingPost map[string]string) (*Post, error) {
    	post := &Post{}
    
    	if title, exists := incomingPost[`name`]; exists {
    		post.Title = title
    	} else if title, exists := incomingPost[`title`]; exists {
    		post.Title = title
    	} else {
    		return nil, errors.New(`title not found`)
    	}
    
    	if text, exists := incomingPost[`text`]; exists {
    		post.Text = text
    	} else if text, exists := incomingPost[`description`]; exists {
    		post.Text = text
    	} else {
    		return nil, errors.New(`text not found`)
    	}
    	
    	return post, nil
    }

    Вот тут работающий пример https://play.golang.org/p/gizeAA8uWKt
    Текст работающего примера

    package main
    
    import (
    	"encoding/json"
    	"fmt"
    	"errors"
    )
    
    type Post struct {
    	Title string
    	Text  string
    }
    
    func NewPostFromIncomingPost(incomingPost map[string]string) (*Post, error) {
    	post := &Post{}
    
    	if title, exists := incomingPost[`name`]; exists {
    		post.Title = title
    	} else if title, exists := incomingPost[`title`]; exists {
    		post.Title = title
    	} else {
    		return nil, errors.New(`title not found`)
    	}
    
    	if text, exists := incomingPost[`text`]; exists {
    		post.Text = text
    	} else if text, exists := incomingPost[`description`]; exists {
    		post.Text = text
    	} else {
    		return nil, errors.New(`text not found`)
    	}
    	
    	return post, nil
    }
    
    func main() {
    	jsonSource1 := []byte(`{"title": "Title", "text": "Text"}`)
    	jsonSource2 := []byte(`{"name": "Title", "description": "Text"}`)
    
    	var incomingPost1 map[string]string
    	if err := json.Unmarshal(jsonSource1, &incomingPost1); err != nil {
    		panic(err)
    	}
    
    	post1, err := NewPostFromIncomingPost(incomingPost1)
    	fmt.Printf("post1: %+v\nerr: %+v\n\n", post1, err)
    
    	var incomingPost2 map[string]string
    	if err := json.Unmarshal(jsonSource2, &incomingPost2); err != nil {
    		panic(err)
    	}
    
    	post2, err := NewPostFromIncomingPost(incomingPost2)
    	fmt.Printf("post2: %+v\nerr: %+v\n\n", post2, err)
    }

    Ответ написан
    3 комментария
  • Как в golang получение From из Header письма (imap)?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Тип заголовка, если я не ошибаюсь, должен быть MIMEHeader, т.е. это обычный map[string][], где ключ - это заголовок + методы Get, Values

    Вот так вы увидите то, что вообще там есть (все ключи и значения map)
    log.Printf("entity: %+v\n", entity.Header)

    А вот так можете получить первое значение заголовка
    log.Printf("entity: %+v\n", entity.Header.Get("From"))


    Или все значения заголовка
    log.Printf("entity: %+v\n", entity.Header.Values("To"))


    Подробнее о методах можно посмотреть тут https://godoc.org/net/textproto#MIMEHeader
    Ответ написан
    3 комментария
  • Как создать и объединить аудио в golang?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Если говорить про реализацию на Go - как вариант...

    1. создаёте файл и encoder как тут https://github.com/viert/go-lame

    2. в цикле читаете ваши файлы при помощи mp3 decoder'a, например как тут https://github.com/hajimehoshi/go-mp3/blob/master/...
    и пишете их содержимое в encoder

    Там, где нужна пустота - попробуйте записать/сгенерировать 1сек или меньше тишины и когда нужно - пишите её в encoder.

    Если задачу нужно просто решить и использовать Go не обязательно - её можно решить при помощи консольной команды `ffmpeg` примерно так:
    ffmpeg file1.mp3 file2.mp3 silence1sec.mp3 silence1sec.mp3 output.mp3


    Если всё же нужно реализовать на Go - то есть, как минимум две обёртки
    https://github.com/giorgisio/goav
    https://github.com/xfrr/goffmpeg

    Можете сначала сделать чтобы из консоли всё заработало, а потом уже "покрутить" обёртки
    Ответ написан
    Комментировать
  • Как получить файл из письма на GoLang?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    package main
    
    import (
        "io"
        "io/ioutil"
        "log"
    
        "github.com/emersion/go-imap"
        "github.com/emersion/go-imap/client"
        "github.com/emersion/go-message"
    )
    
    func main() {
        log.Println("Connecting to server...")
    
        c, err := client.DialTLS("xxxxxxxxx", nil)
        if err != nil {
            log.Fatal(err)
        }
    
        log.Println("Connected")
    
        defer c.Logout()
    
        if err := c.Login("xxxxxxxxxxxxx", "xxxxxxxxxx"); err != nil {
            log.Fatal(err)
        }
        log.Println("Logged in")
    
        mbox, err := c.Select("INBOX", false)
        if err != nil {
            log.Fatal(err)
        }
    
        seqset := new(imap.SeqSet)
        seqset.AddRange(1, mbox.Messages)
    
        messages := make(chan *imap.Message, 10)
        done := make(chan error, 1)
        go func() {
            done <- c.Fetch(seqset, []imap.FetchItem{imap.FetchRFC822}, messages)
        }()
    
        for msg := range messages {
            for _, r := range msg.Body {
                entity, err := message.Read(r)
                if err != nil {
                    log.Fatal(err)
                }
    
                multiPartReader := entity.MultipartReader()
    
                for e, err := multiPartReader.NextPart(); err != io.EOF; e, err = multiPartReader.NextPart() {
                    kind, params, cErr := e.Header.ContentType()
                    if cErr != nil {
                        log.Fatal(cErr)
                    }
    
                    if kind != "image/png" && kind != "image/gif" {
                        continue
                    }
    
                    c, rErr := ioutil.ReadAll(e.Body)
                    if rErr != nil {
                        log.Fatal(rErr)
                    }
    
                    log.Printf("Dump file %s", params["name"])
    
                    if fErr := ioutil.WriteFile("/tmp/"+params["name"], c, 0777); ferr != nil {
                        log.Fatal(fErr)
                    }
                }
            }
        }
    
        if err := <-done; err != nil {
            log.Fatal(err)
        }
    
        log.Println("Done")
    }

    Пример взял тут https://stackoverflow.com/questions/55203878/how-t...
    Ответ написан
    1 комментарий
  • Как извлечь GET-параметр (токен) из URL?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Чтобы функция Query работала так, как вам нужно - URL должен быть вида ?param1=value1&param2=value2, тогда вы сможете извлекать значения из map по ключам param1, param2 и т.д.

    Думаю в вашем случае лучше воспользоваться просто Split или другой подходящей функцией из пакета strings.
    Вот работающий пример со Split.
    package main
    
    import (
        "fmt"
        "strings"
    )
    
    func main() {
        url := `https://site.com/regLink?$2a$10$/VTT.GtRslGWLhZL5d`
        result := strings.SplitN(url, `?`, 2)
        fmt.Printf("token: %s\n", result[1])
    }
    Ответ написан
    3 комментария
  • Как динамически создать гиперссылку (на Golang)?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Вам нужно %d заменить на %s, у вас тип данных строковый, а %d - это число
    Полностью рабочий пример
    package main
    
    import "fmt"
    
    func main() {
       var myLink = `https://site.com/Link?`
       var tokenString = string(`hash....`)
       var dataRespon = fmt.Sprintf(`Данные сохранены. Нажмите <a href="%s%s">ссылку</a>.`, myLink, tokenString)
       fmt.Printf(dataRespon)
    }
    Ответ написан
    2 комментария
  • Как реализовать свой API на домене?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Можно.

    Регистрируете домен и прописываете в его настройках IP вашего сервера.

    Далее у вас два варианта.

    Если у вас всего один сервис на сервере - можете слушать сразу 80й порт, вместо 1234.
    И это уже заработает.

    Если доменов на сервере будет много -
    - cтавите веб сервер, например nginx, который слушает 80й порт на вашем IP
    - настраиваете в nginx виртуальный хост для нужного вам домена и там прописываете, чтобы запросы, перенаправлялись на ваш сервис IP:порт(1234)

    Пример конфига nginx (виртуальный хост)

    server {
        listen *:80;
        server_name тут_ваш_домен;
    
        location / {
            proxy_pass http://тут_ip_вашего_сервиса:1234;
            proxy_set_header Host $host;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }

    Ответ написан
    5 комментариев
  • Как запустить программу как демон в Go?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Если сделать вот так ./main & - тогда при завершении ssh сессии отвалится и сам демон.

    Всё действительно сильно зависит от ОС.

    Если говорить о Linux и о самом простом способе, то можно вот так
    nohup ./main > error.log 2>&1 &
    Этот способ подходит для любых бинарников, которые не делают fork, т.е. ведут себя также, как и Go'шный http ListenAndServe.

    Второй вариант - это запуск сервиса через systemd или init.d в зависимости от того, какой в вашем Linux.
    Я предпочитаю именно этот вариант, особенно, когда нельзя использовать Docker (например на виртуальных машинах с Virtuozzo).
    Для systemd (например на CentOS) можно создать файл /etc/systemd/system/yourservice.conf примерно такого содержания
    [Unit]
    Description=YourServie
    After=network.target
    After=syslog.target
    
    [Service]
    User=nobody # user ID под которым должен работать ваш демон
    Group=nobody
    Type=simple
    WorkingDirectory=/opt/yourservice
    ExecStart=/opt/yourservice/yourservice >> /var/log/yourservice.log 2>&1
    Restart=always
    
    [Install]
    WantedBy=multi-user.target


    Третий вариант - использовать Docker, инструкций много в интернете, если этот вариант актуален - напишите, я помогу найти нормальную.

    Четвёртый вариант, который обычно используется в ПО типа Nginx и т.д.
    В общем случае нужно:
    - породить новый процесс, т.е. сделать fork (вызвать системный вызов)
    - настроить вывод stdout, stderr в файлы (опционально)
    - "отвязаться от сессии" вызвать системный вызов setsid
    - сделать chdir куда нужно
    - настроить обработку сигналов SIGINT, SIGTERM, чтобы иметь возможность корректно завершать работу или перечитывать конфиги

    Возможно я что-то упустил в этом списке, давно уже демонов таким образом не делал, использую или Docker или systemd/init.d, так намного проще :)

    Могу еще добавить, что 4й вариант для Go считается антипаттерном.

    Вот тут https://socketloop.com/tutorials/golang-daemonizin... есть пример, правда он не очень удачный, там очень много нюансов.
    Ответ написан
    Комментировать
  • Как правильно вынести map/массив в отдельный пакет?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Можете назвать переменную с большой буквы, тогда она будет экспортироваться и можно будет получить доступ напрямую.

    Рабочий пример вашего кода может выглядеть вот так:
    ./localization/localization.go
    package localization
    
    var L = map[string]string{
      "start_message": "Привет. Я - Бот",
    }


    main.go
    package main
    
    import (
      "fmt"
      local "./localization"
    )
    
    func main() {
        fmt.Printf(local.L["start_message"])
    }
    Ответ написан
    Комментировать
  • Как сохранить картинку полученную с Frontend в папку?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Вот тут есть пример и backend на Go, и frontend на чистом HTML
    https://tutorialedge.net/golang/go-file-upload-tut...
    Ответ написан
    Комментировать
  • Как округлить число в большую сторону в golang?

    EvgenyMamonov
    @EvgenyMamonov Куратор тега Go
    Senior software developer, system architect
    Используйте функцию Ceil из пакета math
    fmt.Println(math.Ceil(11.3)) // 12
    Полный пример

    package main
    
    import (
    	"fmt"
    	"math"
    )
    
    func main() {
    	fmt.Println(math.Ceil(11.3))
    }

    Ответ написан
    Комментировать