├── src ├── fixture │ ├── empty.fzp │ ├── empty_xml.fzp │ ├── empty_fzp_0.fzp │ ├── empty_fzp_1.fzp │ ├── empty_fzp_2.fzp │ ├── fail │ │ ├── fail_0_all.fzp │ │ └── fail_1_title.fzp │ └── pass │ │ └── pass_0.fzp ├── go │ ├── buses.go │ ├── connectors.go │ ├── views_test.go │ ├── viewlayers_test.go │ ├── connector_test.go │ ├── Makefile │ ├── connectorlayer_test.go │ ├── viewlayer_test.go │ ├── busnode_test.go │ ├── busnode.go │ ├── connectorlayer.go │ ├── viewlayers.go │ ├── tags_test.go │ ├── ext │ │ ├── ext_test.go │ │ └── ext.go │ ├── properties_test.go │ ├── viewlayer.go │ ├── bus_test.go │ ├── connector.go │ ├── property.go │ ├── tags.go │ ├── property_test.go │ ├── views.go │ ├── bus.go │ ├── properties.go │ ├── fzp_test.go │ ├── fzp-validate.go │ └── fzp.go └── README.md ├── .gitignore ├── bin ├── fzp │ ├── Makefile │ ├── main_test.go │ ├── main.go │ ├── command-encode.go │ ├── command-format.go │ ├── command-create.go │ ├── command-get.go │ └── command-validate.go └── README.md ├── README.md ├── .travis.yml └── docs ├── template.fzp └── README.md /src/fixture/empty.fzp: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | *.exe 3 | bin/fzp/fzp 4 | *.exe 5 | -------------------------------------------------------------------------------- /src/fixture/empty_xml.fzp: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /bin/fzp/Makefile: -------------------------------------------------------------------------------- 1 | # github.com/fritzing/fzp/bin/fzp/Makefile 2 | 3 | all: test build 4 | ./bin/fzp/fzp --help 5 | 6 | test: 7 | @go test -v -cover 8 | 9 | build: 10 | @go build 11 | 12 | clean: 13 | @go clean 14 | -------------------------------------------------------------------------------- /src/go/buses.go: -------------------------------------------------------------------------------- 1 | package fzp 2 | 3 | // Buses store all Bus objects as an array. 4 | type Buses []Bus 5 | 6 | // NewBuses return a new Buses object. 7 | func NewBuses() *Buses { 8 | b := Buses{} 9 | b = make([]Bus, 0) 10 | return &b 11 | } 12 | -------------------------------------------------------------------------------- /src/go/connectors.go: -------------------------------------------------------------------------------- 1 | package fzp 2 | 3 | // Connectors array 4 | type Connectors []Connector 5 | 6 | // NewConnectors return a new Connectors object 7 | func NewConnectors() Connectors { 8 | c := Connectors{} 9 | c = make([]Connector, 0) 10 | return c 11 | } 12 | -------------------------------------------------------------------------------- /src/go/views_test.go: -------------------------------------------------------------------------------- 1 | package fzp 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func Test_NewViews(t *testing.T) { 8 | testViews := NewViews() 9 | t.Log(testViews) 10 | // err := testViews.Check() 11 | // if err != nil { 12 | // t.Error(err) 13 | // } 14 | } 15 | -------------------------------------------------------------------------------- /src/go/viewlayers_test.go: -------------------------------------------------------------------------------- 1 | package fzp 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func Test_NewViewLayers(t *testing.T) { 8 | layers := NewViewLayers() 9 | t.Log(layers) 10 | // err := layers.Check() 11 | // if err != nil { 12 | // t.Error(err) 13 | // } 14 | } 15 | -------------------------------------------------------------------------------- /bin/README.md: -------------------------------------------------------------------------------- 1 | # fzp tool 2 | 3 | ## Validator 4 | simple and fast validator to test the fritzing-parts repository (over 1400 parts). 5 | i think [go](https://golang.org) is excelent for this job. 6 | if the validator is ready we can add travis-ci as test service to the fritzing-parts repository. 7 | -------------------------------------------------------------------------------- /src/go/connector_test.go: -------------------------------------------------------------------------------- 1 | package fzp 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func Test_Connector(t *testing.T) { 8 | con1 := NewConnector("Test-Connector") 9 | err := con1.Check() 10 | if err != nil && con1.ID == "Test-Connector" { 11 | t.Error("Connector test failed") 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/go/Makefile: -------------------------------------------------------------------------------- 1 | # github.com/fritzing/fzp/src/go/Makefile 2 | 3 | all: test lint 4 | 5 | test: 6 | @go test -v -cover ./... 7 | 8 | cover: 9 | go test -coverprofile cover.out 10 | go tool cover -html=cover.out -o cover.html 11 | 12 | lint: 13 | @golint 14 | 15 | clean: 16 | rm -f cover.out 17 | rm -f cover.html 18 | -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | # fzp-src 2 | 3 | ## Libraries/Code 4 | 5 | ### Qt 6 | here i think lifes the fritzing-app fzp code... 7 | [fritzing-app](https://github.com/fritzing/fritzing-app/blob/bcf97e72b45fc6d9be836e5014fc53f5eabe0048/src/model/modelpart.h#L114) Qt sourcecode link 8 | 9 | ### go 10 | the `fzp` data model as a struct 11 | -------------------------------------------------------------------------------- /src/go/connectorlayer_test.go: -------------------------------------------------------------------------------- 1 | package fzp 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func Test_ConnectorLayer(t *testing.T) { 8 | cLayer := NewConnectorLayer() 9 | cLayer.Layer = "l1" 10 | cLayer.SvgID = "s1" 11 | cLayer.TerminalID = "t1" 12 | if cLayer.Layer != "l1" { 13 | t.Error("Connector test failed") 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/go/viewlayer_test.go: -------------------------------------------------------------------------------- 1 | package fzp 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func Test_ViewLayer(t *testing.T) { 8 | layer := NewViewLayer("TEST-ID") 9 | if layer.LayerID != "TEST-ID" { 10 | t.Error("LayerID not equal") 11 | } 12 | 13 | // err := layer.Check() 14 | // if err != nil { 15 | // t.Error(err) 16 | // } 17 | } 18 | -------------------------------------------------------------------------------- /src/go/busnode_test.go: -------------------------------------------------------------------------------- 1 | package fzp 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func Test_BusNode(t *testing.T) { 8 | bus1 := NewBusNode("Test-BusNode") 9 | 10 | err := bus1.Check() 11 | if err != nil { 12 | t.Error("BusNode.Check test failed") 13 | } 14 | 15 | bus1.ConnectorID = "" 16 | err = bus1.Check() 17 | if err == nil { 18 | t.Error("BusNode.Check test failed") 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/fixture/empty_fzp_0.fzp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [fritzing](https://github.com/fritzing) fzp specs & tools [![Build Status](https://travis-ci.org/fritzing/fzp.svg)](https://travis-ci.org/fritzing/fzp) 2 | 3 | - [offical documentation from fritzing creator] (https://github.com/fritzing/fritzing-app/wiki/2.1-Part-file-format) 4 | - [docs](docs/README.md) fzp file specification _(DRAFT)_ 5 | - [bin](bin/README.md) store fzp tools like validator or converter 6 | - [src](src/README.md) store fzp libraries (go, TODO: js) 7 | -------------------------------------------------------------------------------- /bin/fzp/main_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestMain(t *testing.T) { 8 | // TODO: test commands 9 | t.SkipNow() 10 | } 11 | 12 | // func Test_dataHandler_dir(t *testing.T) { 13 | // dataHandler("./fixture/pass", dirHandler, fileHandler) 14 | // } 15 | // 16 | // func Test_dataHandler_file(t *testing.T) { 17 | // dataHandler("./fixture/pass/pass_0.fzp", dirHandler, fileHandler) 18 | // } 19 | // 20 | // func dirHandler() {} 21 | // 22 | // func fileHandler() {} 23 | -------------------------------------------------------------------------------- /src/go/busnode.go: -------------------------------------------------------------------------------- 1 | package fzp 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | // BusNode represet a fzp BusNode data object 8 | type BusNode struct { 9 | ConnectorID string `xml:"connectorId,attr" json:"connectorId"` 10 | } 11 | 12 | // NewBusNode returns a BusNode object 13 | func NewBusNode(id string) BusNode { 14 | b := BusNode{} 15 | b.ConnectorID = id 16 | return b 17 | } 18 | 19 | // Check validate the BusNode data 20 | func (b *BusNode) Check() error { 21 | if b.ConnectorID == "" { 22 | return errors.New("BusNode ConnectorID undefined") 23 | } 24 | return nil 25 | } 26 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.13 5 | - tip 6 | 7 | # workarround to successful build a forked version of the fzp repository 8 | #before_install: 9 | # - mkdir -p $HOME/gopath/src/github.com/fritzing 10 | # - ln -s -T ${TRAVIS_BUILD_DIR} $HOME/gopath/src/github.com/fritzing/fzp 11 | # - export TRAVIS_BUILD_DIR=$HOME/gopath/src/github.com/fritzing/fzp 12 | 13 | install: 14 | - go get github.com/paulvollmer/commenttags/cmd/commenttags 15 | - go get ./... 16 | 17 | script: 18 | - cd ${TRAVIS_BUILD_DIR}/src/go && make test 19 | - cd ${TRAVIS_BUILD_DIR}/bin/fzp && make test 20 | - cd ${TRAVIS_BUILD_DIR} && commenttags ./ 21 | -------------------------------------------------------------------------------- /src/go/connectorlayer.go: -------------------------------------------------------------------------------- 1 | package fzp 2 | 3 | // ConnectorLayer represet a fzp ConnectorLayer data object 4 | type ConnectorLayer struct { 5 | Layer string `xml:"layer,attr" json:"layer"` 6 | SvgID string `xml:"svgId,attr" json:"svgId"` 7 | TerminalID string `xml:"terminalId,attr" json:"terminalId"` 8 | } 9 | 10 | // NewConnectorLayer returns a ConnectorLayer object 11 | func NewConnectorLayer() ConnectorLayer { 12 | cLayer := ConnectorLayer{} 13 | return cLayer 14 | } 15 | 16 | // Check validate the ConnectorLayer data 17 | func (c *ConnectorLayer) Check() error { 18 | // TODO: create check rules 19 | return nil 20 | } 21 | -------------------------------------------------------------------------------- /src/go/viewlayers.go: -------------------------------------------------------------------------------- 1 | package fzp 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | // ViewLayers represet a fzp ViewLayers data object 8 | type ViewLayers struct { 9 | Image string `xml:"image,attr" json:"image"` 10 | Layer []ViewLayer `xml:"layer" json:"layer"` 11 | } 12 | 13 | // NewViewLayers returns a ViewLayers object 14 | func NewViewLayers() ViewLayers { 15 | v := ViewLayers{} 16 | v.Layer = make([]ViewLayer, 0) 17 | return v 18 | } 19 | 20 | // Check validate the ViewLayers data 21 | func (v *ViewLayers) Check() error { 22 | // TODO: create check rules 23 | 24 | if v.Image == "" { 25 | return errors.New("missing layer image") 26 | } 27 | 28 | return nil 29 | } 30 | -------------------------------------------------------------------------------- /src/fixture/empty_fzp_1.fzp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/go/tags_test.go: -------------------------------------------------------------------------------- 1 | package fzp 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func Test_Tags(t *testing.T) { 8 | tags := NewTags() 9 | if tags.Total() != 0 { 10 | t.Error("Tags Total not equal") 11 | } 12 | 13 | tags.Add("helllo") 14 | tags.Add("world") 15 | if tags.Total() != 2 { 16 | t.Error("Tags Total not equal") 17 | } 18 | 19 | err := tags.Add("world") 20 | if err == nil { 21 | t.Error("Tags Add 'world' missing error (world already exist)") 22 | } 23 | } 24 | 25 | // TODO: test table 26 | func Test_Tags_Check(t *testing.T) { 27 | tags := Tags{} 28 | tags = []string{"hello", "world", "foo", "hello"} 29 | err := tags.Check() 30 | // t.Log(err) 31 | if err == nil { 32 | t.Error("missing error (hello duplicated)", err) 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/go/ext/ext_test.go: -------------------------------------------------------------------------------- 1 | package ext 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | var testData = []struct { 8 | filename string 9 | valid bool 10 | }{ 11 | {"test.fzp", true}, 12 | {"test.FZP", false}, 13 | {"test.json", true}, 14 | {"test.JSON", false}, 15 | {"test.yml", true}, 16 | {"test.YML", false}, 17 | {"test.yaml", false}, 18 | {"test.YAML", false}, 19 | {"test.xml", false}, 20 | {"test.XML", false}, 21 | {"test.unknown", false}, 22 | 23 | {"path/to/the/fritzing/part.fzp", true}, 24 | {"path/to/the/fritzing/part/but/not/fzp.file", false}, 25 | } 26 | 27 | func Test_IsValid(t *testing.T) { 28 | for _, tt := range testData { 29 | v, _ := IsValid(tt.filename) 30 | if v != tt.valid { 31 | t.Errorf("%s is not equal\n", tt.filename) 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/go/properties_test.go: -------------------------------------------------------------------------------- 1 | package fzp 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func Test_Properties(t *testing.T) { 8 | props := NewProperties("test-fam") 9 | if props.Total() != 1 { 10 | t.Error("NewProperties returned object missing family property") 11 | } 12 | 13 | tmp, err := props.GetValue("family") 14 | if err != nil { 15 | t.Error(err) 16 | } 17 | if tmp != "test-fam" { 18 | t.Error("Properties family not equal") 19 | } 20 | 21 | err = props.AddValue("foo", "F") 22 | if err != nil { 23 | t.Error(err) 24 | } 25 | err = props.AddValue("bar", "B") 26 | if err != nil { 27 | t.Error(err) 28 | } 29 | 30 | if props.Total() != 3 { 31 | t.Error("Properties Total not equal") 32 | } 33 | 34 | // test if an error was returned if the prop exist... 35 | err = props.AddValue("foo", "F") 36 | if err == nil { 37 | t.Error("missing error") 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/go/viewlayer.go: -------------------------------------------------------------------------------- 1 | package fzp 2 | 3 | // ViewLayer represet a fzp ViewLayer data object 4 | type ViewLayer struct { 5 | LayerID string `xml:"layerId,attr" json:"layerId"` 6 | 7 | // BreadboardLayer string `xml:"layerId,attr" json:"breadboard"` 8 | // SchematicLayer string `xml:"layerId,attr" json:"schematic"` 9 | // PcbLayerCopper0 string `xml:"layerId,attr" json:"copper0"` 10 | // PcbLayerCopper1 string `xml:"layerId,attr" json:"copper1"` 11 | // PcbLayerSilkscreen string `xml:"layerId,attr" json:"silkscreen"` 12 | // IconLayer string `xml:"layerId,attr" json:"icon"` 13 | } 14 | 15 | // NewViewLayer returns a ViewLayer object 16 | func NewViewLayer(id string) ViewLayer { 17 | v := ViewLayer{} 18 | v.LayerID = id 19 | return v 20 | } 21 | 22 | // Check validate the ViewLayer data 23 | func (v *ViewLayer) Check() error { 24 | // TODO: create check rules 25 | return nil 26 | } 27 | -------------------------------------------------------------------------------- /src/fixture/empty_fzp_2.fzp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/go/bus_test.go: -------------------------------------------------------------------------------- 1 | package fzp 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func Test_NewBus(t *testing.T) { 8 | b := NewBus("Test") 9 | if b.ID != "Test" { 10 | t.Error("Bus NewBus test faild") 11 | } 12 | } 13 | 14 | func Test_Bus_CheckId(t *testing.T) { 15 | // test without data (the empty state) 16 | b := Bus{} 17 | err := b.CheckID() 18 | if err == nil { 19 | t.Error("Bus test failed") 20 | } 21 | 22 | // test with data 23 | b.ID = "test" 24 | err = b.CheckID() 25 | if err != nil { 26 | t.Error("Bus test failed") 27 | } 28 | } 29 | 30 | func Test_Bus_CheckNodeMembers(t *testing.T) { 31 | b := Bus{} 32 | err := b.CheckNodeMembers() 33 | if err == nil { 34 | t.Error("Bus test failed") 35 | } 36 | 37 | b.AddNodeMember("node1") 38 | if len(b.NodeMember) != 1 && b.NodeMember[0].ConnectorID != "node1" { 39 | t.Error("AddNodeMember broken...") 40 | } 41 | err = b.CheckNodeMembers() 42 | if err != nil { 43 | t.Error("Bus test failed") 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/go/connector.go: -------------------------------------------------------------------------------- 1 | package fzp 2 | 3 | // Connector represet a fzp Connector data object 4 | type Connector struct { 5 | ID string `xml:"id,attr" json:"id"` 6 | Name string `xml:"name,attr" json:"name"` 7 | Type string `xml:"type,attr" json:"type"` 8 | Description string `xml:"description" json:"description"` 9 | BreadboardView []ConnectorLayer `xml:"views>breadboardView>p" json:"breadboard"` 10 | PcbView []ConnectorLayer `xml:"views>pcbView>p" json:"pcb"` 11 | SchematicView []ConnectorLayer `xml:"views>schematicView>p" json:"schematic"` 12 | } 13 | 14 | // NewConnector returns a Connector object 15 | func NewConnector(id string) Connector { 16 | con := Connector{} 17 | con.ID = id 18 | con.BreadboardView = make([]ConnectorLayer, 0) 19 | con.PcbView = make([]ConnectorLayer, 0) 20 | con.SchematicView = make([]ConnectorLayer, 0) 21 | return con 22 | } 23 | 24 | // Check validate the Connector data 25 | func (c *Connector) Check() error { 26 | // TODO: create check rules 27 | return nil 28 | } 29 | -------------------------------------------------------------------------------- /src/go/property.go: -------------------------------------------------------------------------------- 1 | package fzp 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | // Property represet a fzp Property data object 8 | type Property struct { 9 | Name string `xml:"name,attr" json:"name"` 10 | Value string `xml:",chardata" json:"value"` 11 | } 12 | 13 | // NewProperty returns a Property object 14 | func NewProperty(n, v string) Property { 15 | p := Property{} 16 | p.Name = n 17 | p.Value = v 18 | return p 19 | } 20 | 21 | // Check validate the Property data 22 | func (p *Property) Check() error { 23 | errMsg := "" 24 | if err := p.CheckName(); err != nil { 25 | errMsg = err.Error() 26 | } 27 | if err := p.CheckValue(); err != nil { 28 | errMsg += ", " + err.Error() 29 | return errors.New(errMsg) 30 | } 31 | return nil 32 | } 33 | 34 | // CheckName validate the Property Name data 35 | func (p *Property) CheckName() error { 36 | if p.Name == "" { 37 | return errors.New("property name undefined") 38 | } 39 | return nil 40 | } 41 | 42 | // CheckValue validate the Property Value data 43 | func (p *Property) CheckValue() error { 44 | if p.Value == "" { 45 | return errors.New("property value undefined") 46 | } 47 | return nil 48 | } 49 | -------------------------------------------------------------------------------- /src/go/ext/ext.go: -------------------------------------------------------------------------------- 1 | package ext 2 | 3 | import ( 4 | "errors" 5 | "path/filepath" 6 | ) 7 | 8 | const ( 9 | // ExtFzp store the .fzp extension as constant variable 10 | ExtFzp = ".fzp" 11 | // ExtJSON store the .json extension as constant variable 12 | ExtJSON = ".json" 13 | // ExtYAML store the .yml extension as constant variable 14 | ExtYAML = ".yml" 15 | ) 16 | 17 | var errorUseLowercase = errors.New("Not common file extension! Please use lowercase for the extension") 18 | 19 | // IsValid checks if a filename extension is valid. if not, return a description about the issue 20 | func IsValid(filename string) (bool, error) { 21 | fileExt := filepath.Ext(filename) 22 | switch fileExt { 23 | 24 | case ExtFzp, ExtJSON, ExtYAML: 25 | return true, nil 26 | 27 | case ".FZP": 28 | return false, errorUseLowercase 29 | case ".JSON": 30 | return false, errorUseLowercase 31 | case ".YML", ".YAML", ".yaml": 32 | return false, errorUseLowercase 33 | case ".XML", ".xml": 34 | return false, errors.New("Not common file extension! Use .fzp instead of " + fileExt) 35 | 36 | default: 37 | return false, errors.New("Unknown file extension! (" + fileExt + ")") 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/fixture/fail/fail_0_all.fzp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | the connector 1 16 | 17 | 18 |

