1package gofeed
2
3import (
4	"fmt"
5	"strings"
6	"time"
7
8	"github.com/mmcdole/gofeed/atom"
9	ext "github.com/mmcdole/gofeed/extensions"
10	"github.com/mmcdole/gofeed/internal/shared"
11	"github.com/mmcdole/gofeed/rss"
12)
13
14// Translator converts a particular feed (atom.Feed or rss.Feed)
15// into the generic Feed struct
16type Translator interface {
17	Translate(feed interface{}) (*Feed, error)
18}
19
20// DefaultRSSTranslator converts an rss.Feed struct
21// into the generic Feed struct.
22//
23// This default implementation defines a set of
24// mapping rules between rss.Feed -> Feed
25// for each of the fields in Feed.
26type DefaultRSSTranslator struct{}
27
28// Translate converts an RSS feed into the universal
29// feed type.
30func (t *DefaultRSSTranslator) Translate(feed interface{}) (*Feed, error) {
31	rss, found := feed.(*rss.Feed)
32	if !found {
33		return nil, fmt.Errorf("Feed did not match expected type of *rss.Feed")
34	}
35
36	result := &Feed{}
37	result.Title = t.translateFeedTitle(rss)
38	result.Description = t.translateFeedDescription(rss)
39	result.Link = t.translateFeedLink(rss)
40	result.FeedLink = t.translateFeedFeedLink(rss)
41	result.Updated = t.translateFeedUpdated(rss)
42	result.UpdatedParsed = t.translateFeedUpdatedParsed(rss)
43	result.Published = t.translateFeedPublished(rss)
44	result.PublishedParsed = t.translateFeedPublishedParsed(rss)
45	result.Author = t.translateFeedAuthor(rss)
46	result.Language = t.translateFeedLanguage(rss)
47	result.Image = t.translateFeedImage(rss)
48	result.Copyright = t.translateFeedCopyright(rss)
49	result.Generator = t.translateFeedGenerator(rss)
50	result.Categories = t.translateFeedCategories(rss)
51	result.Items = t.translateFeedItems(rss)
52	result.ITunesExt = rss.ITunesExt
53	result.DublinCoreExt = rss.DublinCoreExt
54	result.Extensions = rss.Extensions
55	result.FeedVersion = rss.Version
56	result.FeedType = "rss"
57	return result, nil
58}
59
60func (t *DefaultRSSTranslator) translateFeedItem(rssItem *rss.Item) (item *Item) {
61	item = &Item{}
62	item.Title = t.translateItemTitle(rssItem)
63	item.Description = t.translateItemDescription(rssItem)
64	item.Content = t.translateItemContent(rssItem)
65	item.Link = t.translateItemLink(rssItem)
66	item.Published = t.translateItemPublished(rssItem)
67	item.PublishedParsed = t.translateItemPublishedParsed(rssItem)
68	item.Author = t.translateItemAuthor(rssItem)
69	item.GUID = t.translateItemGUID(rssItem)
70	item.Image = t.translateItemImage(rssItem)
71	item.Categories = t.translateItemCategories(rssItem)
72	item.Enclosures = t.translateItemEnclosures(rssItem)
73	item.DublinCoreExt = rssItem.DublinCoreExt
74	item.ITunesExt = rssItem.ITunesExt
75	item.Extensions = rssItem.Extensions
76	return
77}
78
79func (t *DefaultRSSTranslator) translateFeedTitle(rss *rss.Feed) (title string) {
80	if rss.Title != "" {
81		title = rss.Title
82	} else if rss.DublinCoreExt != nil && rss.DublinCoreExt.Title != nil {
83		title = t.firstEntry(rss.DublinCoreExt.Title)
84	}
85	return
86}
87
88func (t *DefaultRSSTranslator) translateFeedDescription(rss *rss.Feed) (desc string) {
89	return rss.Description
90}
91
92func (t *DefaultRSSTranslator) translateFeedLink(rss *rss.Feed) (link string) {
93	if rss.Link != "" {
94		link = rss.Link
95	} else if rss.ITunesExt != nil && rss.ITunesExt.Subtitle != "" {
96		link = rss.ITunesExt.Subtitle
97	}
98	return
99}
100
101func (t *DefaultRSSTranslator) translateFeedFeedLink(rss *rss.Feed) (link string) {
102	atomExtensions := t.extensionsForKeys([]string{"atom", "atom10", "atom03"}, rss.Extensions)
103	for _, ex := range atomExtensions {
104		if links, ok := ex["link"]; ok {
105			for _, l := range links {
106				if l.Attrs["Rel"] == "self" {
107					link = l.Value
108				}
109			}
110		}
111	}
112	return
113}
114
115func (t *DefaultRSSTranslator) translateFeedUpdated(rss *rss.Feed) (updated string) {
116	if rss.LastBuildDate != "" {
117		updated = rss.LastBuildDate
118	} else if rss.DublinCoreExt != nil && rss.DublinCoreExt.Date != nil {
119		updated = t.firstEntry(rss.DublinCoreExt.Date)
120	}
121	return
122}
123
124func (t *DefaultRSSTranslator) translateFeedUpdatedParsed(rss *rss.Feed) (updated *time.Time) {
125	if rss.LastBuildDateParsed != nil {
126		updated = rss.LastBuildDateParsed
127	} else if rss.DublinCoreExt != nil && rss.DublinCoreExt.Date != nil {
128		dateText := t.firstEntry(rss.DublinCoreExt.Date)
129		date, err := shared.ParseDate(dateText)
130		if err == nil {
131			updated = &date
132		}
133	}
134	return
135}
136
137func (t *DefaultRSSTranslator) translateFeedPublished(rss *rss.Feed) (published string) {
138	return rss.PubDate
139}
140
141func (t *DefaultRSSTranslator) translateFeedPublishedParsed(rss *rss.Feed) (published *time.Time) {
142	return rss.PubDateParsed
143}
144
145func (t *DefaultRSSTranslator) translateFeedAuthor(rss *rss.Feed) (author *Person) {
146	if rss.ManagingEditor != "" {
147		name, address := shared.ParseNameAddress(rss.ManagingEditor)
148		author = &Person{}
149		author.Name = name
150		author.Email = address
151	} else if rss.WebMaster != "" {
152		name, address := shared.ParseNameAddress(rss.WebMaster)
153		author = &Person{}
154		author.Name = name
155		author.Email = address
156	} else if rss.DublinCoreExt != nil && rss.DublinCoreExt.Author != nil {
157		dcAuthor := t.firstEntry(rss.DublinCoreExt.Author)
158		name, address := shared.ParseNameAddress(dcAuthor)
159		author = &Person{}
160		author.Name = name
161		author.Email = address
162	} else if rss.DublinCoreExt != nil && rss.DublinCoreExt.Creator != nil {
163		dcCreator := t.firstEntry(rss.DublinCoreExt.Creator)
164		name, address := shared.ParseNameAddress(dcCreator)
165		author = &Person{}
166		author.Name = name
167		author.Email = address
168	} else if rss.ITunesExt != nil && rss.ITunesExt.Author != "" {
169		name, address := shared.ParseNameAddress(rss.ITunesExt.Author)
170		author = &Person{}
171		author.Name = name
172		author.Email = address
173	}
174	return
175}
176
177func (t *DefaultRSSTranslator) translateFeedLanguage(rss *rss.Feed) (language string) {
178	if rss.Language != "" {
179		language = rss.Language
180	} else if rss.DublinCoreExt != nil && rss.DublinCoreExt.Language != nil {
181		language = t.firstEntry(rss.DublinCoreExt.Language)
182	}
183	return
184}
185
186func (t *DefaultRSSTranslator) translateFeedImage(rss *rss.Feed) (image *Image) {
187	if rss.Image != nil {
188		image = &Image{}
189		image.Title = rss.Image.Title
190		image.URL = rss.Image.URL
191	} else if rss.ITunesExt != nil && rss.ITunesExt.Image != "" {
192		image = &Image{}
193		image.URL = rss.ITunesExt.Image
194	}
195	return
196}
197
198func (t *DefaultRSSTranslator) translateFeedCopyright(rss *rss.Feed) (rights string) {
199	if rss.Copyright != "" {
200		rights = rss.Copyright
201	} else if rss.DublinCoreExt != nil && rss.DublinCoreExt.Rights != nil {
202		rights = t.firstEntry(rss.DublinCoreExt.Rights)
203	}
204	return
205}
206
207func (t *DefaultRSSTranslator) translateFeedGenerator(rss *rss.Feed) (generator string) {
208	return rss.Generator
209}
210
211func (t *DefaultRSSTranslator) translateFeedCategories(rss *rss.Feed) (categories []string) {
212	cats := []string{}
213	if rss.Categories != nil {
214		for _, c := range rss.Categories {
215			cats = append(cats, c.Value)
216		}
217	}
218
219	if rss.ITunesExt != nil && rss.ITunesExt.Keywords != "" {
220		keywords := strings.Split(rss.ITunesExt.Keywords, ",")
221		for _, k := range keywords {
222			cats = append(cats, k)
223		}
224	}
225
226	if rss.ITunesExt != nil && rss.ITunesExt.Categories != nil {
227		for _, c := range rss.ITunesExt.Categories {
228			cats = append(cats, c.Text)
229			if c.Subcategory != nil {
230				cats = append(cats, c.Subcategory.Text)
231			}
232		}
233	}
234
235	if rss.DublinCoreExt != nil && rss.DublinCoreExt.Subject != nil {
236		for _, c := range rss.DublinCoreExt.Subject {
237			cats = append(cats, c)
238		}
239	}
240
241	if len(cats) > 0 {
242		categories = cats
243	}
244
245	return
246}
247
248func (t *DefaultRSSTranslator) translateFeedItems(rss *rss.Feed) (items []*Item) {
249	items = []*Item{}
250	for _, i := range rss.Items {
251		items = append(items, t.translateFeedItem(i))
252	}
253	return
254}
255
256func (t *DefaultRSSTranslator) translateItemTitle(rssItem *rss.Item) (title string) {
257	if rssItem.Title != "" {
258		title = rssItem.Title
259	} else if rssItem.DublinCoreExt != nil && rssItem.DublinCoreExt.Title != nil {
260		title = t.firstEntry(rssItem.DublinCoreExt.Title)
261	}
262	return
263}
264
265func (t *DefaultRSSTranslator) translateItemDescription(rssItem *rss.Item) (desc string) {
266	if rssItem.Description != "" {
267		desc = rssItem.Description
268	} else if rssItem.DublinCoreExt != nil && rssItem.DublinCoreExt.Description != nil {
269		desc = t.firstEntry(rssItem.DublinCoreExt.Description)
270	}
271	return
272}
273
274func (t *DefaultRSSTranslator) translateItemContent(rssItem *rss.Item) (content string) {
275	return rssItem.Content
276}
277
278func (t *DefaultRSSTranslator) translateItemLink(rssItem *rss.Item) (link string) {
279	return rssItem.Link
280}
281
282func (t *DefaultRSSTranslator) translateItemUpdated(rssItem *rss.Item) (updated string) {
283	if rssItem.DublinCoreExt != nil && rssItem.DublinCoreExt.Date != nil {
284		updated = t.firstEntry(rssItem.DublinCoreExt.Date)
285	}
286	return updated
287}
288
289func (t *DefaultRSSTranslator) translateItemUpdatedParsed(rssItem *rss.Item) (updated *time.Time) {
290	if rssItem.DublinCoreExt != nil && rssItem.DublinCoreExt.Date != nil {
291		updatedText := t.firstEntry(rssItem.DublinCoreExt.Date)
292		updatedDate, err := shared.ParseDate(updatedText)
293		if err == nil {
294			updated = &updatedDate
295		}
296	}
297	return
298}
299
300func (t *DefaultRSSTranslator) translateItemPublished(rssItem *rss.Item) (pubDate string) {
301	if rssItem.PubDate != "" {
302		return rssItem.PubDate
303	} else if rssItem.DublinCoreExt != nil && rssItem.DublinCoreExt.Date != nil {
304		return t.firstEntry(rssItem.DublinCoreExt.Date)
305	}
306	return
307}
308
309func (t *DefaultRSSTranslator) translateItemPublishedParsed(rssItem *rss.Item) (pubDate *time.Time) {
310	if rssItem.PubDateParsed != nil {
311		return rssItem.PubDateParsed
312	} else if rssItem.DublinCoreExt != nil && rssItem.DublinCoreExt.Date != nil {
313		pubDateText := t.firstEntry(rssItem.DublinCoreExt.Date)
314		pubDateParsed, err := shared.ParseDate(pubDateText)
315		if err == nil {
316			pubDate = &pubDateParsed
317		}
318	}
319	return
320}
321
322func (t *DefaultRSSTranslator) translateItemAuthor(rssItem *rss.Item) (author *Person) {
323	if rssItem.Author != "" {
324		name, address := shared.ParseNameAddress(rssItem.Author)
325		author = &Person{}
326		author.Name = name
327		author.Email = address
328	} else if rssItem.DublinCoreExt != nil && rssItem.DublinCoreExt.Author != nil {
329		dcAuthor := t.firstEntry(rssItem.DublinCoreExt.Author)
330		name, address := shared.ParseNameAddress(dcAuthor)
331		author = &Person{}
332		author.Name = name
333		author.Email = address
334	} else if rssItem.DublinCoreExt != nil && rssItem.DublinCoreExt.Creator != nil {
335		dcCreator := t.firstEntry(rssItem.DublinCoreExt.Creator)
336		name, address := shared.ParseNameAddress(dcCreator)
337		author = &Person{}
338		author.Name = name
339		author.Email = address
340	} else if rssItem.ITunesExt != nil && rssItem.ITunesExt.Author != "" {
341		name, address := shared.ParseNameAddress(rssItem.ITunesExt.Author)
342		author = &Person{}
343		author.Name = name
344		author.Email = address
345	}
346	return
347}
348
349func (t *DefaultRSSTranslator) translateItemGUID(rssItem *rss.Item) (guid string) {
350	if rssItem.GUID != nil {
351		guid = rssItem.GUID.Value
352	}
353	return
354}
355
356func (t *DefaultRSSTranslator) translateItemImage(rssItem *rss.Item) (image *Image) {
357	if rssItem.ITunesExt != nil && rssItem.ITunesExt.Image != "" {
358		image = &Image{}
359		image.URL = rssItem.ITunesExt.Image
360	}
361	return
362}
363
364func (t *DefaultRSSTranslator) translateItemCategories(rssItem *rss.Item) (categories []string) {
365	cats := []string{}
366	if rssItem.Categories != nil {
367		for _, c := range rssItem.Categories {
368			cats = append(cats, c.Value)
369		}
370	}
371
372	if rssItem.ITunesExt != nil && rssItem.ITunesExt.Keywords != "" {
373		keywords := strings.Split(rssItem.ITunesExt.Keywords, ",")
374		for _, k := range keywords {
375			cats = append(cats, k)
376		}
377	}
378
379	if rssItem.DublinCoreExt != nil && rssItem.DublinCoreExt.Subject != nil {
380		for _, c := range rssItem.DublinCoreExt.Subject {
381			cats = append(cats, c)
382		}
383	}
384
385	if len(cats) > 0 {
386		categories = cats
387	}
388
389	return
390}
391
392func (t *DefaultRSSTranslator) translateItemEnclosures(rssItem *rss.Item) (enclosures []*Enclosure) {
393	if rssItem.Enclosure != nil {
394		e := &Enclosure{}
395		e.URL = rssItem.Enclosure.URL
396		e.Type = rssItem.Enclosure.Type
397		e.Length = rssItem.Enclosure.Length
398		enclosures = []*Enclosure{e}
399	}
400	return
401}
402
403func (t *DefaultRSSTranslator) extensionsForKeys(keys []string, extensions ext.Extensions) (matches []map[string][]ext.Extension) {
404	matches = []map[string][]ext.Extension{}
405
406	if extensions == nil {
407		return
408	}
409
410	for _, key := range keys {
411		if match, ok := extensions[key]; ok {
412			matches = append(matches, match)
413		}
414	}
415	return
416}
417
418func (t *DefaultRSSTranslator) firstEntry(entries []string) (value string) {
419	if entries == nil {
420		return
421	}
422
423	if len(entries) == 0 {
424		return
425	}
426
427	return entries[0]
428}
429
430// DefaultAtomTranslator converts an atom.Feed struct
431// into the generic Feed struct.
432//
433// This default implementation defines a set of
434// mapping rules between atom.Feed -> Feed
435// for each of the fields in Feed.
436type DefaultAtomTranslator struct{}
437
438// Translate converts an Atom feed into the universal
439// feed type.
440func (t *DefaultAtomTranslator) Translate(feed interface{}) (*Feed, error) {
441	atom, found := feed.(*atom.Feed)
442	if !found {
443		return nil, fmt.Errorf("Feed did not match expected type of *atom.Feed")
444	}
445
446	result := &Feed{}
447	result.Title = t.translateFeedTitle(atom)
448	result.Description = t.translateFeedDescription(atom)
449	result.Link = t.translateFeedLink(atom)
450	result.FeedLink = t.translateFeedFeedLink(atom)
451	result.Updated = t.translateFeedUpdated(atom)
452	result.UpdatedParsed = t.translateFeedUpdatedParsed(atom)
453	result.Author = t.translateFeedAuthor(atom)
454	result.Language = t.translateFeedLanguage(atom)
455	result.Image = t.translateFeedImage(atom)
456	result.Copyright = t.translateFeedCopyright(atom)
457	result.Categories = t.translateFeedCategories(atom)
458	result.Generator = t.translateFeedGenerator(atom)
459	result.Items = t.translateFeedItems(atom)
460	result.Extensions = atom.Extensions
461	result.FeedVersion = atom.Version
462	result.FeedType = "atom"
463	return result, nil
464}
465
466func (t *DefaultAtomTranslator) translateFeedItem(entry *atom.Entry) (item *Item) {
467	item = &Item{}
468	item.Title = t.translateItemTitle(entry)
469	item.Description = t.translateItemDescription(entry)
470	item.Content = t.translateItemContent(entry)
471	item.Link = t.translateItemLink(entry)
472	item.Updated = t.translateItemUpdated(entry)
473	item.UpdatedParsed = t.translateItemUpdatedParsed(entry)
474	item.Published = t.translateItemPublished(entry)
475	item.PublishedParsed = t.translateItemPublishedParsed(entry)
476	item.Author = t.translateItemAuthor(entry)
477	item.GUID = t.translateItemGUID(entry)
478	item.Image = t.translateItemImage(entry)
479	item.Categories = t.translateItemCategories(entry)
480	item.Enclosures = t.translateItemEnclosures(entry)
481	item.Extensions = entry.Extensions
482	return
483}
484
485func (t *DefaultAtomTranslator) translateFeedTitle(atom *atom.Feed) (title string) {
486	return atom.Title
487}
488
489func (t *DefaultAtomTranslator) translateFeedDescription(atom *atom.Feed) (desc string) {
490	return atom.Subtitle
491}
492
493func (t *DefaultAtomTranslator) translateFeedLink(atom *atom.Feed) (link string) {
494	l := t.firstLinkWithType("alternate", atom.Links)
495	if l != nil {
496		link = l.Href
497	}
498	return
499}
500
501func (t *DefaultAtomTranslator) translateFeedFeedLink(atom *atom.Feed) (link string) {
502	feedLink := t.firstLinkWithType("self", atom.Links)
503	if feedLink != nil {
504		link = feedLink.Href
505	}
506	return
507}
508
509func (t *DefaultAtomTranslator) translateFeedUpdated(atom *atom.Feed) (updated string) {
510	return atom.Updated
511}
512
513func (t *DefaultAtomTranslator) translateFeedUpdatedParsed(atom *atom.Feed) (updated *time.Time) {
514	return atom.UpdatedParsed
515}
516
517func (t *DefaultAtomTranslator) translateFeedAuthor(atom *atom.Feed) (author *Person) {
518	a := t.firstPerson(atom.Authors)
519	if a != nil {
520		feedAuthor := Person{}
521		feedAuthor.Name = a.Name
522		feedAuthor.Email = a.Email
523		author = &feedAuthor
524	}
525	return
526}
527
528func (t *DefaultAtomTranslator) translateFeedLanguage(atom *atom.Feed) (language string) {
529	return atom.Language
530}
531
532func (t *DefaultAtomTranslator) translateFeedImage(atom *atom.Feed) (image *Image) {
533	if atom.Logo != "" {
534		feedImage := Image{}
535		feedImage.URL = atom.Logo
536		image = &feedImage
537	}
538	return
539}
540
541func (t *DefaultAtomTranslator) translateFeedCopyright(atom *atom.Feed) (rights string) {
542	return atom.Rights
543}
544
545func (t *DefaultAtomTranslator) translateFeedGenerator(atom *atom.Feed) (generator string) {
546	if atom.Generator != nil {
547		if atom.Generator.Value != "" {
548			generator += atom.Generator.Value
549		}
550		if atom.Generator.Version != "" {
551			generator += " v" + atom.Generator.Version
552		}
553		if atom.Generator.URI != "" {
554			generator += " " + atom.Generator.URI
555		}
556		generator = strings.TrimSpace(generator)
557	}
558	return
559}
560
561func (t *DefaultAtomTranslator) translateFeedCategories(atom *atom.Feed) (categories []string) {
562	if atom.Categories != nil {
563		categories = []string{}
564		for _, c := range atom.Categories {
565			categories = append(categories, c.Term)
566		}
567	}
568	return
569}
570
571func (t *DefaultAtomTranslator) translateFeedItems(atom *atom.Feed) (items []*Item) {
572	items = []*Item{}
573	for _, entry := range atom.Entries {
574		items = append(items, t.translateFeedItem(entry))
575	}
576	return
577}
578
579func (t *DefaultAtomTranslator) translateItemTitle(entry *atom.Entry) (title string) {
580	return entry.Title
581}
582
583func (t *DefaultAtomTranslator) translateItemDescription(entry *atom.Entry) (desc string) {
584	return entry.Summary
585}
586
587func (t *DefaultAtomTranslator) translateItemContent(entry *atom.Entry) (content string) {
588	if entry.Content != nil {
589		content = entry.Content.Value
590	}
591	return
592}
593
594func (t *DefaultAtomTranslator) translateItemLink(entry *atom.Entry) (link string) {
595	l := t.firstLinkWithType("alternate", entry.Links)
596	if l != nil {
597		link = l.Href
598	}
599	return
600}
601
602func (t *DefaultAtomTranslator) translateItemUpdated(entry *atom.Entry) (updated string) {
603	return entry.Updated
604}
605
606func (t *DefaultAtomTranslator) translateItemUpdatedParsed(entry *atom.Entry) (updated *time.Time) {
607	return entry.UpdatedParsed
608}
609
610func (t *DefaultAtomTranslator) translateItemPublished(entry *atom.Entry) (updated string) {
611	return entry.Published
612}
613
614func (t *DefaultAtomTranslator) translateItemPublishedParsed(entry *atom.Entry) (updated *time.Time) {
615	return entry.PublishedParsed
616}
617
618func (t *DefaultAtomTranslator) translateItemAuthor(entry *atom.Entry) (author *Person) {
619	a := t.firstPerson(entry.Authors)
620	if a != nil {
621		author = &Person{}
622		author.Name = a.Name
623		author.Email = a.Email
624	}
625	return
626}
627
628func (t *DefaultAtomTranslator) translateItemGUID(entry *atom.Entry) (guid string) {
629	return entry.ID
630}
631
632func (t *DefaultAtomTranslator) translateItemImage(entry *atom.Entry) (image *Image) {
633	return nil
634}
635
636func (t *DefaultAtomTranslator) translateItemCategories(entry *atom.Entry) (categories []string) {
637	if entry.Categories != nil {
638		categories = []string{}
639		for _, c := range entry.Categories {
640			categories = append(categories, c.Term)
641		}
642	}
643	return
644}
645
646func (t *DefaultAtomTranslator) translateItemEnclosures(entry *atom.Entry) (enclosures []*Enclosure) {
647	if entry.Links != nil {
648		enclosures = []*Enclosure{}
649		for _, e := range entry.Links {
650			if e.Rel == "enclosure" {
651				enclosure := &Enclosure{}
652				enclosure.URL = e.Href
653				enclosure.Length = e.Length
654				enclosure.Type = e.Type
655				enclosures = append(enclosures, enclosure)
656			}
657		}
658
659		if len(enclosures) == 0 {
660			enclosures = nil
661		}
662	}
663	return
664}
665
666func (t *DefaultAtomTranslator) firstLinkWithType(linkType string, links []*atom.Link) *atom.Link {
667	if links == nil {
668		return nil
669	}
670
671	for _, link := range links {
672		if link.Rel == linkType {
673			return link
674		}
675	}
676	return nil
677}
678
679func (t *DefaultAtomTranslator) firstPerson(persons []*atom.Person) (person *atom.Person) {
680	if persons == nil || len(persons) == 0 {
681		return
682	}
683
684	person = persons[0]
685	return
686}
687