Port RSOPMLAttributes and RSOPMLItem to Swift.

This commit is contained in:
Brent Simmons 2024-08-18 21:03:26 -07:00
parent 95ef280698
commit ad00ee52ff
8 changed files with 123 additions and 221 deletions

View File

@ -0,0 +1,53 @@
//
// OPMLAttributes.swift
//
//
// Created by Brent Simmons on 8/18/24.
//
import Foundation
// OPML allows for arbitrary attributes.
// These are the common attributes in OPML files used as RSS subscription lists.
private static let opmlTextKey = "text"
private static let opmlTitleKey = "title"
private static let opmlDescriptionKey = "description"
private static let opmlTypeKey = "type"
private static let opmlVersionKey = "version"
private static let opmlHMTLURLKey = "htmlUrl"
private static let opmlXMLURLKey = "xmlUrl"
// A frequent error in OPML files is to mess up the capitalization,
// so these do a case-insensitive lookup.
extension Dictionary where Key == String, Value == String {
var opml_text: String? {
object(forCaseInsensitiveKey: opmlTextKey)
}
var opml_title: String? {
object(forCaseInsensitiveKey: opmlTitleKey)
}
var opml_description: String? {
object(forCaseInsensitiveKey: opmlDescriptionKey)
}
var opml_type: String? {
object(forCaseInsensitiveKey: opmlTypeKey)
}
var opml_version: String? {
object(forCaseInsensitiveKey: opmlVersionKey)
}
var opml_htmlUrl: String? {
object(forCaseInsensitiveKey: opmlHMTLURLKey)
}
var opml_xmlUrl: String? {
object(forCaseInsensitiveKey: opmlXMLURLKey)
}
}

View File

@ -0,0 +1,42 @@
//
// OPMLItem.swift
//
//
// Created by Brent Simmons on 8/18/24.
//
import Foundation
import os
public final class OPMLItem: Sendable {
public let feedSpecifier: OPMLFeedSpecifier
public let attributes: [String: String]
public let titleFromAttributes: String?
public let isFolder: Bool
public let children: [OPMLItem]?
init?(attributes: [String : String], children: [OPMLItem]?) {
guard let feedURL = attributes.opml_xmlUrl, !feedURL.isEmpty else {
return nil
}
let titleFromAttributes = {
if let title = attributes.opml_title {
return title
}
return attributes.opml_text
}()
self.titleFromAttributes = titleFromAttributes
self.feedSpecifier = OPMLFeedSpecifier(title: titleFromAttributes, feedDescription: attributes.opml_description, homePageURL: attributes.opml_htmlUrl, feedURL: feedURL)
self.attributes = attributes
self.children = children
self.isFolder = (children?.count ?? 0) > 0
}
}

View File

@ -0,0 +1,28 @@
//
// Dictionary+Parser.swift
//
//
// Created by Brent Simmons on 8/18/24.
//
import Foundation
extension Dictionary where Key == String, Value == String {
func object(forCaseInsensitiveKey key: String) -> String? {
if let object = self[key] {
return object
}
let lowercaseKey = key.lowercased()
for (oneKey, oneValue) in self {
if lowercaseKey.caseInsensitiveCompare(oneKey) == .orderedSame {
return oneValue
}
}
return nil
}
}

View File

@ -1,36 +0,0 @@
//
// RSOPMLAttributes.h
// RSParser
//
// Created by Brent Simmons on 2/28/16.
// Copyright © 2016 Ranchero Software, LLC. All rights reserved.
//
@import Foundation;
// OPML allows for arbitrary attributes.
// These are the common attributes in OPML files used as RSS subscription lists.
extern NSString *OPMLTextKey; //text
extern NSString *OPMLTitleKey; //title
extern NSString *OPMLDescriptionKey; //description
extern NSString *OPMLTypeKey; //type
extern NSString *OPMLVersionKey; //version
extern NSString *OPMLHMTLURLKey; //htmlUrl
extern NSString *OPMLXMLURLKey; //xmlUrl
@interface NSDictionary (RSOPMLAttributes)
// A frequent error in OPML files is to mess up the capitalization,
// so these do a case-insensitive lookup.
@property (nonatomic, readonly) NSString *opml_text;
@property (nonatomic, readonly) NSString *opml_title;
@property (nonatomic, readonly) NSString *opml_description;
@property (nonatomic, readonly) NSString *opml_type;
@property (nonatomic, readonly) NSString *opml_version;
@property (nonatomic, readonly) NSString *opml_htmlUrl;
@property (nonatomic, readonly) NSString *opml_xmlUrl;
@end

