Skip to content
Snippets Groups Projects
types.go 4.43 KiB
Newer Older
  • Learn to ignore specific revisions
  • Christian Pointner's avatar
    Christian Pointner committed
    //
    //  tank
    //
    //  Import and Playlist Daemon for autoradio project
    //
    //
    //  Copyright (C) 2017-2018 Christian Pointner <equinox@helsinki.at>
    //
    //  This file is part of tank.
    //
    //  tank is free software: you can redistribute it and/or modify
    //  it under the terms of the GNU General Public License as published by
    //  the Free Software Foundation, either version 3 of the License, or
    //  any later version.
    //
    //  tank is distributed in the hope that it will be useful,
    //  but WITHOUT ANY WARRANTY; without even the implied warranty of
    //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    //  GNU General Public License for more details.
    //
    //  You should have received a copy of the GNU General Public License
    //  along with tank. If not, see <http://www.gnu.org/licenses/>.
    //
    
    package importer
    
    import (
    
    	"encoding/json"
    
    Christian Pointner's avatar
    Christian Pointner committed
    	"errors"
    
    Christian Pointner's avatar
    Christian Pointner committed
    	"net/url"
    
    	"sync/atomic"
    
    Christian Pointner's avatar
    Christian Pointner committed
    const (
    	DefaultBacklog = 100
    
    
    	SourceSchemeAttachment = "attachment"
    
    //******* Errors
    
    
    Christian Pointner's avatar
    Christian Pointner committed
    var (
    
    	ErrNotImplemented        = errors.New("not implemented")
    	ErrNotFound              = errors.New("not found")
    	ErrSourceNotSupported    = errors.New("source uri format is not supported")
    	ErrImportNotRunning      = errors.New("import not running")
    	ErrSourceAlreadyAttached = errors.New("source is already attached")
    	ErrFileNotNew            = errors.New("importing already running or done")
    	ErrTooManyJobs           = errors.New("too many pending jobs")
    	ErrAlreadyCanceled       = errors.New("job is already canceled")
    
    //******* Source URL
    
    
    Christian Pointner's avatar
    Christian Pointner committed
    type SourceURL url.URL
    
    func (s *SourceURL) String() string {
    	return (*url.URL)(s).String()
    }
    
    func (s *SourceURL) MarshalText() (data []byte, err error) {
    	data = []byte(s.String())
    	return
    }
    
    func (s *SourceURL) UnmarshalText(data []byte) (err error) {
    	_, err = (*url.URL)(s).Parse(string(data))
    	return
    }
    
    
    //******* Source
    
    type JobSource struct {
    	len uint64
    	r   io.Reader
    }
    
    
    //******* State
    
    
    Christian Pointner's avatar
    Christian Pointner committed
    type JobState uint32
    
    const (
    
    	// it is important that JobCanceled is smaller than any other state of the job
    	// see job.Cancel() for why this is!
    
    	JobCanceled JobState = iota
    	JobNew
    
    Christian Pointner's avatar
    Christian Pointner committed
    	JobInitializing
    	JobPending
    	JobRunning
    	JobDestroying
    )
    
    
    func (s *JobState) String() string {
    	switch JobState(atomic.LoadUint32((*uint32)(s))) {
    
    Christian Pointner's avatar
    Christian Pointner committed
    	case JobNew:
    		return "new"
    	case JobInitializing:
    		return "initializing"
    	case JobPending:
    		return "pending"
    	case JobRunning:
    		return "running"
    	case JobDestroying:
    		return "destroying"
    	}
    	return "unknown"
    }
    
    func (s *JobState) fromString(str string) error {
    	switch str {
    	case "new":
    
    		atomic.StoreUint32((*uint32)(s), uint32(JobNew))
    
    Christian Pointner's avatar
    Christian Pointner committed
    	case "initializing":
    
    		atomic.StoreUint32((*uint32)(s), uint32(JobInitializing))
    
    Christian Pointner's avatar
    Christian Pointner committed
    	case "pending":
    
    		atomic.StoreUint32((*uint32)(s), uint32(JobPending))
    
    Christian Pointner's avatar
    Christian Pointner committed
    	case "running":
    
    		atomic.StoreUint32((*uint32)(s), uint32(JobRunning))
    
    Christian Pointner's avatar
    Christian Pointner committed
    	case "destroying":
    
    		atomic.StoreUint32((*uint32)(s), uint32(JobDestroying))
    
    Christian Pointner's avatar
    Christian Pointner committed
    	default:
    		return errors.New("invalid job state: '" + str + "'")
    	}
    	return nil
    }
    
    
    func (s *JobState) MarshalText() (data []byte, err error) {
    
    Christian Pointner's avatar
    Christian Pointner committed
    	data = []byte(s.String())
    	return
    }
    
    func (s *JobState) UnmarshalText(data []byte) (err error) {
    	return s.fromString(string(data))
    }
    
    
    //******* Progress
    
    
    type JobProgressStep uint32
    
    
    const (
    	StepFetching JobProgressStep = iota
    	StepNormalizing
    )
    
    func (s JobProgressStep) String() string {
    	switch s {
    	case StepFetching:
    		return "fetching"
    	case StepNormalizing:
    		return "normalizing"
    	}
    	return "unknown"
    }
    
    func (s JobProgressStep) MarshalText() (data []byte, err error) {
    	data = []byte(s.String())
    	return
    }
    
    
    type JobProgress uint64
    
    func (p *JobProgress) set(step JobProgressStep, progress float32) {
    	tmp := uint64(step) << 32
    	tmp |= uint64(math.Float32bits(progress))
    	atomic.StoreUint64((*uint64)(p), tmp)
    
    func (p *JobProgress) get() (step JobProgressStep, progress float32) {
    	tmp := atomic.LoadUint64((*uint64)(p))
    	step = JobProgressStep(tmp >> 32)
    	progress = math.Float32frombits(uint32(tmp))
    	return
    }
    
    func (p *JobProgress) MarshalJSON() ([]byte, error) {
    	step, progress := p.get()
    
    	return json.Marshal(struct {
    		Step     JobProgressStep `json:"step"`
    
    		Progress float32         `json:"progress"`
    
    	}{step, progress})
    }
    
    
    //******* Timestamp
    
    type Timestamp int64
    
    func (t *Timestamp) set(ts time.Time) {
    	atomic.StoreInt64((*int64)(t), ts.Unix())
    }
    
    func (t *Timestamp) get() (ts time.Time) {
    	return time.Unix(atomic.LoadInt64((*int64)(t)), 0)
    }
    
    func (t *Timestamp) MarshalJSON() (data []byte, err error) {
    	return t.get().MarshalJSON()
    }