package gen import ( _ "embed" // Пакет для встраивания содержимого файлов в бинарники Go, использовав откладку //go:embed "encoding/json" // Пакет для работы с JSON: декодирование и кодирование "errors" // Пакет для создания и обработки ошибок "fmt" // Пакет для форматированного ввода и вывода "io" // Пакет для интерфейсов ввода и вывода "net/http" // Пакет для HTTP-клиентов и серверов "text/template" // Пакет для обработки текстовых шаблонов ) // Используем директиву //go:embed для встраивания содержимого файла шаблона в строку pipTmpl // Встраивание файла tmpls/pip.tmpl.sh //go:embed tmpls/pip.tmpl.sh var pipTmpl string // PipOptions содержит параметры, которые будут переданы в шаблон type PipOptions struct { Name string // Имя пакета Version string // Версия пакета Description string // Описание пакета } // pypiAPIResponse представляет структуру ответа от API PyPI type pypiAPIResponse struct { Info pypiInfo `json:"info"` // Информация о пакете URLs []pypiURL `json:"urls"` // Список URL-адресов для загрузки пакета } // Метод SourceURL ищет и возвращает URL исходного distribution для пакета, если он существует func (res pypiAPIResponse) SourceURL() (pypiURL, error) { for _, url := range res.URLs { if url.PackageType == "sdist" { return url, nil } } return pypiURL{}, errors.New("package doesn't have a source distribution") } // pypiInfo содержит основную информацию о пакете, такую как имя, версия и пр. type pypiInfo struct { Name string `json:"name"` Version string `json:"version"` Summary string `json:"summary"` Homepage string `json:"home_page"` License string `json:"license"` } // pypiURL представляет информацию об одном из доступных для загрузки URL type pypiURL struct { Digests map[string]string `json:"digests"` // Контрольные суммы для файлов Filename string `json:"filename"` // Имя файла PackageType string `json:"packagetype"` // Тип пакета (например sdist) } // Функция Pip загружает информацию о пакете из PyPI и использует шаблон для вывода информации func Pip(w io.Writer, opts PipOptions) error { // Создаем новый шаблон с добавлением функций из FuncMap tmpl, err := template.New("pip"). Funcs(funcs). Parse(pipTmpl) if err != nil { return err } // Формируем URL для запроса к PyPI на основании имени и версии пакета url := fmt.Sprintf( "https://pypi.org/pypi/%s/%s/json", opts.Name, opts.Version, ) // Выполняем HTTP GET запрос к PyPI res, err := http.Get(url) if err != nil { return err } defer res.Body.Close() // Закрываем тело ответа после завершения работы if res.StatusCode != 200 { return fmt.Errorf("pypi: %s", res.Status) } // Раскодируем ответ JSON от PyPI в структуру pypiAPIResponse var resp pypiAPIResponse err = json.NewDecoder(res.Body).Decode(&resp) if err != nil { return err } // Если в opts указано описание, используем его вместо описания из PyPI if opts.Description != "" { resp.Info.Summary = opts.Description } // Выполняем шаблон с использованием данных из resp и записываем результат в w return tmpl.Execute(w, resp) }