View File

@ -1,68 +0,0 @@
//
// RSOPMLAttributes.m
// RSParser
//
// Created by Brent Simmons on 2/28/16.
// Copyright © 2016 Ranchero Software, LLC. All rights reserved.
//
#import "RSOPMLAttributes.h"
#import "RSParserInternal.h"
NSString *OPMLTextKey = @"text";
NSString *OPMLTitleKey = @"title";
NSString *OPMLDescriptionKey = @"description";
NSString *OPMLTypeKey = @"type";
NSString *OPMLVersionKey = @"version";
NSString *OPMLHMTLURLKey = @"htmlUrl";
NSString *OPMLXMLURLKey = @"xmlUrl";
@implementation NSDictionary (RSOPMLAttributes)
- (NSString *)opml_text {
return [self rsparser_objectForCaseInsensitiveKey:OPMLTextKey];
}
- (NSString *)opml_title {
return [self rsparser_objectForCaseInsensitiveKey:OPMLTitleKey];
}
- (NSString *)opml_description {
return [self rsparser_objectForCaseInsensitiveKey:OPMLDescriptionKey];
}
- (NSString *)opml_type {
return [self rsparser_objectForCaseInsensitiveKey:OPMLTypeKey];
}
- (NSString *)opml_version {
return [self rsparser_objectForCaseInsensitiveKey:OPMLVersionKey];
}
- (NSString *)opml_htmlUrl {
return [self rsparser_objectForCaseInsensitiveKey:OPMLHMTLURLKey];
}
- (NSString *)opml_xmlUrl {
return [self rsparser_objectForCaseInsensitiveKey:OPMLXMLURLKey];
}
@end

View File

@ -1,30 +0,0 @@
//
// RSOPMLItem.h
// RSParser
//
// Created by Brent Simmons on 2/28/16.
// Copyright © 2016 Ranchero Software, LLC. All rights reserved.
//
@import Foundation;
@class RSOPMLFeedSpecifier;
NS_ASSUME_NONNULL_BEGIN
@interface RSOPMLItem : NSObject
@property (nonatomic, nullable) NSDictionary *attributes;
@property (nonatomic, nullable) NSArray <RSOPMLItem *> *children;
- (void)addChild:(RSOPMLItem *)child;
@property (nonatomic, nullable, readonly) RSOPMLFeedSpecifier *feedSpecifier;
@property (nonatomic, nullable, readonly) NSString *titleFromAttributes;
@property (nonatomic, readonly) BOOL isFolder;
@end
NS_ASSUME_NONNULL_END

View File

@ -1,87 +0,0 @@
//
// RSOPMLItem.m
// RSParser
//
// Created by Brent Simmons on 2/28/16.
// Copyright © 2016 Ranchero Software, LLC. All rights reserved.
//
#import "RSOPMLItem.h"
#import "RSOPMLAttributes.h"
#import "RSOPMLFeedSpecifier.h"
#import "RSParserInternal.h"
@interface RSOPMLItem ()
@property (nonatomic) NSMutableArray *mutableChildren;
@end
@implementation RSOPMLItem
@synthesize children = _children;
@synthesize feedSpecifier = _feedSpecifier;
- (NSArray *)children {
return [self.mutableChildren copy];
}
- (void)setChildren:(NSArray *)children {
_children = children;
self.mutableChildren = [_children mutableCopy];
}
- (void)addChild:(RSOPMLItem *)child {
if (!self.mutableChildren) {
self.mutableChildren = [NSMutableArray new];
}
[self.mutableChildren addObject:child];
}
- (RSOPMLFeedSpecifier *)feedSpecifier {
if (_feedSpecifier) {
return _feedSpecifier;
}
NSString *feedURL = self.attributes.opml_xmlUrl;
if (RSParserObjectIsEmpty(feedURL)) {
return nil;
}
_feedSpecifier = [[RSOPMLFeedSpecifier alloc] initWithTitle:self.titleFromAttributes feedDescription:self.attributes.opml_description homePageURL:self.attributes.opml_htmlUrl feedURL:feedURL];
return _feedSpecifier;
}
- (NSString *)titleFromAttributes {
NSString *title = self.attributes.opml_title;
if (title) {
return title;
}
title = self.attributes.opml_text;
if (title) {
return title;
}
return nil;
}
- (BOOL)isFolder {
return self.mutableChildren.count > 0;
}
@end