1// Copyright (C) 2013-2015 Sören Tempel2//3// This program is free software: you can redistribute it and/or modify4// it under the terms of the GNU General Public License as published by5// the Free Software Foundation, either version 3 of the License, or6// (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 of10// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the11// GNU General Public License for more details.12//13// You should have received a copy of the GNU General Public License14// along with this program. If not, see <http://www.gnu.org/licenses/>.1516// Package opml implements a parser for OPML files.17// See also: http://dev.opml.org/spec2.html18package opml1920import (21 "encoding/xml"22 "golang.org/x/net/html/charset"23 "os"24 "time"25)2627// OPML version supported by this library.28const version = "2.0"2930// OPML represent an OPML document.31type OPML struct {32 // XML name.33 XMLName xml.Name `xml:"opml"`3435 // OPML standard version implemented by this file.36 Version string `xml:"version,attr"`3738 // Title of the OPML document.39 Title string `xml:"head>title"`4041 // Time the document was created.42 Created string `xml:"head>dateCreated"`4344 // Array of outlines, each represents a subscription.45 Outlines []Outline `xml:"body>outline"`46}4748// Outline represents an arbitrary OPML outline.49type Outline struct {50 // Text attribute, might contain HTML markup.51 Text string `xml:"text,attr"`5253 // Type of file found at the outline URL.54 Type string `xml:"type,attr"`5556 // Arbitrary outline URL.57 URL string `xml:"xmlUrl,attr"`58}5960// 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}7071// 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 return76 }77 defer file.Close()7879 decoder := xml.NewDecoder(file)80 decoder.CharsetReader = charset.NewReaderLabel8182 if err = decoder.Decode(&o); err != nil {83 return84 }8586 return87}8889// Add appends a new outline to the OPML document, even if the outline90// 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 }9798 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}