add interfaces
This commit is contained in:
		
							parent
							
								
									d3495f874e
								
							
						
					
					
						commit
						06f3247b08
					
				| @ -8,8 +8,9 @@ _	"fmt" | |||||||
| _	"github.com/xeipuuv/gojsonschema" | _	"github.com/xeipuuv/gojsonschema" | ||||||
| 	"gopkg.in/yaml.v3" | 	"gopkg.in/yaml.v3" | ||||||
| 	"io" | 	"io" | ||||||
| _	"log" | 	"log/slog" | ||||||
| 	"strings" | 	"strings" | ||||||
|  | 	"google.golang.org/protobuf/proto" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| //type JSONDecoder json.Decoder
 | //type JSONDecoder json.Decoder
 | ||||||
| @ -30,6 +31,10 @@ func NewDecoder(r io.Reader, format Format) Decoder { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func NewStringDecoder(s string, format Format) Decoder { | ||||||
|  | 	return NewDecoder(strings.NewReader(s), format) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func NewJSONDecoder(r io.Reader) Decoder { | func NewJSONDecoder(r io.Reader) Decoder { | ||||||
| 	return json.NewDecoder(r) | 	return json.NewDecoder(r) | ||||||
| } | } | ||||||
| @ -39,6 +44,7 @@ func NewJSONStringDecoder(s string) Decoder { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func NewYAMLDecoder(r io.Reader) Decoder { | func NewYAMLDecoder(r io.Reader) Decoder { | ||||||
|  | 	slog.Info("NewYAMLDecoder()", "reader", r) | ||||||
| 	return yaml.NewDecoder(r) | 	return yaml.NewDecoder(r) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -46,6 +52,17 @@ func NewYAMLStringDecoder(s string) Decoder { | |||||||
| 	return yaml.NewDecoder(strings.NewReader(s)) | 	return yaml.NewDecoder(strings.NewReader(s)) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func NewProtoBufDecoder(r io.Reader) Decoder { | type ProtoDecoder struct { | ||||||
| 	return nil | 	reader io.Reader | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (p *ProtoDecoder) Decode(v any) (err error) { | ||||||
|  | 	var protoData []byte | ||||||
|  | 	protoData, err = io.ReadAll(p.reader) | ||||||
|  | 	err = proto.Unmarshal(protoData, v.(proto.Message)) | ||||||
|  | 	return  | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func NewProtoBufDecoder(r io.Reader) Decoder { | ||||||
|  | 	return &ProtoDecoder{ reader: r } | ||||||
| } | } | ||||||
|  | |||||||
| @ -9,14 +9,17 @@ _	"log" | |||||||
| 	"strings" | 	"strings" | ||||||
| 	"testing" | 	"testing" | ||||||
| 	"github.com/xeipuuv/gojsonschema" | 	"github.com/xeipuuv/gojsonschema" | ||||||
|  | 	"io" | ||||||
|  | 	"bytes" | ||||||
|  | 	"google.golang.org/protobuf/proto" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type TestUser struct { | type TestUser struct { | ||||||
| 	Name string `json:"name" yaml:"name"` | 	Name string `json:"name" yaml:"name" protobuf:"bytes,1,opt,name=name"` | ||||||
| 	Uid string `json:"uid" yaml:"uid"` | 	Uid string `json:"uid" yaml:"uid" protobuf:"bytes,2,opt,name=uid"` | ||||||
| 	Group string `json:"group" yaml:"group"` | 	Group string `json:"group" yaml:"group" protobuf:"bytes,3,opt,name=group"` | ||||||
| 	Home string `json:"home" yaml:"home"` | 	Home string `json:"home" yaml:"home" protobuf:"bytes,4,opt,name=home"` | ||||||
| 	State string `json:"state" yaml:"state"` | 	State string `json:"state" yaml:"state" protobuf:"bytes,5,opt,name=state"` | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestNewYAMLDecoder(t *testing.T) { | func TestNewYAMLDecoder(t *testing.T) { | ||||||
| @ -78,3 +81,31 @@ func TestNewJSONStringDecoder(t *testing.T) { | |||||||
| 	docErr := e.Decode(&TestUser{}) | 	docErr := e.Decode(&TestUser{}) | ||||||
| 	assert.Nil(t, docErr) | 	assert.Nil(t, docErr) | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func TestNewDecoder(t *testing.T) { | ||||||
|  | 	pbData, err := proto.Marshal(&TestUser{ Name: "pb", Uid: "15001", Group: "15005", Home: "/home/pb", State: "present" }) | ||||||
|  | 	assert.Nil(t, err) | ||||||
|  | 	for _, v := range []struct{ reader io.Reader; format Format; expectedhome string } { | ||||||
|  | 		{ reader: strings.NewReader(`{ | ||||||
|  |   "name": "testuser", | ||||||
|  |   "uid": "12001", | ||||||
|  |   "group": "12001", | ||||||
|  |   "home": "/home/testuser", | ||||||
|  |   "state": "present" }`), format: FormatJson, expectedhome: "/home/testuser" }, | ||||||
|  | 		{ reader: strings.NewReader(` | ||||||
|  |   name: "testuser" | ||||||
|  |   uid: "12001" | ||||||
|  |   group: "12001" | ||||||
|  |   home: "/home/test" | ||||||
|  |   state: "present" | ||||||
|  | `), format: FormatYaml, expectedhome: "/home/test" }, | ||||||
|  | 		{ reader: bytes.NewReader(pbData), format: FormatProtoBuf, expectedhome: "/home/pb" }, | ||||||
|  | 	} { | ||||||
|  | 
 | ||||||
|  | 		decoder := NewDecoder(v.reader,  v.format) | ||||||
|  | 		assert.NotNil(t, decoder) | ||||||
|  | 		u := &TestUser{} | ||||||
|  | 		assert.Nil(t, decoder.Decode(u)) | ||||||
|  | 		assert.Equal(t, v.expectedhome, u.Home ) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | |||||||
| @ -70,6 +70,10 @@ func (f Format) Decoder(r io.Reader) Decoder { | |||||||
| 	return NewDecoder(r, f) | 	return NewDecoder(r, f) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (f Format) StringDecoder(s string) Decoder { | ||||||
|  | 	return NewStringDecoder(s, f) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (f Format) Serialize(object any, w io.Writer) error { | func (f Format) Serialize(object any, w io.Writer) error { | ||||||
| 	return f.Encoder(w).Encode(object) | 	return f.Encoder(w).Encode(object) | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										10
									
								
								internal/data/config.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								internal/data/config.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,10 @@ | |||||||
|  | // Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
 | ||||||
|  | 
 | ||||||
|  | package data | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type ConfigurationValueGetter interface { | ||||||
|  | 	GetValue(key string) (any, error) | ||||||
|  | } | ||||||
							
								
								
									
										26
									
								
								internal/data/converter.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								internal/data/converter.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,26 @@ | |||||||
|  | // Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
 | ||||||
|  | 
 | ||||||
|  | package data | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // Convert a resource to a document and a document to a resource
 | ||||||
|  | 
 | ||||||
|  | type Emitter interface { | ||||||
|  | 	Emit(document Document, filter ResourceSelector) (Resource, error) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type Extracter interface { | ||||||
|  | 	Extract(resource Resource, filter ResourceSelector) (Document, error) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type Converter interface { | ||||||
|  | 	Typer | ||||||
|  | 	Emitter | ||||||
|  | 	Extracter | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type ManyExtractor interface { | ||||||
|  | 	ExtractMany(resource Resource, filter ResourceSelector) ([]Document, error) | ||||||
|  | } | ||||||
| @ -26,7 +26,7 @@ type Deleter interface { | |||||||
| 	Delete(context.Context) error | 	Delete(context.Context) error | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type Crudder struct { | type Crudder interface { | ||||||
| 	Creator | 	Creator | ||||||
| 	Reader | 	Reader | ||||||
| 	Updater | 	Updater | ||||||
|  | |||||||
							
								
								
									
										51
									
								
								internal/data/document.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								internal/data/document.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,51 @@ | |||||||
|  | // Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
 | ||||||
|  | 
 | ||||||
|  | package data | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"errors" | ||||||
|  | 	"decl/internal/codec" | ||||||
|  | 	"io" | ||||||
|  | 	"decl/internal/mapper" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var ( | ||||||
|  | 	ErrEmptyDocument error = errors.New("Document contains no resources") | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type Serializer interface { | ||||||
|  | 	JSON() ([]byte, error) | ||||||
|  | 	YAML() ([]byte, error) | ||||||
|  | 	PB() ([]byte, error) | ||||||
|  | 	Generate(w io.Writer) (error) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type Loader interface { | ||||||
|  | 	LoadString(string, codec.Format) (error) | ||||||
|  | 	Load([]byte, codec.Format) (error) | ||||||
|  | 	LoadReader(io.ReadCloser, codec.Format) (error) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type DocumentGetter interface { | ||||||
|  | 	GetDocument() Document | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type Document interface { | ||||||
|  | 	Serializer | ||||||
|  | 	Loader | ||||||
|  | 	Validator | ||||||
|  | 	mapper.Mapper | ||||||
|  | 
 | ||||||
|  | 	NewResource(uri string) (Resource, error) | ||||||
|  | 	Types() (TypesRegistry[Resource]) | ||||||
|  | //	Resources() []Declaration
 | ||||||
|  | 
 | ||||||
|  | 	SetConfig(config Document) | ||||||
|  | 	ConfigDoc() Document | ||||||
|  | 	Len() int | ||||||
|  | 	ResolveIds(ctx context.Context) | ||||||
|  | 	Filter(filter DeclarationSelector) []Declaration | ||||||
|  | 
 | ||||||
|  | 	//Diff(with *Document, output io.Writer) (returnOutput string, diffErr error)
 | ||||||
|  | } | ||||||
							
								
								
									
										22
									
								
								internal/data/identifier.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								internal/data/identifier.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | |||||||
|  | // Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
 | ||||||
|  | 
 | ||||||
|  | package data | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var ( | ||||||
|  | 	ErrInvalidURI error = errors.New("Invalid URI") | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type Identifier interface { | ||||||
|  | 	URI() string | ||||||
|  | 	SetURI(string) error | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type Selector[Item comparable] func(r Item) bool | ||||||
|  | 
 | ||||||
|  | type ResourceSelector Selector[Resource] | ||||||
|  | 
 | ||||||
|  | type DeclarationSelector Selector[Declaration] | ||||||
							
								
								
									
										91
									
								
								internal/data/resource.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								internal/data/resource.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,91 @@ | |||||||
|  | // Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
 | ||||||
|  | 
 | ||||||
|  | package data | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"errors" | ||||||
|  | 	"decl/internal/mapper" | ||||||
|  | 	"decl/internal/transport" | ||||||
|  | 	"gitea.rosskeen.house/rosskeen.house/machine" | ||||||
|  | 	"io" | ||||||
|  | 	"io/fs" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var ( | ||||||
|  | 	ErrInvalidResource error = errors.New("Invalid resource") | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type ResourceMapper mapper.Map[string, Declaration] | ||||||
|  | 
 | ||||||
|  | type StateTransformer interface { | ||||||
|  | 	Apply() error | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type Resource interface { | ||||||
|  | 	Identifier | ||||||
|  | 	Type() string | ||||||
|  | 	StateMachine() machine.Stater | ||||||
|  | 	UseConfig(config ConfigurationValueGetter) | ||||||
|  | 	ResolveId(context.Context) string | ||||||
|  | 	Loader | ||||||
|  | 	StateTransformer | ||||||
|  | 	Crudder | ||||||
|  | 	Validator | ||||||
|  | 	Clone() Resource | ||||||
|  | 	SetResourceMapper(ResourceMapper) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type Declaration interface { | ||||||
|  | 	Identifier | ||||||
|  | 	ResourceType() TypeName | ||||||
|  | 	ResolveId(context.Context) string | ||||||
|  | 	Loader | ||||||
|  | 	Validator | ||||||
|  | 	StateTransformer | ||||||
|  | 	Resource() Resource | ||||||
|  | 	Clone() Declaration | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func NewResourceMapper() ResourceMapper { | ||||||
|  | 	return mapper.New[string, Declaration]() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type ContentIdentifier interface { | ||||||
|  | 	ContentType() string | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type ContentReader interface { | ||||||
|  | 	ContentReaderStream() (*transport.Reader, error) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type ContentWriter interface { | ||||||
|  | 	ContentWriterStream() (*transport.Writer, error) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type ContentReadWriter interface { | ||||||
|  | 	ContentReader | ||||||
|  | 	ContentWriter | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type ContentGetter interface { | ||||||
|  | 	GetContent(w io.Writer) (contentReader io.ReadCloser, err error) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type ContentSetter interface { | ||||||
|  | 	SetContent(r io.Reader) error | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type ContentGetSetter interface { | ||||||
|  | 	ContentGetter | ||||||
|  | 	ContentSetter | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type FileResource interface { | ||||||
|  | 	FilePath() string | ||||||
|  | 	SetFileInfo(fs.FileInfo) error | ||||||
|  | 	FileInfo() fs.FileInfo | ||||||
|  | 	ContentGetSetter | ||||||
|  | 	SetContentSourceRef(uri string) | ||||||
|  | } | ||||||
|  | 
 | ||||||
							
								
								
									
										50
									
								
								internal/data/stater.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								internal/data/stater.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,50 @@ | |||||||
|  | // Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
 | ||||||
|  | 
 | ||||||
|  | package data | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"gitea.rosskeen.house/rosskeen.house/machine" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func StorageMachine(sub machine.Subscriber) machine.Stater { | ||||||
|  | // start_destroy -> absent -> start_create -> present -> start_destroy
 | ||||||
|  | 	stater := machine.New("unknown") | ||||||
|  | 	stater.AddStates("initialized", "unkonwn", "absent", "start_create", "present", "start_delete", "start_read", "start_update") | ||||||
|  | 	stater.AddTransition("construct", machine.States("unknown"), "initialized") | ||||||
|  | 	stater.AddTransition("create", machine.States("unknown", "initialized", "absent"), "start_create") | ||||||
|  | 	if e := stater.AddSubscription("create", sub); e != nil { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	stater.AddTransition("created", machine.States("start_create"), "present") | ||||||
|  | 	if e := stater.AddSubscription("created", sub); e != nil { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	stater.AddTransition("exists", machine.States("unknown", "initialized", "absent"), "present") | ||||||
|  | 	if e := stater.AddSubscription("exists", sub); e != nil { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	stater.AddTransition("notexists", machine.States("*"), "absent") | ||||||
|  | 	if e := stater.AddSubscription("notexists", sub); e != nil { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	stater.AddTransition("read", machine.States("*"), "start_read") | ||||||
|  | 	if e := stater.AddSubscription("read", sub); e != nil { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	stater.AddTransition("state_read", machine.States("start_read"), "present") | ||||||
|  | 	stater.AddTransition("update", machine.States("*"), "start_update") | ||||||
|  | 	if e := stater.AddSubscription("update", sub); e != nil { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	stater.AddTransition("updated", machine.States("start_update"), "present") | ||||||
|  | 	stater.AddTransition("delete", machine.States("*"), "start_delete") | ||||||
|  | 	if e := stater.AddSubscription("delete", sub); e != nil { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	stater.AddTransition("deleted", machine.States("start_delete"), "absent") | ||||||
|  | 	if e := stater.AddSubscription("deleted", sub); e != nil { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	return stater | ||||||
|  | } | ||||||
|  | 
 | ||||||
							
								
								
									
										23
									
								
								internal/data/types.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								internal/data/types.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,23 @@ | |||||||
|  | // Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
 | ||||||
|  | 
 | ||||||
|  | package data | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"net/url" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type Factory[Product comparable] func(*url.URL) Product | ||||||
|  | 
 | ||||||
|  | type TypesRegistry[Product comparable] interface { | ||||||
|  | 	New(uri string) (result Product, err error) | ||||||
|  | 	Has(typename string) bool | ||||||
|  | 	//Get(string) Factory[Product] 
 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type TypeName string //`json:"type"`
 | ||||||
|  | 
 | ||||||
|  | func (t TypeName) String() string { return string(t) } | ||||||
|  | 
 | ||||||
|  | type Typer interface { | ||||||
|  | 	Type() TypeName | ||||||
|  | } | ||||||
							
								
								
									
										64
									
								
								internal/folio/uri.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								internal/folio/uri.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,64 @@ | |||||||
|  | // Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
 | ||||||
|  | 
 | ||||||
|  | package folio | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"net/url" | ||||||
|  | 	"decl/internal/transport" | ||||||
|  | 	"decl/internal/data" | ||||||
|  | 	"decl/internal/identifier" | ||||||
|  | 	"errors" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | var ( | ||||||
|  | 	ErrInvalidURI error = errors.New("Invalid URI") | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type URI identifier.ID | ||||||
|  | 
 | ||||||
|  | func (u URI) NewResource(document data.Document) (newResource data.Resource, err error) { | ||||||
|  | 	if document == nil { | ||||||
|  | 		declaration := NewDeclaration() | ||||||
|  | 		if err = declaration.NewResource((*string)(&u)); err == nil { | ||||||
|  | 			return declaration.Attributes.(data.Resource), err | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		newResource, err = document.NewResource(string(u)) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (u URI) Parse() *url.URL { | ||||||
|  | 	url, e := url.Parse(string(u)) | ||||||
|  | 	if e == nil { | ||||||
|  | 		return url | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (u URI) Exists() bool { | ||||||
|  | 	return transport.ExistsURI(string(u)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (u URI) ContentReaderStream() (*transport.Reader, error) { | ||||||
|  | 	return transport.NewReaderURI(string(u)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (u URI) ContentWriterStream() (*transport.Writer, error) { | ||||||
|  | 	return transport.NewWriterURI(string(u)) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (u URI) String() string { | ||||||
|  | 	return string(u) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (u *URI) SetURL(url *url.URL) { | ||||||
|  | 	(*identifier.ID)(u).SetURL(url) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (u URI) Extension() (string, string) { | ||||||
|  | 	return (identifier.ID)(u).Extension() | ||||||
|  | } | ||||||
|  | 
 | ||||||
							
								
								
									
										16
									
								
								internal/folio/uri_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								internal/folio/uri_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | |||||||
|  | // Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
 | ||||||
|  | 
 | ||||||
|  | package folio | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"github.com/stretchr/testify/assert" | ||||||
|  | 	"testing" | ||||||
|  | 	"fmt" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func TestURI(t *testing.T) { | ||||||
|  | 	var file URI = URI(fmt.Sprintf("file://%s", TempDir)) | ||||||
|  | 	u := file.Parse() | ||||||
|  | 	assert.Equal(t, "file", u.Scheme) | ||||||
|  | 	assert.True(t, file.Exists()) | ||||||
|  | } | ||||||
							
								
								
									
										42
									
								
								internal/identifier/id.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								internal/identifier/id.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,42 @@ | |||||||
|  | // Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
 | ||||||
|  | 
 | ||||||
|  | package identifier | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"net/url" | ||||||
|  | 	"errors" | ||||||
|  | 	"strings" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | var ( | ||||||
|  | 	ErrInvalidURI error = errors.New("Invalid URI") | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | type ID string | ||||||
|  | 
 | ||||||
|  | func (i *ID) SetURL(u *url.URL) { | ||||||
|  | 	*i = ID(u.String()) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (i ID) Parse() *url.URL { | ||||||
|  | 	url, e := url.Parse(string(i)) | ||||||
|  | 	if e == nil { | ||||||
|  | 		return url | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (i ID) Extension() (exttype string, fileext string) { | ||||||
|  | 	elements := strings.Split(string(i), ".") | ||||||
|  | 	numberOfElements := len(elements) | ||||||
|  | 	if numberOfElements > 1 { | ||||||
|  | 		if numberOfElements > 2 { | ||||||
|  | 			exttype = elements[numberOfElements - 2] | ||||||
|  | 			fileext = elements[numberOfElements - 1] | ||||||
|  | 		} | ||||||
|  | 		exttype = elements[numberOfElements - 1] | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
							
								
								
									
										44
									
								
								internal/identifier/id_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								internal/identifier/id_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,44 @@ | |||||||
|  | // Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. All rights reserved.
 | ||||||
|  | 
 | ||||||
|  | package identifier | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"github.com/stretchr/testify/assert" | ||||||
|  | 	"testing" | ||||||
|  | 	"fmt" | ||||||
|  | 	"os" | ||||||
|  | 	"log" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var TempDir string | ||||||
|  | 
 | ||||||
|  | func TestMain(m *testing.M) { | ||||||
|  | 	var err error | ||||||
|  | 	TempDir, err = os.MkdirTemp("", "testidentifier") | ||||||
|  | 	if err != nil || TempDir == "" { | ||||||
|  | 		log.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	rc := m.Run() | ||||||
|  | 
 | ||||||
|  | 	os.RemoveAll(TempDir) | ||||||
|  | 	os.Exit(rc) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestID(t *testing.T) { | ||||||
|  | 	var file ID = ID(fmt.Sprintf("file://%s", TempDir)) | ||||||
|  | 	u := file.Parse() | ||||||
|  | 	assert.Equal(t, "file", u.Scheme) | ||||||
|  | 	filetype, fileext := file.Extension() | ||||||
|  | 	assert.Equal(t, "", filetype) | ||||||
|  | 	assert.Equal(t, "", fileext) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func TestSetID(t *testing.T) { | ||||||
|  | 	var file ID = ID(fmt.Sprintf("file://%s", TempDir)) | ||||||
|  | 	u := file.Parse() | ||||||
|  | 
 | ||||||
|  | 	var setFile ID | ||||||
|  | 	setFile.SetURL(u) | ||||||
|  | 	assert.Equal(t, file, setFile) | ||||||
|  | } | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user