19 |
20 | 21 |

22 |

23 |
24 | 25 |

26 |
27 |
28 |
29 |
30 | 31 | 32 | 33 | 34 | 35 | 36 |
37 | -------------------------------------------------------------------------------- /src/go/tags.go: -------------------------------------------------------------------------------- 1 | package fzp 2 | 3 | import ( 4 | "errors" 5 | // "fmt" 6 | "sort" 7 | ) 8 | 9 | // Tags array 10 | type Tags []string 11 | 12 | // NewTags return a new Tags object 13 | func NewTags() Tags { 14 | t := Tags{} 15 | t = make([]string, 0) 16 | return t 17 | } 18 | 19 | // Total return the total number of tags 20 | func (t *Tags) Total() int { 21 | return len(*t) 22 | } 23 | 24 | // Exist return true if a tag with the given value exist 25 | func (t *Tags) Exist(tag string) int { 26 | count := 0 27 | for _, v := range *t { 28 | if v == tag { 29 | count++ 30 | } 31 | } 32 | return count 33 | } 34 | 35 | // Add a tag to the array 36 | func (t *Tags) Add(tag string) error { 37 | if t.Exist(tag) != 0 { 38 | return errors.New("tag name '" + tag + "' already exist") 39 | } 40 | *t = append(*t, tag) 41 | return nil 42 | } 43 | 44 | // Check the Tags 45 | func (t *Tags) Check() error { 46 | for _, v := range *t { 47 | // TODO: check if upper / lowercase version exist 48 | total := sort.SearchStrings(*t, v) 49 | // fmt.Println("total:", total) 50 | if total != 0 { 51 | // fmt.Println("DUPLICATE", v) 52 | 53 | // TODO: collect all errors and return after for loop was finished 54 | return errors.New("duplicated tag: " + v) 55 | } 56 | } 57 | return nil 58 | } 59 | -------------------------------------------------------------------------------- /src/go/property_test.go: -------------------------------------------------------------------------------- 1 | package fzp 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func Test_NewProperty(t *testing.T) { 8 | p := NewProperty("p1", "foo") 9 | if p.Name != "p1" && p.Value != "foo" { 10 | t.Error("Property data not equal") 11 | } 12 | } 13 | 14 | func Test_Property_Check(t *testing.T) { 15 | // empty 16 | p1 := NewProperty("", "") 17 | err1 := p1.Check() 18 | if err1 == nil { 19 | t.Error("Property.Check missing error") 20 | } 21 | 22 | // with data 23 | p2 := NewProperty("foo", "bar") 24 | err2 := p2.Check() 25 | if err2 != nil { 26 | t.Error(err2) 27 | } 28 | } 29 | 30 | func Test_Property_CheckName(t *testing.T) { 31 | p := Property{} 32 | // empty 33 | err := p.CheckName() 34 | if err == nil { 35 | t.Error("Property.CheckName test failed") 36 | } 37 | // with data 38 | p.Name = "test" 39 | err = p.CheckName() 40 | if err != nil { 41 | t.Error("Property.CheckName test failed - ERROR:", err) 42 | } 43 | } 44 | 45 | func Test_Property_CheckValue(t *testing.T) { 46 | p := Property{} 47 | // empty 48 | err := p.CheckValue() 49 | if err == nil { 50 | t.Error("Property.CheckValue test failed") 51 | } 52 | // with data 53 | p.Value = "test" 54 | err = p.CheckValue() 55 | if err != nil { 56 | t.Error("Property.CheckValue test failed - ERROR:", err) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/go/views.go: -------------------------------------------------------------------------------- 1 | package fzp 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "strings" 7 | ) 8 | 9 | // Views represet a fzp Views data object 10 | type Views struct { 11 | Icon ViewLayers `xml:"iconView>layers" json:"icon"` 12 | Breadboard ViewLayers `xml:"breadboardView>layers" json:"breadboard"` 13 | Pcb ViewLayers `xml:"pcbView>layers" json:"pcb"` 14 | Schematic ViewLayers `xml:"schematicView>layers" json:"schematic"` 15 | } 16 | 17 | // NewViews returns a Views object 18 | func NewViews() Views { 19 | v := Views{} 20 | v.Icon = NewViewLayers() 21 | 22 | fmt.Println("create copper0---------------------") 23 | // v.Layer = append(v.Layer, NewViewLayer("copper0")) 24 | 25 | v.Breadboard = NewViewLayers() 26 | v.Pcb = NewViewLayers() 27 | v.Schematic = NewViewLayers() 28 | return v 29 | } 30 | 31 | // Check validate the Views data. this calls the icon, breadboard, pcb and 32 | // schematic check funcs and concatendate the error messages 33 | func (v *Views) Check() error { 34 | var errConcat []string 35 | 36 | if err := v.Icon.Check(); err != nil { 37 | errConcat = append(errConcat, err.Error()) 38 | } 39 | if err := v.Breadboard.Check(); err != nil { 40 | errConcat = append(errConcat, err.Error()) 41 | } 42 | if err := v.Pcb.Check(); err != nil { 43 | errConcat = append(errConcat, err.Error()) 44 | } 45 | if err := v.Schematic.Check(); err != nil { 46 | errConcat = append(errConcat, err.Error()) 47 | } 48 | 49 | if len(errConcat) != 0 { 50 | return errors.New(strings.Join(errConcat, "\n")) 51 | } 52 | return nil 53 | } 54 | -------------------------------------------------------------------------------- /src/go/bus.go: -------------------------------------------------------------------------------- 1 | package fzp 2 | 3 | import ( 4 | "errors" 5 | "strings" 6 | ) 7 | 8 | // Bus represet a fzp bus data object 9 | type Bus struct { 10 | ID string `xml:"id,attr" json:"id"` 11 | NodeMember []BusNode `xml:"nodeMember" json:"nodeMember"` 12 | } 13 | 14 | // NewBus returns a Bus object 15 | func NewBus(id string) Bus { 16 | b := Bus{} 17 | b.ID = id 18 | b.NodeMember = make([]BusNode, 0) 19 | return b 20 | } 21 | 22 | // Check validate the Bus data 23 | func (b *Bus) Check() error { 24 | errMsg := "" 25 | if err := b.CheckID(); err != nil { 26 | errMsg = err.Error() 27 | } 28 | if err := b.CheckNodeMembers(); err != nil { 29 | errMsg += ", " + err.Error() 30 | return errors.New(errMsg) 31 | } 32 | return nil 33 | } 34 | 35 | // CheckID validate the Bus ID data 36 | func (b *Bus) CheckID() error { 37 | if b.ID == "" { 38 | return errors.New("bus id undefined") 39 | } 40 | return nil 41 | } 42 | 43 | // CheckNodeMembers validate the Bus NodeMember data 44 | func (b *Bus) CheckNodeMembers() error { 45 | if len(b.NodeMember) == 0 { 46 | return errors.New("bus nodemembers undefined") 47 | } 48 | var errConcat []string 49 | for _, n := range b.NodeMember { 50 | err := n.Check() 51 | if err != nil { 52 | errConcat = append(errConcat, err.Error()) 53 | } 54 | } 55 | if len(errConcat) != 0 { 56 | return errors.New(strings.Join(errConcat, "\n")) 57 | } 58 | return nil 59 | } 60 | 61 | // AddNodeMember adds a BusNode to the NodeMember array 62 | func (b *Bus) AddNodeMember(id string) { 63 | nMember := BusNode{} 64 | nMember.ConnectorID = id 65 | b.NodeMember = append(b.NodeMember, nMember) 66 | } 67 | -------------------------------------------------------------------------------- /src/go/properties.go: -------------------------------------------------------------------------------- 1 | package fzp 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | // Properties array 8 | type Properties []Property 9 | 10 | // NewProperties return a new Properties object 11 | func NewProperties(famname string) Properties { 12 | p := Properties{} 13 | p = make([]Property, 0) 14 | // the 'family' property is required by the fritzing app 15 | p = append(p, NewProperty("family", famname)) 16 | return p 17 | } 18 | 19 | // Total return the total number of properties 20 | func (p *Properties) Total() int { 21 | return len(*p) 22 | } 23 | 24 | // AddValue a new property 25 | func (p *Properties) AddValue(name, val string) error { 26 | _, err := p.GetValue(name) 27 | if err != nil { 28 | newProp := NewProperty(name, val) 29 | *p = append(*p, newProp) 30 | return nil 31 | } 32 | return errors.New("exist") 33 | } 34 | 35 | // GetValue return a property 36 | func (p *Properties) GetValue(name string) (string, error) { 37 | for _, v := range *p { 38 | if v.Name == name { 39 | return v.Value, nil 40 | } 41 | } 42 | return "", errors.New("property '" + name + "' does not exist") 43 | } 44 | 45 | // Exist return true is a property name exist 46 | func (p *Properties) Exist(name string) error { 47 | // TODO:... 48 | return nil 49 | } 50 | 51 | // Check the properties 52 | func (p *Properties) Check() error { 53 | // check if each property name only exist once a time 54 | var tmp map[string]bool 55 | tmp = make(map[string]bool, 0) 56 | for _, prop := range *p { 57 | //TODO: check if a property with name="family" exists and is not empty 58 | if !tmp[prop.Name] { 59 | tmp[prop.Name] = true 60 | } else { 61 | return errors.New("property name '" + prop.Name + "' already exist") 62 | } 63 | } 64 | return nil 65 | } 66 | -------------------------------------------------------------------------------- /bin/fzp/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/urfave/cli" 5 | "os" 6 | ) 7 | 8 | func main() { 9 | app := cli.NewApp() 10 | app.Name = "fzp" 11 | app.Usage = "fzp tool (validator, encoder, formatter)" 12 | app.Version = "0.2.6" 13 | //app.Email = "https://github.com/fritzing/fzp" 14 | app.Commands = []cli.Command{ 15 | { 16 | Name: "validate", 17 | Usage: "validate fzp file/files", 18 | Flags: commandValidateFlags, 19 | Action: commandValidateAction, 20 | }, 21 | { 22 | Name: "encode", 23 | Usage: "read a fzp file and encode to json", 24 | Flags: commandEncodeFlags, 25 | Action: commandEncodeAction, 26 | }, 27 | { 28 | Name: "format", 29 | Usage: "read a fzp file and format it", 30 | Flags: commandFormatFlags, 31 | Action: commandFormatAction, 32 | }, 33 | { 34 | Name: "get", 35 | Usage: "get data from a fzp file", 36 | Flags: commandGetFlags, 37 | Action: commandGetAction, 38 | }, 39 | { 40 | Name: "create", 41 | Usage: "create a new fzp dataset", 42 | Flags: commandCreateFlags, 43 | Action: commandCreateAction, 44 | }, 45 | } 46 | // cli.HelpPrinter = func(w io.Writer, templ string, data interface{}) { 47 | // fmt.Println("TODO: better main help") 48 | // } 49 | 50 | ac := func(c *cli.Context) error { 51 | err := cli.ShowAppHelp(c) 52 | return err 53 | } 54 | 55 | app.Action = ac 56 | app.Run(os.Args) 57 | } 58 | 59 | // check if the source is a directory of a file and call a handler func 60 | func dataHandler(source string, dir, file func()) { 61 | finfo, err := os.Stat(source) 62 | if err != nil { // no such file or dir 63 | cli.NewExitError(err.Error(), 1) 64 | os.Exit(127) 65 | } 66 | if finfo.IsDir() { 67 | dir() 68 | } else { 69 | file() 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/go/fzp_test.go: -------------------------------------------------------------------------------- 1 | package fzp 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func Test_GetFormat(t *testing.T) { 8 | format, _ := GetFormat("FZP") 9 | if format != FormatFzp { 10 | t.Error("not equal") 11 | } 12 | 13 | format, _ = GetFormat("data.fzp") 14 | if format != FormatFzp { 15 | t.Error("not equal") 16 | } 17 | 18 | format, _ = GetFormat("data.json") 19 | if format != FormatJSON { 20 | t.Error("not equal") 21 | } 22 | 23 | format, _ = GetFormat("data.yaml") 24 | if format != FormatYAML { 25 | t.Error("not equal") 26 | } 27 | } 28 | 29 | var TestTable_Read = []struct { 30 | src string 31 | readError bool 32 | checkError bool 33 | }{ 34 | {"../fixture/empty.fzp", true, true}, 35 | {"../fixture/empty_xml.fzp", true, true}, 36 | {"../fixture/empty_fzp_0.fzp", false, true}, 37 | // {"../fixture/empty_fzp_1.fzp", false, true}, 38 | // {"../fixture/empty_fzp_2.fzp", false, true}, 39 | 40 | // passed 41 | // {"../fixture/pass/pass_0.fzp", false, false}, 42 | 43 | // failed 44 | // {"../fixture/fail/fail_0_all.fzp", false, true}, 45 | // {"../fixture/fail/fail_1_title.fzp", false, true}, 46 | } 47 | 48 | func Test_ReadFzp_Ok(t *testing.T) { 49 | totalTests := len(TestTable_Read) 50 | for k, v := range TestTable_Read { 51 | t.Logf("Run Test %v of %v - %q\n", k, totalTests, v.src) 52 | f, _, errFile := ReadFzp(v.src) 53 | 54 | if v.readError { 55 | if errFile == nil { 56 | t.Error("Fzp.ReadFzp missing error", errFile) 57 | } 58 | } 59 | 60 | errCheck := f.Check() 61 | if v.checkError { 62 | if errCheck == nil { 63 | t.Error("Fzp.Check missing error:", errCheck) 64 | } 65 | } 66 | } 67 | } 68 | 69 | func Test_ReadFzp_Failed(t *testing.T) { 70 | _, _, err := ReadFzp("../not.found") 71 | if err == nil { 72 | t.Error("Fzp.ReadFzp (that doesn't exists) broken") 73 | } 74 | } 75 | 76 | func Test_ReadFzp_CheckTags(t *testing.T) { 77 | // fake data 78 | fzpData := Fzp{} 79 | fzpData.Tags = append(fzpData.Tags, "") 80 | // was an error returned? 81 | err, _ := fzpData.CheckTags() 82 | if err == nil { 83 | t.Error("Fzp.CheckTags broken") 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/fixture/fail/fail_1_title.fzp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 0.0.0 4 | 5 | some words about the part 6 | First LastName 7 | yyyy.mm.dd 8 | http://part.url 9 | 10 | 11 | tagvalue1 12 | tagvalue2 13 | tagvalue3 14 | 15 | 16 | value-1 17 | value-2 18 | value-3 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | the connector 1 47 | 48 | 49 |

50 |
51 | 52 |

53 |

54 |
55 | 56 |

57 |
58 |
59 |
60 |
61 | 62 | 63 | 64 | 65 | 66 | 67 |
68 | -------------------------------------------------------------------------------- /src/fixture/pass/pass_0.fzp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 0.0.0 4 | part-name 5 | some words about the part 6 | First LastName 7 | yyyy.mm.dd 8 | http://part.url 9 | 10 | 11 | tagvalue1 12 | tagvalue2 13 | tagvalue3 14 | 15 | 16 | value-1 17 | value-2 18 | value-3 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | the connector 1 47 | 48 | 49 |

50 |
51 | 52 |

53 |

54 |
55 | 56 |

57 |
58 |
59 |
60 |
61 | 62 | 63 | 64 | 65 | 66 | 67 |
68 | -------------------------------------------------------------------------------- /docs/template.fzp: -------------------------------------------------------------------------------- 1 | 2 | 3 | x.y.z 4 | part-name 5 | some words about the part 6 | First LastName 7 | yyyy.mm.dd 8 | http://part.url 9 | 10 | 11 | tagvalue1 12 | tagvalue2 13 | tagvalue3 14 | 15 | 16 | value-1 17 | value-2 18 | value-3 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | the connector 1 47 | 48 | 49 |

50 | 51 | 52 |

53 | 54 | 55 |

56 |

57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /bin/fzp/command-encode.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "io/ioutil" 7 | "os" 8 | "path" 9 | 10 | "github.com/fritzing/fzp/src/go" 11 | "github.com/urfave/cli" 12 | ) 13 | 14 | var commandEncodeFlags = []cli.Flag{ 15 | &cli.StringFlag{ 16 | Name: "input, i", 17 | Usage: "the input file. supported formats: xml, json and yaml", 18 | }, 19 | &cli.StringFlag{ 20 | Name: "output, o", 21 | Usage: "the output file or format. supported formats: xml, json and yaml", 22 | }, 23 | } 24 | 25 | func commandEncodeAction(c *cli.Context) error { 26 | flagInput := c.String("input") 27 | flagOutput := c.String("output") 28 | fmt.Println("in", flagInput, "out", flagOutput) 29 | 30 | if flagInput == "" { 31 | fmt.Println("missing -input flag") 32 | os.Exit(1) 33 | } 34 | if flagOutput == "" { 35 | fmt.Println("missing -output flag") 36 | os.Exit(1) 37 | } 38 | 39 | encodeDirHandler := func() { 40 | if err := encodeDir(flagInput, flagOutput); err != nil { 41 | fmt.Println("Error", err) 42 | os.Exit(127) 43 | } 44 | } 45 | 46 | encodeFileHandler := func() { 47 | if err := encodeFile(flagInput, flagOutput); err != nil { 48 | fmt.Println("Error", err) 49 | os.Exit(127) 50 | } 51 | } 52 | 53 | dataHandler(flagInput, encodeDirHandler, encodeFileHandler) 54 | return nil 55 | } 56 | 57 | func encodeFile(source, out string) error { 58 | // fmt.Println("encode file", source, out) 59 | 60 | // read 61 | format, _ := fzp.GetFormat(source) 62 | if format != fzp.FormatFzp { 63 | return errors.New("Error: input file '" + source + "' is not a fzp file!") 64 | } 65 | loadedFzp, _, err := fzp.ReadFzp(source) 66 | if err != nil { 67 | fmt.Println(err) 68 | return err 69 | } 70 | 71 | // write := false 72 | 73 | // format 74 | outFormat, isOutFile := fzp.GetFormat(out) 75 | // fmt.Println("outFormat", outFormat) 76 | 77 | formatted, err := loadedFzp.Marshal(outFormat) 78 | if err != nil { 79 | return err 80 | } 81 | 82 | if isOutFile { 83 | err := ioutil.WriteFile(out, formatted, 0755) 84 | if err != nil { 85 | fmt.Println(err) 86 | return err 87 | } 88 | fmt.Printf("file %q successful written\n", out) 89 | return nil 90 | } 91 | 92 | // if not write, print to stdout 93 | fmt.Println(string(formatted)) 94 | return nil 95 | } 96 | 97 | func encodeDir(source, format string) error { 98 | dirData, err := ioutil.ReadDir(source) 99 | if err != nil { 100 | return err 101 | } 102 | for i, v := range dirData { 103 | fmt.Println(i, v.Name()) 104 | tmpFile := path.Join(source, v.Name()) 105 | format, _ := fzp.GetFormat(tmpFile) 106 | if format == fzp.FormatFzp { 107 | err := encodeFile(tmpFile, "format") //, runWrite, runDiff) 108 | if err != nil { 109 | fmt.Println(err) 110 | } 111 | } 112 | } 113 | return nil 114 | } 115 | -------------------------------------------------------------------------------- /bin/fzp/command-format.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "io/ioutil" 7 | "os" 8 | "path" 9 | "strings" 10 | 11 | "github.com/fritzing/fzp/src/go" 12 | "github.com/pmezard/go-difflib/difflib" 13 | "github.com/urfave/cli" 14 | ) 15 | 16 | var commandFormatFlags = []cli.Flag{ 17 | &cli.BoolFlag{ 18 | Name: "write, w", 19 | Usage: "write result to file instead of stdout", 20 | }, 21 | &cli.BoolFlag{ 22 | Name: "diff, d", 23 | Usage: "display diffs instead of stdout or write file", 24 | }, 25 | } 26 | 27 | // the format action is similar to the gofmt command. 28 | // it can be used to format the source of a fzp file. 29 | func commandFormatAction(c *cli.Context) error { 30 | tmpArgs := c.Args() 31 | if len(tmpArgs) == 0 { 32 | fmt.Println("missing source to format. try run") 33 | fmt.Println(" fzp format part.fzp") 34 | os.Exit(127) 35 | } 36 | 37 | source := tmpArgs.Get(0) 38 | 39 | formatDirHandler := func() { 40 | err := formatFile(source, c.Bool("write"), c.Bool("diff")) 41 | if err != nil { 42 | fmt.Println("Error", err) 43 | os.Exit(127) 44 | } 45 | } 46 | 47 | formatFileHandler := func() { 48 | err := formatDir(source, c.Bool("write"), c.Bool("diff")) 49 | if err != nil { 50 | fmt.Println("Error", err) 51 | os.Exit(127) 52 | } 53 | } 54 | 55 | dataHandler(source, formatFileHandler, formatDirHandler) 56 | return nil 57 | } 58 | 59 | func formatFile(filepath string, runWrite, runDiff bool) error { 60 | format, _ := fzp.GetFormat(filepath) 61 | 62 | if format != fzp.FormatFzp { 63 | return errors.New("at the moment only fzp format supported") 64 | } 65 | 66 | fzpFile, fzpBytes, err := fzp.ReadFzp(filepath) 67 | if err != nil { 68 | fmt.Println(err) 69 | return err 70 | } 71 | 72 | formattedXML, err := fzpFile.ToXML() 73 | if err != nil { 74 | fmt.Println(err) 75 | return err 76 | } 77 | 78 | if runWrite { 79 | err := ioutil.WriteFile(filepath, formattedXML, 0755) 80 | if err != nil { 81 | fmt.Println("Error", err) 82 | os.Exit(127) 83 | } 84 | return nil 85 | } 86 | 87 | // diff 88 | if runDiff { 89 | diff := difflib.ContextDiff{ 90 | A: difflib.SplitLines(string(fzpBytes)), 91 | B: difflib.SplitLines(string(formattedXML)), 92 | FromFile: filepath, 93 | ToFile: "Current", 94 | Context: 3, 95 | Eol: "\n", 96 | } 97 | result, _ := difflib.GetContextDiffString(diff) 98 | fmt.Printf(strings.Replace(result, "\t", " ", -1)) 99 | return nil 100 | } 101 | 102 | fmt.Println(string(formattedXML)) 103 | return nil 104 | } 105 | 106 | func formatDir(filepath string, runWrite, runDiff bool) error { 107 | dirData, err := ioutil.ReadDir(filepath) 108 | if err != nil { 109 | return err 110 | } 111 | for _, v := range dirData { 112 | fmt.Println("i", v.Name()) 113 | formatFile(path.Join(filepath, v.Name()), runWrite, runDiff) 114 | } 115 | return nil 116 | } 117 | -------------------------------------------------------------------------------- /bin/fzp/command-create.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | "time" 7 | 8 | "github.com/fritzing/fzp/src/go" 9 | "github.com/urfave/cli" 10 | ) 11 | 12 | var commandCreateFlags = []cli.Flag{ 13 | &cli.StringFlag{ 14 | Name: "title, t", 15 | Usage: "fzp title", 16 | Value: "untitled", 17 | }, 18 | &cli.StringFlag{ 19 | Name: "family, f", 20 | Usage: "set a familyname property for the part", 21 | }, 22 | &cli.StringFlag{ 23 | Name: "version, v", 24 | Usage: "fzp version", 25 | Value: "0.1.0", 26 | }, 27 | &cli.StringFlag{ 28 | Name: "desc, d", 29 | Usage: "fzp description", 30 | }, 31 | &cli.StringFlag{ 32 | Name: "author, a", 33 | Usage: "author of the fritzing part", 34 | Value: "unknown", 35 | }, 36 | &cli.StringFlag{ 37 | Name: "date, D", 38 | Usage: "creation date of the part", 39 | }, 40 | &cli.StringFlag{ 41 | Name: "url, u", 42 | Usage: "parts url", 43 | }, 44 | &cli.StringFlag{ 45 | Name: "label, l", 46 | Usage: "set a label for the part", 47 | }, 48 | &cli.StringFlag{ 49 | Name: "tags, T", 50 | Usage: "a list of part tags", 51 | }, 52 | // TODO: add all flags to setup the fzp data 53 | } 54 | 55 | func commandCreateAction(c *cli.Context) error { 56 | // get the flag data 57 | flagTitle := c.String("title") 58 | flagFamily := c.String("family") 59 | flagVersion := c.String("version") 60 | flagDesc := c.String("desc") 61 | flagAuthor := c.String("author") 62 | flagDate := c.String("date") 63 | if flagDate == "" { 64 | now := time.Now() 65 | flagDate = now.Format(time.UnixDate) 66 | } 67 | flagURL := c.String("url") 68 | flagLabel := c.String("label") 69 | // flagTags := c.String("tags") 70 | // flagProperties := c.String("properties") 71 | // flagViews := c.String("views") 72 | // flagConnectors := c.String("connectors") 73 | // flagBusses := c.String("busses") 74 | 75 | // create a new fzp object 76 | tmpFzp := fzp.NewFzp("filename.fzp", "unknown", flagTitle, flagFamily) 77 | // tmpFzp.FritzingVersion = 78 | // tmpFzp.ModuleId = 79 | // tmpFzp.ReferenceFile = 80 | tmpFzp.Title = flagTitle 81 | tmpFzp.Version = flagVersion 82 | tmpFzp.Description = flagDesc 83 | tmpFzp.Author = flagAuthor 84 | tmpFzp.Date = flagDate 85 | tmpFzp.URL = flagURL 86 | tmpFzp.Label = flagLabel 87 | // tmpFzp.Tags = flagTags 88 | // tmpFzp.Properties = flagProperties 89 | //fmt.Println("fzp", tmpFzp) 90 | // tmpFzp.PrettyPrint() 91 | 92 | // output 93 | tmpArgs := c.Args() 94 | tmpArgsLen := len(tmpArgs) 95 | // fmt.Println("args", tmpArgs) 96 | 97 | format := fzp.FormatFzp 98 | isFile := false 99 | if tmpArgsLen > 0 { 100 | format, isFile = fzp.GetFormat(tmpArgs.Get(0)) 101 | } 102 | 103 | // format data 104 | tmpFzpEncoded, err := tmpFzp.Marshal(format) 105 | if err != nil { 106 | cli.NewExitError("Encode Error: "+err.Error(), 127) 107 | } 108 | 109 | if !isFile { 110 | os.Stdout.Write(tmpFzpEncoded) 111 | return nil 112 | } 113 | 114 | err = ioutil.WriteFile(tmpArgs.Get(0), tmpFzpEncoded, 0755) 115 | if err != nil { 116 | cli.NewExitError("WriteError: "+err.Error(), 127) 117 | } 118 | 119 | return nil 120 | } 121 | -------------------------------------------------------------------------------- /bin/fzp/command-get.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/fritzing/fzp/src/go" 6 | "github.com/urfave/cli" 7 | "os" 8 | "strconv" 9 | ) 10 | 11 | var commandGetFlags = []cli.Flag{ 12 | &cli.BoolFlag{ 13 | Name: "title, t", 14 | Usage: "get the title data", 15 | }, 16 | &cli.BoolFlag{ 17 | Name: "version, v", 18 | Usage: "get the version data", 19 | }, 20 | &cli.BoolFlag{ 21 | Name: "desc, d", 22 | Usage: "get the description data", 23 | }, 24 | &cli.BoolFlag{ 25 | Name: "author, a", 26 | Usage: "get the author data", 27 | }, 28 | &cli.BoolFlag{ 29 | Name: "date, D", 30 | Usage: "get the date data", 31 | }, 32 | &cli.BoolFlag{ 33 | Name: "url, u", 34 | Usage: "get the url data", 35 | }, 36 | &cli.BoolFlag{ 37 | Name: "label, l", 38 | Usage: "get the label data", 39 | }, 40 | &cli.StringFlag{ 41 | Name: "tags, T", 42 | Usage: "get the tags data", 43 | }, 44 | &cli.StringFlag{ 45 | Name: "props, p", 46 | Usage: "get the properties data", 47 | }, 48 | &cli.StringFlag{ 49 | Name: "views, V", 50 | Usage: "get the views data", 51 | }, 52 | &cli.StringFlag{ 53 | Name: "connectors, c", 54 | Usage: "get the connectorsr data", 55 | }, 56 | &cli.StringFlag{ 57 | Name: "buses, b", 58 | Usage: "get the buses data", 59 | }, 60 | } 61 | 62 | func commandGetAction(c *cli.Context) error { 63 | tmpArgs := c.Args() 64 | // fmt.Println("ARGS", tmpArgs) 65 | 66 | if len(tmpArgs) == 0 { 67 | fmt.Println("Missing fritzing part filepath") 68 | os.Exit(1) 69 | } 70 | 71 | getActionFile := func(src string) { 72 | // fmt.Println("get file data...") 73 | part, _, err := fzp.ReadFzp(src) 74 | if err != nil { 75 | fmt.Println(err) 76 | os.Exit(127) 77 | } 78 | // fmt.Printf("%# v\n", part) 79 | 80 | if c.Bool("title") { 81 | fmt.Println(part.Title) 82 | } 83 | 84 | if c.Bool("version") { 85 | fmt.Println(part.Version) 86 | } 87 | 88 | if c.Bool("descritino") { 89 | fmt.Println(part.Description) 90 | } 91 | 92 | if c.Bool("author") { 93 | fmt.Println(part.Author) 94 | } 95 | 96 | if c.Bool("date") { 97 | fmt.Println(part.Date) 98 | } 99 | 100 | if c.Bool("url") { 101 | fmt.Println(part.URL) 102 | } 103 | 104 | if c.Bool("label") { 105 | fmt.Println(part.Label) 106 | } 107 | 108 | flagTags := c.String("tags") 109 | if flagTags != "" { 110 | totalTags := len(part.Tags) 111 | if flagTags == "all" { 112 | for i := 0; i < totalTags; i++ { 113 | fmt.Println(i, part.Tags[i]) 114 | } 115 | } else { 116 | // is the flag an indice 117 | flagTagsInt, err := strconv.Atoi(flagTags) 118 | if err != nil { 119 | fmt.Println("tags flag is not an integer. please use numbers to query a tag item") 120 | os.Exit(1) 121 | } 122 | if flagTagsInt > totalTags { 123 | fmt.Println("tag does not exist! total number of tags:", totalTags) 124 | } 125 | fmt.Println(part.Tags[flagTagsInt]) 126 | } 127 | } 128 | 129 | flagProps := c.String("props") 130 | if flagProps != "" { 131 | if flagProps == "all" { 132 | for i := 0; i < len(part.Properties); i++ { 133 | fmt.Println(i, part.Properties[i].Name, part.Properties[i].Value) 134 | } 135 | } else { 136 | tmp, err := part.Properties.GetValue(flagProps) 137 | if err != nil { 138 | fmt.Println(err) 139 | os.Exit(1) 140 | } 141 | fmt.Println(tmp) 142 | } 143 | } 144 | 145 | flagViews := c.String("views") 146 | if flagViews != "" { 147 | // for i := 0; i < len(part.Views); i++ { 148 | // fmt.Println(i, part.Views[i]) 149 | // } 150 | } 151 | 152 | flagConnectors := c.String("connectors") 153 | if flagConnectors != "" { 154 | fmt.Println(part.Connectors) 155 | } 156 | 157 | flagBuses := c.String("buses") 158 | if flagBuses != "" { 159 | fmt.Println(part.Buses) 160 | } 161 | } 162 | 163 | getActionDir := func(src string) { 164 | fmt.Println("TODO: get folder data...") 165 | } 166 | 167 | dataHandler(tmpArgs.Get(0), func() { 168 | // if the source is a directory... 169 | getActionDir(tmpArgs.Get(0)) 170 | 171 | }, func() { 172 | // if the source is a file... 173 | getActionFile(tmpArgs.Get(0)) 174 | }) 175 | 176 | return nil 177 | } 178 | -------------------------------------------------------------------------------- /src/go/fzp-validate.go: -------------------------------------------------------------------------------- 1 | package fzp 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "strconv" 7 | ) 8 | 9 | // Check the whole Fzp data 10 | func (f *Fzp) Check() []error { 11 | var errList []error 12 | 13 | if err := f.CheckFritzingVersion(); err != nil { 14 | errList = append(errList, err) 15 | } 16 | if err := f.CheckModuleID(); err != nil { 17 | errList = append(errList, err) 18 | } 19 | tmp := f.CheckVersion() 20 | if tmp != "" { 21 | fmt.Println(tmp) 22 | } 23 | if err := f.CheckTitle(); err != nil { 24 | errList = append(errList, err) 25 | } 26 | errTags, _ := f.CheckTags() 27 | if errTags != nil { 28 | errList = append(errList, errTags) 29 | } 30 | if err := f.CheckProperties(); err != nil { 31 | errList = append(errList, err) 32 | } 33 | if err := f.CheckViews(); err != nil { 34 | errList = append(errList, err) 35 | } 36 | if err := f.CheckConnectors(); err != nil { 37 | errList = append(errList, err) 38 | } 39 | if err := f.CheckBuses(); err != nil { 40 | errList = append(errList, err) 41 | } 42 | 43 | return errList 44 | } 45 | 46 | // CheckFritzingVersion validate the FritzingVersion data 47 | // type error 48 | func (f *Fzp) CheckFritzingVersion() error { 49 | if f.FritzingVersion == "" { 50 | return errors.New("fritzingVersion undefined") 51 | } 52 | return nil 53 | } 54 | 55 | // CheckModuleID validate the ModuleID data 56 | // type error 57 | func (f *Fzp) CheckModuleID() error { 58 | if f.ModuleID == "" { 59 | return errors.New("moduleId undefined") 60 | } 61 | return nil 62 | } 63 | 64 | // TODO: is the referenceFile required? 65 | 66 | // CheckVersion validate the Version data 67 | // Version is not a part critical property. 68 | // type warning 69 | func (f *Fzp) CheckVersion() string { 70 | if f.Version == "" { 71 | return "==> WARN version undefined: " + f.FileName 72 | } 73 | return "" 74 | } 75 | 76 | // CheckTitle validate the Title data 77 | // type error 78 | func (f *Fzp) CheckTitle() error { 79 | if f.Title == "" { 80 | return errors.New("title undefined") 81 | } 82 | return nil 83 | } 84 | 85 | // CheckDescription validate the Description data. 86 | // Description is not a part critical property. 87 | // type warning 88 | func (f *Fzp) CheckDescription() string { 89 | if f.Description == "" { 90 | return "==> WARN description undefined" + f.Title 91 | 92 | } 93 | return "" 94 | } 95 | 96 | // CheckAuthor validate the Author data. 97 | // Author is not a part critical property. 98 | // type warning 99 | func (f *Fzp) CheckAuthor() string { 100 | if f.Author == "" { 101 | return "==> WARN author undefined" 102 | 103 | } 104 | return "" 105 | } 106 | 107 | // Check Date ? 108 | // Check URL ? 109 | // Check Label ? 110 | // Check Taxonomy ? 111 | // Check Family ? 112 | // Check Variant ? 113 | 114 | // CheckTags validate the Tags data 115 | // if no tag exist, the part cannot be found by the fritzing app. 116 | func (f *Fzp) CheckTags() (error, int) { 117 | countBrokenTags := 0 118 | 119 | if len(f.Tags) != 0 { 120 | for _, tag := range f.Tags { 121 | if tag == "" { 122 | countBrokenTags++ 123 | } 124 | } 125 | } 126 | 127 | if countBrokenTags == 0 { 128 | return nil, countBrokenTags 129 | } 130 | errMsg := strconv.Itoa(countBrokenTags) + " tag value/s undefined" 131 | return errors.New(errMsg), countBrokenTags 132 | } 133 | 134 | // CheckProperties validate the Properties data 135 | func (f *Fzp) CheckProperties() error { 136 | return f.Properties.Check() 137 | 138 | // if len(f.Properties) == 0 { 139 | // return errors.New("Missing property family!") 140 | // } 141 | // 142 | // for _, property := range f.Properties { 143 | // if err := property.Check(); err != nil { 144 | // return err 145 | // } 146 | // } 147 | // 148 | // return err 149 | } 150 | 151 | // CheckViews validate the Views data 152 | func (f *Fzp) CheckViews() error { 153 | // TODO: ... 154 | return nil 155 | } 156 | 157 | // CheckConnectors validate the Connectors data 158 | func (f *Fzp) CheckConnectors() error { 159 | if len(f.Connectors) != 0 { 160 | for _, connector := range f.Connectors { 161 | if err := connector.Check(); err != nil { 162 | return err 163 | } 164 | } 165 | } 166 | return nil 167 | } 168 | 169 | // CheckBuses validate the Buses data 170 | func (f *Fzp) CheckBuses() error { 171 | if len(f.Buses) != 0 { 172 | for _, bus := range f.Buses { 173 | if err := bus.CheckID(); err != nil { 174 | return err 175 | } 176 | } 177 | } 178 | return nil 179 | } 180 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # fzp-specs 2 | 3 | - [Sample `fzp`](#sample-fzp) 4 | - [Tools](#tools) 5 | - [Validator](#validator) 6 | - [Libraries/Code](#librariescode) 7 | - [Qt](#qt) 8 | - [go](#go) 9 | - [Specification](#specification) 10 | - [`module`](#module) 11 | - [`fritzingVersion`](#fritzingversion) 12 | - [`moduleId`](#moduleid) 13 | - [`referenceFile`](#referencefile) 14 | - [`version`](#version) 15 | - [`title`](#title) 16 | - [`description`](#description) 17 | - [`author`](#author) 18 | - [`date`](#date) 19 | - [`url`](#url) 20 | - [`label`](#label) 21 | - [`tags`](#tags) 22 | - [`tag`](#tag) 23 | - [`taxonomy`](#taxonomy) 24 | - [`language`](#language) 25 | - [`family`](#family) 26 | - [`variant`](#variant) 27 | - [`properties`](#properties) 28 | - [`property`](#property) 29 | - [`views`](#views) 30 | - [`iconView`](#iconview) 31 | - [`breadboardView`](#breadboardview) 32 | - [`schematicView`](#schematicview) 33 | - [`pcbView`](#pcbview) 34 | - [`copper0`](#copper0) 35 | - [`copper1`](#copper1) 36 | - [`connectors`](#connectors) 37 | - [`connector`](#connector) 38 | - [`description`](#description) 39 | - [`views`](#views) 40 | - [`breadboard`](#breadboard) 41 | - [`schematic`](#schematic) 42 | - [`pcb`](#pcb) 43 | - [`buses`](#buses) 44 | 45 | read more about the fzp format at: 46 | [https://github.com/fritzing/fritzing-app/wiki/2.1-Part-file-format](https://github.com/fritzing/fritzing-app/wiki/2.1-Part-file-format) 47 | 48 | ## Sample `fzp` 49 | 50 | TO DO: use template.fzp to load the structure of the fzp 51 | 52 | ``` 53 | 54 | 55 | x.y.z 56 | part-name 57 | some words about the part 58 | your-name 59 | yyyy-mm-dd 60 | http://part.org 61 | 62 | ... 63 | ... 64 | ... 65 | ... 66 | ... 67 | ... 68 | ... 69 | ... 70 | ... 71 | 72 | ``` 73 | A complete sample file can be found [here](sample.fzp) 74 | 75 | 76 | ### template.fzp ckeckLevel 77 | 78 | the template.fzp has a checkLevel identifier for the automated travis checks. 79 | this checkLevel defines how important the xml-tag of the fzp-xml is, for the fritzing-part. 80 | 81 | 82 | 83 | ## Specification 84 | 85 | ### module 86 | ``` 87 | 88 | ``` 89 | 90 | ##### fritzingVersion 91 | ? 92 | 93 | ##### moduleId 94 | ? 95 | 96 | ##### referenceFile 97 | ? 98 | 99 | 100 | ### version 101 | ``` 102 | x.y.z 103 | ``` 104 | Store the part version 105 | 106 | 107 | ### title 108 | ``` 109 | part-name 110 | ``` 111 | Store the part title 112 | 113 | 114 | ### description 115 | ``` 116 | some words about the part 117 | ``` 118 | Store the part description (you can use simple html, as defined by Qt's Rich Text) 119 | 120 | 121 | ### author 122 | ``` 123 | your-name 124 | ``` 125 | Store the part author 126 | 127 | 128 | ### date 129 | ``` 130 | yyyy-mm-dd 131 | ``` 132 | Store the part date 133 | 134 | 135 | ### url 136 | ``` 137 | http://part.org 138 | ``` 139 | Store the part's url if it is described on a web page 140 | 141 | 142 | ### label 143 | ``` 144 | 145 | ``` 146 | Store the default part label prefix 147 | 148 | 149 | ### tags 150 | ``` 151 | 152 | ... 153 | tagvalue 154 | ... 155 | 156 | ``` 157 | Store the part's tags 158 | 159 | ##### tag 160 | 161 | 162 | ### taxonomy 163 | ``` 164 | ... 165 | ``` 166 | 167 | 168 | ### language 169 | ? 170 | 171 | 172 | ### family 173 | ``` 174 | ... 175 | ``` 176 | Store the parts family (what other parts is this related to) 177 | 178 | 179 | ### variant 180 | ``` 181 | ... 182 | ``` 183 | Store the part's variant (this makes it unique from all other parts in the same family) 184 | 185 | 186 | ### properties 187 | ``` 188 | 189 | ... 190 | the-value 191 | ... 192 | 193 | ``` 194 | Store the part's properties 195 | 196 | ##### property 197 | 198 | 199 | ### views 200 | ``` 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | ``` 226 | 227 | ##### iconView 228 | ##### breadboardView 229 | ##### pcbView 230 | ##### schematicView 231 | ###### layers 232 | ###### layer 233 | 234 | 235 | ### connectors 236 | ``` 237 | 238 | 239 | Channel 1 voltage low 240 | 241 | 242 |

243 | 244 | 245 |

246 | 247 | 248 |

249 |

250 | 251 | 252 | 253 | 254 | ``` 255 | ##### connector 256 | ###### description 257 | 258 | 259 | ### buses 260 | ``` 261 | 262 | 263 | ``` 264 | -------------------------------------------------------------------------------- /src/go/fzp.go: -------------------------------------------------------------------------------- 1 | package fzp 2 | 3 | import ( 4 | "encoding/json" 5 | "encoding/xml" 6 | "errors" 7 | "io/ioutil" 8 | "path/filepath" 9 | 10 | "gopkg.in/yaml.v2" 11 | ) 12 | 13 | // Fzp represet a fzp data object 14 | type Fzp struct { 15 | // data not includet at the final fzp file 16 | FileName string `xml:"-" json:"-" yaml:"-"` 17 | 18 | // required xml tags: 19 | XMLName xml.Name `xml:"module" json:"-" yaml:"-"` 20 | ModuleID string `xml:"moduleId,attr" json:"moduleid" yaml:"moduleid"` 21 | Title string `xml:"title" json:"title" yaml:"title"` 22 | 23 | // important part data models 24 | Tags Tags `xml:"tags>tag" json:"tags"` 25 | Properties Properties `xml:"properties>property" json:"version"` 26 | Views Views `xml:"views" json:"views"` 27 | Connectors Connectors `xml:"connectors>connector" json:"connectors"` 28 | Buses Buses `xml:"buses>bus" json:"busses"` 29 | 30 | // nice to have xml tags: 31 | FritzingVersion string `xml:"fritzingVersion,attr" json:"fritzingversion" yaml:"fritzingversion"` 32 | ReferenceFile string `xml:"referenceFile,attr" json:"referencefileurl" yaml:"referencefile"` 33 | Version string `xml:"version" json:"version" yaml:"version"` 34 | Description string `xml:"description" json:"description" yaml:"description"` 35 | Author string `xml:"author" json:"author" yaml:"author"` 36 | Date string `xml:"date" json:"date" yaml:"date"` 37 | URL string `xml:"url" json:"url" yaml:"url"` 38 | Label string `xml:"label" json:"label" yaml:"label"` 39 | } 40 | 41 | func NewFzp(filename, moduleID, title, familyname string /*tags Tags, properties Properties, views Views, connectors Connector*/) Fzp { 42 | f := Fzp{} 43 | f.FileName = filename 44 | f.ModuleID = moduleID 45 | f.Title = title 46 | f.Tags = NewTags() 47 | f.Properties = NewProperties(familyname) 48 | f.Views = NewViews() 49 | f.Connectors = NewConnectors() 50 | return f 51 | } 52 | 53 | // The Format type we unse for enumerate 54 | type Format int 55 | 56 | const ( 57 | FormatNotSupported Format = iota 58 | FormatUnknown 59 | FormatFzp 60 | FormatJSON 61 | FormatYAML 62 | ) 63 | 64 | func (f *Format) String() string { 65 | switch *f { 66 | case FormatNotSupported: 67 | return "Format not supported" 68 | case FormatUnknown: 69 | return "Format unknown" 70 | case FormatFzp: 71 | return "Format Fzp" 72 | case FormatJSON: 73 | return "Format json" 74 | case FormatYAML: 75 | return "Format yaml" 76 | } 77 | return "" 78 | } 79 | 80 | // GetFormat return the format of a filename 81 | func GetFormat(src string) (Format, bool) { 82 | // is it a filename with an extension? 83 | tmp := filepath.Ext(src) 84 | isFile := true 85 | // fmt.Println("tmp", tmp) 86 | if tmp == "" { 87 | tmp = src 88 | isFile = false 89 | } 90 | 91 | switch tmp { 92 | case "": 93 | return FormatUnknown, isFile 94 | 95 | case "FZP", "fzp", ".FZP", ".fzp": 96 | return FormatFzp, isFile 97 | 98 | case "JSON", "json", ".JSON", ".json": 99 | return FormatJSON, isFile 100 | 101 | case "YAML", "yaml", "yml", ".YAML", ".yaml", ".yml": 102 | return FormatYAML, isFile 103 | 104 | default: 105 | return FormatNotSupported, isFile 106 | } 107 | } 108 | 109 | // ReadFzp and decode xml data 110 | func ReadFzp(src string) (Fzp, []byte, error) { 111 | fzpData := Fzp{} 112 | fzpData.FileName = src 113 | // read 114 | fzpBytes, err := ioutil.ReadFile(src) 115 | if err != nil { 116 | return fzpData, []byte{}, err 117 | } 118 | // decode XML 119 | err = xml.Unmarshal(fzpBytes, &fzpData) 120 | if err != nil { 121 | return fzpData, []byte{}, err 122 | } 123 | return fzpData, fzpBytes, nil 124 | } 125 | 126 | // func ReadXML(src string) (Fzp, error) { 127 | // } 128 | 129 | // func ReadJSON(src string) (Fzp, error) { 130 | // } 131 | 132 | // func ReadYAML(src string) (Fzp, error) { 133 | // } 134 | 135 | // Marshal the Fzp data object 136 | func (f *Fzp) Marshal(format Format) ([]byte, error) { 137 | // fmt.Println("marshal", format) 138 | 139 | var data []byte 140 | var err error 141 | switch format { 142 | case FormatFzp: 143 | // fmt.Println("...xml") 144 | data, err = f.ToXML() 145 | break 146 | 147 | case FormatJSON: 148 | // fmt.Println("...json") 149 | data, err = f.ToJSON() 150 | break 151 | 152 | case FormatYAML: 153 | // fmt.Println("...yaml") 154 | data, err = f.ToYAML() 155 | break 156 | 157 | default: 158 | errMsg := "format '" + string(format) + "' not supported!\n" 159 | errMsg += "you can choose between the following three formats:\n" 160 | errMsg += "- fzp (default)\n" 161 | errMsg += "- json\n" 162 | errMsg += "- yml or yaml\n" 163 | return []byte(""), errors.New(errMsg) 164 | } 165 | 166 | if err != nil { 167 | return []byte(""), errors.New("Format Error:" + err.Error()) 168 | } 169 | 170 | return data, err 171 | } 172 | 173 | // ToXML encode the fzp data to yaml and returns a bytes array 174 | func (f *Fzp) ToXML() ([]byte, error) { 175 | data, err := xml.MarshalIndent(f, "", " ") 176 | data = append([]byte("\n"), data...) 177 | data = append(data, []byte("\n")...) 178 | return data, err 179 | } 180 | 181 | // ToJSON encode the fzp data to json and returns a bytes array 182 | func (f *Fzp) ToJSON() ([]byte, error) { 183 | data, err := json.MarshalIndent(f, "", " ") 184 | data = append(data, []byte("\n")...) 185 | return data, err 186 | } 187 | 188 | // ToYAML encode the fzp data to yaml and returns a bytes array 189 | func (f *Fzp) ToYAML() ([]byte, error) { 190 | data, err := yaml.Marshal(f) 191 | return data, err 192 | } 193 | 194 | // WriteXML write the fzp data formatted as xml 195 | // func (f *Fzp) WriteXML(src string) error { 196 | // data, err := f.ToXML() 197 | // if err != nil { 198 | // return err 199 | // } 200 | // err = ioutil.WriteFile(src, data, 0755) 201 | // return err 202 | // } 203 | // 204 | // // WriteJSON write the fzp data formatted as json 205 | // func (f *Fzp) WriteJSON(src string) error { 206 | // data, err := f.ToJSON() 207 | // if err != nil { 208 | // return err 209 | // } 210 | // err = ioutil.WriteFile(src, data, 0755) 211 | // return err 212 | // } 213 | // 214 | // // WriteYAML write the fzp data formatted as yaml 215 | // func (f *Fzp) WriteYAML(src string) error { 216 | // data, err := f.ToYAML() 217 | // if err != nil { 218 | // return err 219 | // } 220 | // err = ioutil.WriteFile(src, data, 0755) 221 | // return err 222 | // } 223 | -------------------------------------------------------------------------------- /bin/fzp/command-validate.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "io/ioutil" 7 | "os" 8 | "path/filepath" 9 | 10 | "github.com/fritzing/fzp/src/go" 11 | "github.com/urfave/cli" 12 | ) 13 | 14 | var commandValidateFlags = []cli.Flag{ 15 | // input flags 16 | &cli.StringFlag{ 17 | Name: "file, f", 18 | Usage: "fzp filepath", 19 | }, 20 | &cli.StringFlag{ 21 | Name: "dir, d", 22 | Usage: "fzp directory", 23 | }, 24 | 25 | // data check settings 26 | &cli.BoolFlag{ 27 | Name: "no-check-fritzingversion, nf", 28 | Usage: "disable fritzingVersion check", 29 | }, 30 | &cli.BoolFlag{ 31 | Name: "no-check-moduleid, nm", 32 | Usage: "disable moduleid check", 33 | }, 34 | &cli.BoolFlag{ 35 | Name: "no-check-referencefile, nr", 36 | Usage: "disable referenceFile check", 37 | }, 38 | &cli.BoolFlag{ 39 | Name: "no-check-version, nv", 40 | Usage: "disable check", 41 | }, 42 | &cli.BoolFlag{ 43 | Name: "no-check-title, nt", 44 | Usage: "disable check", 45 | }, 46 | &cli.BoolFlag{ 47 | Name: "no-check-description, nd", 48 | Usage: "disable <description> check", 49 | }, 50 | &cli.BoolFlag{ 51 | Name: "no-check-author, na", 52 | Usage: "disable <author> check", 53 | }, 54 | // cli.BoolFlag{ 55 | // Name: "no-check-date, nD", 56 | // Usage: "disable <date> check", 57 | // }, 58 | // cli.BoolFlag{ 59 | // Name: "no-check-url, nD", 60 | // Usage: "disable <url> check", 61 | // }, 62 | // cli.BoolFlag{ 63 | // Name: "no-check-label, nD", 64 | // Usage: "disable <label> check", 65 | // }, 66 | // cli.BoolFlag{ 67 | // Name: "no-check-taxonomy, nD", 68 | // Usage: "disable <taxonomy> check", 69 | // }, 70 | &cli.BoolFlag{ 71 | Name: "no-check-family, nD", 72 | Usage: "disable <family> check", 73 | }, 74 | // cli.BoolFlag{ 75 | // Name: "no-check-variant, nD", 76 | // Usage: "disable <variant> check", 77 | // }, 78 | &cli.BoolFlag{ 79 | Name: "no-check-tags, nT", 80 | Usage: "disable <tags> check", 81 | }, 82 | &cli.BoolFlag{ 83 | Name: "no-check-properties, np", 84 | Usage: "disable <properties> check", 85 | }, 86 | &cli.BoolFlag{ 87 | Name: "no-check-views, nV", 88 | Usage: "disable <views> check", 89 | }, 90 | &cli.BoolFlag{ 91 | Name: "no-check-connectors, nc", 92 | Usage: "disable <connectors> check", 93 | }, 94 | &cli.BoolFlag{ 95 | Name: "no-check-buses, nb", 96 | Usage: "disable <buses> check", 97 | }, 98 | // utils 99 | &cli.BoolFlag{ 100 | Name: "verbose, V", 101 | Usage: "verbose mode", 102 | }, 103 | } 104 | 105 | func commandValidateAction(c *cli.Context) error { 106 | fmt.Println("validate fzp file") 107 | // get cli flag value 108 | fzpFile := c.String("file") 109 | fzpDir := c.String("dir") 110 | 111 | // process data 112 | if fzpFile != "" { 113 | if err := validateFile(c, fzpFile); err != nil { 114 | fmt.Println(err) 115 | os.Exit(1) 116 | } 117 | os.Exit(0) 118 | } else if fzpDir != "" { 119 | //Logf("read folder '%v'\n", fzpDir) 120 | var err []error 121 | if err = validateFolder(c, fzpDir); err != nil { 122 | os.Exit(1) 123 | } 124 | os.Exit(0) 125 | } else { 126 | cli.ShowSubcommandHelp(c) 127 | fmt.Println("USAGE-SAMPLES") 128 | fmt.Println("") 129 | fmt.Printf(" $ %v validate --file file/path.fzp\n", c.App.Name) 130 | fmt.Println(" # or") 131 | fmt.Printf(" $ %v validate -f file/path.fzp\n", c.App.Name) 132 | fmt.Println("") 133 | fmt.Printf(" $ %v validate --dir file/dir\n", c.App.Name) 134 | fmt.Println(" # or") 135 | fmt.Printf(" $ %v validate -d file/dir\n", c.App.Name) 136 | fmt.Println("") 137 | fmt.Println("also you can combine the other flags (no-check, verbose)") 138 | } 139 | return nil 140 | } 141 | 142 | func validateFile(c *cli.Context, src string) error { 143 | tmpFilename := filepath.Base(src) 144 | if filepath.Ext(tmpFilename) == ".fzp" { 145 | // fmt.Println("call validateFile", src) 146 | fzpData, _, err := fzp.ReadFzp(src) 147 | if err != nil { 148 | fmt.Printf("validator failed @ %v, %s\n ", err, src) 149 | os.Exit(1) 150 | } 151 | // Logf("fzp file '%v' successful read\n", src, fzpData) 152 | 153 | errCounter /*, warnings*/ := fzpData.Check() //checkData(c, fzpData) 154 | if len(errCounter) != 0 { 155 | for _, error := range errCounter { 156 | fmt.Println(error) 157 | } 158 | return errors.New(" Errors @ " + src) 159 | } 160 | // if len(warnings) != 0 { 161 | // fmt.Println(" Warnings @ " + warnings) 162 | // } 163 | 164 | } 165 | 166 | // Logf("fzp valid\n") 167 | return nil 168 | } 169 | 170 | func validateFolder(c *cli.Context, src string) []error { 171 | var errList []error 172 | folderFiles, err := ioutil.ReadDir(src) 173 | if err != nil { 174 | errList = append(errList, errors.New("validator failed @ read folder '"+src+"'")) 175 | return errList 176 | } 177 | 178 | for _, v := range folderFiles { 179 | filename := v.Name() 180 | // fmt.Printf("file %v: %v\n", k, filename) 181 | // check if file is a fzp file 182 | // if fzp.HasExtFzp(filename) { 183 | if err := validateFile(c, src+"/"+filename); err != nil { 184 | errList = append(errList, err) 185 | fmt.Println(err) 186 | // } 187 | } 188 | } 189 | return errList 190 | } 191 | 192 | func checkData(c *cli.Context, fzpData fzp.Fzp) (int, string) { 193 | checkErrorCounter := 0 194 | collectedWarnings := "" 195 | 196 | /*if !c.Bool("no-check-fritzingversion") { 197 | if err := fzpData.CheckFritzingVersion(); err != nil { 198 | fmt.Println("=>", err) 199 | checkErrorCounter++ 200 | } 201 | }*/ 202 | 203 | if !c.Bool("no-check-moduleid") { 204 | if err := fzpData.CheckModuleID(); err != nil { 205 | fmt.Println("=>", err) 206 | checkErrorCounter++ 207 | } 208 | } 209 | 210 | // ReferenceFile 211 | 212 | /*if !c.Bool("no-check-version") { 213 | if err := fzpData.CheckVersion(); err != nil { 214 | fmt.Println("=>", err) 215 | checkErrorCounter++ 216 | } 217 | }*/ 218 | 219 | if !c.Bool("no-check-title") { 220 | if err := fzpData.CheckTitle(); err != nil { 221 | fmt.Println("=>", err) 222 | checkErrorCounter++ 223 | } 224 | } 225 | 226 | if !c.Bool("no-check-description") { 227 | if fzpData.CheckDescription() != "" { 228 | fmt.Println("=>") 229 | checkErrorCounter++ 230 | } 231 | } 232 | 233 | // if !c.Bool("no-check-author") { 234 | // if err := fzpData.CheckAuthor(); err != nil { 235 | // fmt.Println("=>", err) 236 | // checkErrorCounter++ 237 | // } 238 | // } 239 | 240 | // Check Date ? 241 | // Check URL ? 242 | // Check Label ? 243 | // Check Taxonomy ? 244 | // Check Family ? 245 | // Check Variant ? 246 | 247 | /*if !c.Bool("no-check-tags") { 248 | if err := fzpData.CheckTags(); err != nil { 249 | fmt.Println("=>", err) 250 | checkErrorCounter++ 251 | } 252 | }*/ 253 | 254 | if !c.Bool("no-check-properties") { 255 | if err := fzpData.CheckProperties(); err != nil { 256 | fmt.Println("=>", err) 257 | checkErrorCounter++ 258 | } 259 | } 260 | 261 | // if !c.Bool("no-check-buses") { 262 | // if err := fzpData.CheckBuses(); err != nil { 263 | // fmt.Println("=>", err) 264 | // checkErrorCounter++ 265 | // } 266 | // } 267 | 268 | return checkErrorCounter, collectedWarnings 269 | } 270 | --------------------------------------------------------------------------------