1// Copyright (C) 2013-2015 Sören Tempel 2// 3// This program is free software: you can redistribute it and/or modify 4// it under the terms of the GNU General Public License as published by 5// the Free Software Foundation, either version 3 of the License, or 6// (at your option) any later version. 7// 8// This program is distributed in the hope that it will be useful, 9// but WITHOUT ANY WARRANTY; without even the implied warranty of 10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11// GNU General Public License for more details. 12// 13// You should have received a copy of the GNU General Public License 14// along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16// Package opml implements a parser for OPML files. 17// See also: http://dev.opml.org/spec2.html 18package opml 19 20import ( 21 "encoding/xml" 22 "golang.org/x/net/html/charset" 23 "os" 24 "time" 25) 26 27// OPML version supported by this library. 28const version = "2.0" 29 30// OPML represent an OPML document. 31type OPML struct { 32 // XML name. 33 XMLName xml.Name `xml:"opml"` 34 35 // OPML standard version implemented by this file. 36 Version string `xml:"version,attr"` 37 38 // Title of the OPML document. 39 Title string `xml:"head>title"` 40 41 // Time the document was created. 42 Created string `xml:"head>dateCreated"` 43 44 // Array of outlines, each represents a subscription. 45 Outlines []Outline `xml:"body>outline"` 46} 47 48// Outline represents an arbitrary OPML outline. 49type Outline struct { 50 // Text attribute, might contain HTML markup. 51 Text string `xml:"text,attr"` 52 53 // Type of file found at the outline URL. 54 Type string `xml:"type,attr"` 55 56 // Arbitrary outline URL. 57 URL string `xml:"xmlUrl,attr"` 58} 59 60// Create returns a new OPML document with the given title. However, 61// this is just syntax sugar. A file is only written after a call Save, 62// it's the callers responsibility to do so if desired. 63func Create(title string) *OPML { 64 return &OPML{ 65 Version: version, 66 Title: title, 67 Created: time.Now().Format(time.RFC1123Z), 68 } 69} 70 71// Load reads an existing OPML document located at the given path. 72func Load(path string) (o *OPML, err error) { 73 file, err := os.Open(path) 74 if err != nil { 75 return 76 } 77 defer file.Close() 78 79 decoder := xml.NewDecoder(file) 80 decoder.CharsetReader = charset.NewReaderLabel 81 82 if err = decoder.Decode(&o); err != nil { 83 return 84 } 85 86 return 87} 88 89// Add appends a new outline to the OPML document, even if the outline 90// is already a part of the document. 91func (o *OPML) Add(text, ftype, url string) { 92 outline := Outline{ 93 Text: text, 94 Type: ftype, 95 URL: url, 96 } 97 98 o.Outlines = append(o.Outlines, outline) 99}100101// Save writes an indented version of the OPML document to the given102// file path.103func (o *OPML) Save(path string) error {104 file, err := os.Create(path)105 if err != nil {106 return err107 }108109 defer file.Close()110 data, err := xml.MarshalIndent(o, "", "\t")111 if err != nil {112 return err113 }114115 if _, err = file.WriteString(xml.Header); err != nil {116 return err117 }118119 if _, err = file.Write(data); err != nil {120 return err121 }122123 return err124}