/source/file/source.go
https://gitlab.com/actions/go-actions · Go · 187 lines · 140 code · 24 blank · 23 comment · 31 complexity · 06bb2bb7b86ae2d1250678e2329ad613 MD5 · raw file
- // Package file defines file action source.
- //
- // It registers file source in github.com/crackcomm/go-actions/source.
- package file
- import "os"
- import "regexp"
- import "strings"
- import "path/filepath"
- import "github.com/crackcomm/go-actions/action"
- import "github.com/crackcomm/go-actions/source"
- import "github.com/crackcomm/go-actions/encoding/json"
- import "github.com/crackcomm/go-actions/encoding/yaml"
- // DefaultExtension - Default format when saving actions.
- var DefaultExtension = ".json"
- // PathSeparator - Used to replace dots (.) in action name to find them in directories.
- var PathSeparator = string(os.PathSeparator)
- // Source - File actions source.
- type Source struct {
- Addr string
- files map[string]string
- }
- // acceptedExtensions - Accepted extensions.
- var acceptedExtensions = []string{".json", ".yaml", ".yml"}
- // All - Returns all available actions.
- func (s *Source) All() (res action.Actions, err error) {
- // Get all files in source path
- files, err := s.allFiles()
- if err != nil {
- return
- }
- // Read all files
- res = make(action.Actions)
- for name, filename := range files {
- res[name], err = s.readFile(filename)
- if err != nil {
- return
- }
- }
- return
- }
- // Add - Adds action to file store.
- func (s *Source) Add(name string, a *action.Action) (err error) {
- name = strings.Replace(name, ".", PathSeparator, -1)
- return s.writeFile(name+DefaultExtension, a)
- }
- // Get - Gets action from file source.
- func (s *Source) Get(name string) (a *action.Action, err error) {
- filename, err := s.getFilename(name)
- if err != nil {
- return
- }
- a, err = s.readFile(filename)
- return
- }
- // Delete - Deletes action from file source.
- func (s *Source) Delete(name string) (err error) {
- filename, err := s.getFilename(name)
- if err != nil {
- return
- }
- filename = filepath.Join(s.Addr, filename)
- return os.Remove(filename)
- }
- // readFile - Reads action from file, accepts relative path.
- func (s *Source) readFile(filename string) (a *action.Action, err error) {
- filename = filepath.Join(s.Addr, filename)
- file, err := os.Open(filename)
- if err != nil {
- return
- }
- defer file.Close()
- switch filepath.Ext(filename) {
- case ".json":
- a, err = json.NewDecoder(file).Decode()
- case ".yaml", ".yml":
- a, err = yaml.NewDecoder(file).Decode()
- }
- return
- }
- // writeFile - Writes action to file, encodes using DefaultExtension marshaler (yaml or json).
- func (s *Source) writeFile(filename string, a *action.Action) (err error) {
- filename = filepath.Join(s.Addr, filename)
- err = os.MkdirAll(filepath.Dir(filename), os.ModeDir)
- if err != nil {
- return
- }
- file, err := os.Create(filename)
- if err != nil {
- return
- }
- defer file.Close()
- switch DefaultExtension {
- case ".json":
- err = json.NewEncoder(file).Encode(a)
- case ".yaml", ".yml":
- err = yaml.NewEncoder(file).Encode(a)
- }
- return
- }
- // getFilename - Gets action filename by its name, it returns source.ErrNotFound when its not found.
- func (s *Source) getFilename(name string) (filename string, err error) {
- // Get all files in source path
- files, err := s.allFiles()
- if err != nil {
- return
- }
- // Get action filename
- filename = files[name]
- if filename == "" {
- err = source.ErrNotFound
- }
- return
- }
- // allFiles - Finds all action files in source path, returns in map with action name in key and filename in value.
- func (s *Source) allFiles() (res map[string]string, err error) {
- path := filepath.Join(s.Addr, "**/*")
- files, err := filepath.Glob(path)
- if err != nil {
- return
- }
- // Create a map with action names in keys and filenames in value
- res = make(map[string]string)
- for _, filename := range files {
- filename, _ = filepath.Rel(s.Addr, filename)
- name, ok := fileActionName(filename) // minus base path
- if ok {
- res[name] = filename
- }
- }
- return
- }
- // reformat - formats filename from `../../test/me/now/../../` to `test/me/now` and from `.test.me.` to `test.me`
- var reformat = regexp.MustCompile(`(^([./\\]+)|([./\\]+)$)`)
- // fileActionName - Converts filename to action name. Ok can be false when filename extension is not supported.
- func fileActionName(filename string) (name string, ok bool) {
- ext := filepath.Ext(filename)
- if !isAccepted(ext) {
- return
- }
- filename = filename[:len(filename)-len(ext)]
- name = reformat.ReplaceAllString(filename, "")
- name = strings.Replace(name, PathSeparator, ".", -1)
- ok = true
- return
- }
- // isAccepted - checks if extension appears in acceptedExtensions list.
- func isAccepted(ext string) bool {
- for _, accept := range acceptedExtensions {
- if accept == ext {
- return true
- }
- }
- return false
- }
- func init() {
- source.Register("file", func(address string, authorization ...string) (source.Source, error) {
- src := &Source{
- Addr: address,
- files: make(map[string]string),
- }
- return src, nil
- })
- }