github.com/martinohmann/rfoutlet@v1.2.1-0.20220707195255-8a66aa411105/internal/schedule/schedule.go (about)

     1  // Package schedule provides types to define time switch schedule intervals for
     2  // outlets.
     3  package schedule
     4  
     5  import (
     6  	"encoding/json"
     7  	"fmt"
     8  	"sync"
     9  	"time"
    10  
    11  	uuid "github.com/satori/go.uuid"
    12  )
    13  
    14  // Schedule is a collection of intervals.
    15  type Schedule struct {
    16  	sync.RWMutex
    17  	intervals []Interval
    18  }
    19  
    20  // New creates a new empty *Schedule.
    21  func New() *Schedule {
    22  	return NewWithIntervals(make([]Interval, 0))
    23  }
    24  
    25  // NewWithIntervals create a new *Schedule with intervals.
    26  func NewWithIntervals(intervals []Interval) *Schedule {
    27  	return &Schedule{
    28  		intervals: intervals,
    29  	}
    30  }
    31  
    32  // Enabled returns true if any of the intervals is enabled.
    33  func (s *Schedule) Enabled() bool {
    34  	if s == nil {
    35  		return false
    36  	}
    37  
    38  	s.RLock()
    39  	intervals := s.intervals
    40  	s.RUnlock()
    41  
    42  	for _, i := range intervals {
    43  		if i.Enabled {
    44  			return true
    45  		}
    46  	}
    47  
    48  	return false
    49  }
    50  
    51  // Contains returns true if any of the intervals contains t.
    52  func (s *Schedule) Contains(t time.Time) bool {
    53  	if s == nil {
    54  		return false
    55  	}
    56  
    57  	s.RLock()
    58  	intervals := s.intervals
    59  	s.RUnlock()
    60  
    61  	for _, i := range intervals {
    62  		if i.Contains(t) {
    63  			return true
    64  		}
    65  	}
    66  
    67  	return false
    68  }
    69  
    70  // AddInterval adds an interval to the schedule of an outlet.
    71  func (s *Schedule) AddInterval(interval Interval) error {
    72  	if interval.ID == "" {
    73  		interval.ID = uuid.NewV4().String()
    74  	}
    75  
    76  	s.Lock()
    77  	defer s.Unlock()
    78  
    79  	for _, i := range s.intervals {
    80  		if i.ID == interval.ID {
    81  			return fmt.Errorf("interval %q already exists", interval.ID)
    82  		}
    83  	}
    84  
    85  	s.intervals = append(s.intervals, interval)
    86  
    87  	return nil
    88  }
    89  
    90  // UpdateInterval updates an interval of the schedule of an outlet. Will return
    91  // an error if the interval does not exist.
    92  func (s *Schedule) UpdateInterval(interval Interval) error {
    93  	s.Lock()
    94  	defer s.Unlock()
    95  
    96  	for j, i := range s.intervals {
    97  		if i.ID == interval.ID {
    98  			s.intervals[j] = interval
    99  			return nil
   100  		}
   101  	}
   102  
   103  	return fmt.Errorf("interval %q does not exist", interval.ID)
   104  }
   105  
   106  // DeleteInterval deletes an interval of the schedule of an outlet. Will return
   107  // an error if the interval does not exist.
   108  func (s *Schedule) DeleteInterval(interval Interval) error {
   109  	s.Lock()
   110  	defer s.Unlock()
   111  
   112  	for j, i := range s.intervals {
   113  		if i.ID == interval.ID {
   114  			s.intervals = append(s.intervals[:j], s.intervals[j+1:]...)
   115  			return nil
   116  		}
   117  	}
   118  
   119  	return fmt.Errorf("interval %q does not exist", interval.ID)
   120  }
   121  
   122  // UnmarshalJSON implements the json.Unmarshaler interface.
   123  //
   124  // This ensures that the json bytes are correctly unmarshalled into the
   125  // internal slice of Interval values.
   126  func (s *Schedule) UnmarshalJSON(b []byte) error {
   127  	return json.Unmarshal(b, &s.intervals)
   128  }
   129  
   130  // MarshalJSON implements the json.Marshaler interface.
   131  //
   132  // This hides that fact that *Schedule wraps a slice of Interval in the
   133  // marshalled json.
   134  func (s *Schedule) MarshalJSON() ([]byte, error) {
   135  	return json.Marshal(s.intervals)
   136  }