You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
sonic/service/theme/property_scanner.go

239 lines
6.4 KiB
Go

package theme
import (
"context"
"io"
"os"
"path/filepath"
"strings"
"gopkg.in/yaml.v2"
"github.com/go-sonic/sonic/config"
"github.com/go-sonic/sonic/consts"
"github.com/go-sonic/sonic/model/dto"
"github.com/go-sonic/sonic/util/xerr"
)
type PropertyScanner interface {
ListAll(ctx context.Context, themeRootPath string) ([]*dto.ThemeProperty, error)
GetThemeByThemeID(ctx context.Context, themeID string) (*dto.ThemeProperty, error)
ReadThemeProperty(ctx context.Context, themePath string) (*dto.ThemeProperty, error)
ReadThemeConfig(ctx context.Context, themePath string) ([]*dto.ThemeConfigGroup, error)
UnmarshalProperty(ctx context.Context, themePropertyContent []byte) (*dto.ThemeProperty, error)
UnmarshalConfig(ctx context.Context, themeSettingContent []byte) ([]*dto.ThemeConfigGroup, error)
}
type propertyScannerImpl struct {
Config *config.Config
}
func NewPropertyScanner(config *config.Config) PropertyScanner {
return &propertyScannerImpl{
Config: config,
}
}
func (s *propertyScannerImpl) GetThemeByThemeID(ctx context.Context, themeID string) (*dto.ThemeProperty, error) {
allThemes, err := s.ListAll(ctx, s.Config.Sonic.ThemeDir)
if err != nil {
return nil, err
}
for _, themeProperty := range allThemes {
if themeProperty.ID == themeID {
return themeProperty, nil
}
}
return nil, nil
}
func (s *propertyScannerImpl) ListAll(ctx context.Context, themeRootPath string) (themes []*dto.ThemeProperty, err error) {
themeRootDir, err := os.Open(themeRootPath)
if err != nil {
return nil, xerr.NoType.Wrap(err)
}
defer func() {
_ = themeRootDir.Close()
}()
themeDirs, err := themeRootDir.ReadDir(0)
if err != nil {
return
}
for _, themeDir := range themeDirs {
if themeDir.IsDir() {
themeProperty, err := s.ReadThemeProperty(ctx, filepath.Join(themeRootPath, themeDir.Name()))
if err != nil {
return nil, err
}
themes = append(themes, themeProperty)
}
}
return
}
func (s *propertyScannerImpl) ReadThemeProperty(ctx context.Context, themePath string) (*dto.ThemeProperty, error) {
var (
err error
themePropertyFile *os.File
fileStat os.FileInfo
)
for _, themePropertyFilename := range consts.ThemePropertyFilenames {
themePropertyFile, err = os.Open(filepath.Join(themePath, themePropertyFilename))
if os.IsNotExist(err) {
continue
}
defer themePropertyFile.Close()
fileStat, err = themePropertyFile.Stat()
if err != nil {
continue
}
if fileStat.IsDir() {
err = xerr.WithErrMsgf(err, "%s is dir", fileStat.Name())
continue
}
break
}
if err != nil {
return nil, xerr.NoType.Wrapf(err, "theme.yaml not exist")
}
propertyContent, err := io.ReadAll(themePropertyFile)
if err != nil {
return nil, xerr.WithMsg(err, "read theme property file err")
}
themeProperty, err := s.UnmarshalProperty(ctx, propertyContent)
if err != nil {
return nil, err
}
themeProperty.ThemePath = themePath
themeProperty.FolderName = filepath.Base(themePath)
themeProperty.Activated = false
hasOptions, _ := s.hasSettingFile(themePath)
themeProperty.HasOptions = hasOptions
screenshotFilename, err := s.GetThemeScreenshotAbsPath(ctx, themePath)
if err != nil {
return nil, err
}
themeProperty.ScreenShots = strings.Join([]string{"/themes", filepath.Base(themePath), screenshotFilename}, "/")
return themeProperty, nil
}
func (s *propertyScannerImpl) UnmarshalProperty(ctx context.Context, themePropertyContent []byte) (*dto.ThemeProperty, error) {
property := dto.ThemeProperty{}
err := yaml.Unmarshal(themePropertyContent, &property)
if err != nil {
return nil, xerr.WithMsg(err, "unmarshal yaml file err")
}
return &property, nil
}
func (s *propertyScannerImpl) GetThemeScreenshotAbsPath(ctx context.Context, themePath string) (string, error) {
themeDir, err := os.Open(themePath)
if err != nil {
return "", xerr.NoType.Wrapf(err, "open theme path error themePath=%s", themePath)
}
defer themeDir.Close()
themeFiles, err := themeDir.ReadDir(0)
if err != nil {
return "", xerr.NoType.Wrapf(err, "read theme file error themePath=%s", themePath)
}
for _, themeFile := range themeFiles {
if themeFile.IsDir() {
continue
}
if !themeFile.Type().IsRegular() {
continue
}
if strings.HasPrefix(themeFile.Name(), consts.ThemeScreenshotsName) {
return themeFile.Name(), nil
}
}
return "", nil
}
func (s *propertyScannerImpl) hasSettingFile(themePath string) (bool, error) {
var (
err error
themeSettingFileInfo os.FileInfo
)
for _, themeSettingFilename := range consts.ThemeSettingFilenames {
themeSettingFileInfo, err = os.Stat(filepath.Join(themePath, themeSettingFilename))
if os.IsNotExist(err) {
continue
}
if err != nil {
continue
}
if themeSettingFileInfo.IsDir() {
err = xerr.WithErrMsgf(err, "%s is dir", themeSettingFileInfo.Name())
continue
}
return true, nil
}
return false, err
}
func (s *propertyScannerImpl) ReadThemeConfig(ctx context.Context, themePath string) ([]*dto.ThemeConfigGroup, error) {
var (
err error
themeSettingFile *os.File
fileStat os.FileInfo
)
for _, themeSettingFilename := range consts.ThemeSettingFilenames {
themeSettingFile, err = os.Open(filepath.Join(themePath, themeSettingFilename))
if os.IsNotExist(err) {
continue
}
defer themeSettingFile.Close()
fileStat, err = themeSettingFile.Stat()
if err != nil {
continue
}
if fileStat.IsDir() {
err = xerr.WithErrMsgf(err, "%s is dir", fileStat.Name())
continue
}
break
}
if err != nil {
return nil, xerr.NoType.Wrapf(err, "setting.yaml not exist")
}
settingContent, err := io.ReadAll(themeSettingFile)
if err != nil {
return nil, xerr.WithMsg(err, "read theme setting file err")
}
themeSetting, err := s.UnmarshalConfig(ctx, settingContent)
if err != nil {
return nil, err
}
return themeSetting, nil
}
func (s *propertyScannerImpl) UnmarshalConfig(ctx context.Context, themeSettingContent []byte) ([]*dto.ThemeConfigGroup, error) {
settingMap := make(map[string]*dto.ThemeConfigGroup, 0)
err := yaml.Unmarshal(themeSettingContent, &settingMap)
if err != nil {
return nil, xerr.WithMsg(err, "unmarshal yaml file err")
}
settings := make([]*dto.ThemeConfigGroup, 0, len(settingMap))
for name, setting := range settingMap {
setting.Name = name
for _, item := range setting.ItemMap {
setting.Items = append(setting.Items, item)
}
settings = append(settings, setting)
}
return settings, nil
}