// Package steam is designed to be a rather simplistic library to help pull // information from a local steam library and run basic operations like // archiving, restore and deleting games package steam import ( "errors" "fmt" "io/ioutil" "regexp" "sync" ) var E_GameDoesNotExist = errors.New("Game does not exist") // Library is used to represent the steam library, the Games map is populated // by NewLibrary when called or when ProcessLibrary is called directly // The key in the map is the same as the Game's `Name` property. // // Status contains various bits of information about previous jobs // and the status of any currently running jobs type Library struct { folder string games map[string]*Game status *Jobs m sync.Mutex } // Game represents an actual game in the steam Library. The purpose is only // to provide info on a game. type Game struct { Name string LibraryPath string Size int64 } var slugregexp = regexp.MustCompile(`[^-0-9A-Za-z_:.]`) // Slug returns a safer version of the name with spaces and other chars // transformed into dashes for use in HTML element ids and such. func (g Game) Slug() string { return slugregexp.ReplaceAllString(g.Name, "-") } // NewLibrary returns a pointer to a processed library and an error // if any func NewLibrary(path string) (*Library, error) { l := &Library{ status: &Jobs{ running: make([]*Job, 0), previous: make([]*Job, 0), }, } err := l.ProcessLibrary(path) if err != nil { return nil, err } return l, err } // NewLibraryMust is the same as NewLibrary but calls panic if there // is any error func NewLibraryMust(path string) *Library { l, err := NewLibrary(path) if err != nil { panic(err) } return l } // Games returns a slice of *Game for the current library func (l *Library) Games() []*Game { l.m.Lock() out := []*Game{} for _, g := range l.games { out = append(out, g) } l.m.Unlock() return out } // Jobs returns the current *Jobs struct which can be used to keep track // of any long running operations on the library as well as any errors // encountered along the way func (l *Library) Status() *Jobs { return l.status } // ProcessLibrary Populates the "Folder" and "Games" fields based on the // provided directory. // func (s *Library) ProcessLibrary(r string) error { if s.status.Running() { return errors.New("Cannot process library with actions running") } if !hasCommon(r) { return fmt.Errorf("No common directory in: %s", r) } s.m.Lock() defer s.m.Unlock() s.games = make(map[string]*Game) dirs, err := ioutil.ReadDir(r + "/common") if err != nil { return err } s.folder = r for _, f := range dirs { if f.IsDir() { g := &Game{ Name: f.Name(), LibraryPath: r, } g.SetSizeInfo() s.games[f.Name()] = g } } return nil } func (s *Library) String() (str string) { str = fmt.Sprintf("Library: %s\n", s.folder) str = str + "----\n" for _, v := range s.games { str = str + fmt.Sprintf("%s\n", v.Name) } return } func hasCommon(d string) bool { dirs, err := ioutil.ReadDir(d) if err != nil { return false } for _, f := range dirs { if f.Name() == "common" && f.IsDir() { return true } } return false }