Skip to content

Commit

Permalink
Wrap resources in namespaces
Browse files Browse the repository at this point in the history
  • Loading branch information
NathanE73 committed Dec 3, 2023
1 parent fd0c4ea commit f214018
Show file tree
Hide file tree
Showing 33 changed files with 414 additions and 328 deletions.
90 changes: 90 additions & 0 deletions Sources/BlackboardFramework/Assets/Asset.swift
@@ -0,0 +1,90 @@
//
// Copyright (c) 2024 Nathan E. Walczak
//
// MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//

import Foundation

protocol Asset {
var namespace: String? { get }
var name: String { get }
}

indirect enum AssetItem<A: Asset> {
case asset(A)
case namespace(String, [AssetItem<A>])
}

extension AssetItem: Comparable {

static func < (lhs: AssetItem<A>, rhs: AssetItem<A>) -> Bool {
switch (lhs, rhs) {
case let (.asset(lhs), .asset(rhs)):
return lhs.name.localizedCaseInsensitiveCompare(rhs.name) == .orderedAscending
case let (.asset(lhs), .namespace(rhs, _)):
return lhs.name.localizedCaseInsensitiveCompare(rhs) == .orderedAscending
case let (.namespace(lhs, _), .asset(rhs)):
return lhs.localizedCaseInsensitiveCompare(rhs.name) == .orderedAscending
case let (.namespace(lhs, _), .namespace(rhs, _)):
return lhs.localizedCaseInsensitiveCompare(rhs) == .orderedAscending
}
}

static func == (lhs: AssetItem<A>, rhs: AssetItem<A>) -> Bool {
switch (lhs, rhs) {
case let (.asset(lhs), .asset(rhs)):
return lhs.name == rhs.name
case let (.asset(lhs), .namespace(rhs, _)):
return lhs.name == rhs
case let (.namespace(lhs, _), .asset(rhs)):
return lhs == rhs.name
case let (.namespace(lhs, _), .namespace(rhs, _)):
return lhs == rhs
}
}

}

extension Collection {
func mapAssets<T, E: Any>(_ transform: @escaping (E) -> T) -> [AssetItem<T>] where Element == AssetItem<E> {
map { something in
switch something {
case let .asset(asset):
return .asset(transform(asset))
case let .namespace(namespace, assets):
return .namespace(namespace, assets.mapAssets(transform))
}
}
}

func flatMapAssets<E: Any>() -> [E] where Element == AssetItem<E> {
flatMap { something in
switch something {
case let .asset(asset):
return [asset]
case let .namespace(_, assets):
return assets.flatMapAssets()
}
}

}
}
18 changes: 12 additions & 6 deletions Sources/BlackboardFramework/Assets/AssetSetFactory.swift
Expand Up @@ -26,7 +26,7 @@ import Foundation

protocol AssetSetFactory {

associatedtype AssetSet
associatedtype AssetSet: Asset

var pathExtension: String { get }

Expand All @@ -36,18 +36,22 @@ protocol AssetSetFactory {

extension AssetSetFactory {

func assetsAt(path: String) -> [AssetSet] {
func assetItemsAt(paths: [String]) -> [AssetItem<AssetSet>] {
paths.flatMap { assetsAt(path: $0) }
}

func assetsAt(path: String) -> [AssetItem<AssetSet>] {
assetsAt(path: path, namespace: nil)
}

func assetsAt(path: String, namespace: String?) -> [AssetSet] {
func assetsAt(path: String, namespace: String?) -> [AssetItem<AssetSet>] {
let fileManager = FileManager.default

guard let contents = try? fileManager.contentsOfDirectory(atPath: path) else {
return []
}

return contents.flatMap { content -> [AssetSet] in
return contents.flatMap { content -> [AssetItem<AssetSet>] in
let file = path.appendingPathComponent(content)

guard fileManager.isDirectory(file) else {
Expand All @@ -56,17 +60,19 @@ extension AssetSetFactory {

if file.pathExtension == pathExtension {
if let asset = assetAt(path: file, namespace: namespace) {
return [asset]
return [.asset(asset)]
}
return []
}

if providesNamespaceAt(path: file) {
let namespace = Naming.namespace(from: namespace, content)
return assetsAt(path: file, namespace: namespace)
let assets = assetsAt(path: file, namespace: namespace)
return assets.isEmpty ? [] : [.namespace(namespace, assets)]
}
return assetsAt(path: file, namespace: namespace)
}
.sorted()
}

func assetAt(path: String, namespace: String?) -> AssetSet? {
Expand Down
10 changes: 7 additions & 3 deletions Sources/BlackboardFramework/Color Assets/BlackboardColor.swift
Expand Up @@ -24,12 +24,14 @@

import Foundation

struct BlackboardColor {
struct BlackboardColor: Asset {

var namespace: String?
var name: String
var propertyName: String

var resourceName: String
var propertyName: String
var propertyPath: String

}

Expand All @@ -39,10 +41,12 @@ extension BlackboardColor {
namespace = colorSet.namespace
name = colorSet.name

resourceName = colorSet.resourceName

propertyName = Naming.methodName(from: colorSet.name)
.removingSuffix("Color")

resourceName = Naming.namespace(from: colorSet.namespace, colorSet.name) ?? colorSet.name
propertyPath = Naming.propertyPath(namespace: namespace, propertyName: propertyName)
}

}
Expand Up @@ -28,14 +28,14 @@ extension SwiftSource {

// MARK: Color Assets

func appendColorAssets(colors: [BlackboardColor]) -> Self {
func appendColorAssets(colors: [AssetItem<BlackboardColor>]) -> Self {
appendHeading(filename: Filename.ColorAsset, modules: ["Foundation"])
append("public struct ColorAsset: Hashable") {
append("let name: String")
}
append()
append("public extension ColorAsset") {
colors.forEach { color in
appendAssetItems(colors) { color in
append("static let \(color.propertyName) = ColorAsset(name: \"\(color.resourceName)\")")
}
}
Expand All @@ -46,7 +46,7 @@ extension SwiftSource {

// MARK: Color

func appendColors(colors: [BlackboardColor], target: Version) -> Self {
func appendColors(colors: [AssetItem<BlackboardColor>], target: Version) -> Self {
appendHeading(filename: Filename.Color, modules: ["SwiftUI"], includeBundle: true)
appendAvailability(.available(platform: .iOS, version: Version(13, 0)), target: target)
append("public extension Color") {
Expand All @@ -56,8 +56,8 @@ extension SwiftSource {
}
append()
directive("#if swift(<5.9.0)")
colors.forEach { color in
append("static var \(color.propertyName): Color { Color(asset: .\(color.propertyName)) }")
appendAssetItems(colors) { color in
append("static var \(color.propertyName): Color { Color(asset: .\(color.propertyPath)) }")
}
directive("#endif")
append()
Expand All @@ -67,8 +67,10 @@ extension SwiftSource {
appendAvailability(.available(platform: .iOS, version: Version(13, 0)), target: target)
append("public extension ShapeStyle where Self == Color") {
append()
colors.forEach { color in
append("static var \(color.propertyName): Color { Color(asset: .\(color.propertyName)) }")
colors.forEach { asset in
if case let .asset(color) = asset, color.namespace == nil {
append("static var \(color.propertyName): Color { Color(asset: .\(color.propertyPath)) }")
}
}
append()
}
Expand All @@ -80,15 +82,15 @@ extension SwiftSource {

// MARK: CGColor

func appendCGColors(colors: [BlackboardColor]) -> Self {
func appendCGColors(colors: [AssetItem<BlackboardColor>]) -> Self {
appendHeading(filename: Filename.CGColor, modules: ["CoreGraphics"])
append("public extension ColorAsset") {
append("var cgColor: CGColor { color.cgColor }")
}
append()
append("public extension CGColor") {
colors.forEach { color in
append("static var \(color.propertyName): CGColor { ColorAsset.\(color.propertyName).cgColor }")
appendAssetItems(colors) { color in
append("static var \(color.propertyName): CGColor { ColorAsset.\(color.propertyPath).cgColor }")
}
}
append()
Expand All @@ -98,7 +100,7 @@ extension SwiftSource {

// MARK: UIColor

func appendUIColors(colors: [BlackboardColor]) -> Self {
func appendUIColors(colors: [AssetItem<BlackboardColor>]) -> Self {
appendHeading(filename: Filename.UIColor, modules: ["UIKit"], includeBundle: true)
append("public extension ColorAsset") {
append("var color: UIColor { UIColor(asset: self) }")
Expand All @@ -111,8 +113,8 @@ extension SwiftSource {
}
append()
directive("#if swift(<5.9.0)")
colors.forEach { color in
append("static var \(color.propertyName): UIColor { UIColor(asset: .\(color.propertyName)) }")
appendAssetItems(colors) { color in
append("static var \(color.propertyName): UIColor { UIColor(asset: .\(color.propertyPath)) }")
}
directive("#endif")
append()
Expand Down
6 changes: 5 additions & 1 deletion Sources/BlackboardFramework/Color Assets/ColorSet.swift
Expand Up @@ -24,9 +24,13 @@

import Foundation

struct ColorSet {
struct ColorSet: Asset {
var namespace: String?
var name: String

var resourceName: String {
Naming.namespace(from: namespace, name)
}
}

extension ColorSet {
Expand Down
Expand Up @@ -28,10 +28,6 @@ class ColorSetFactory: AssetSetFactory {

let pathExtension = "colorset"

func colorSetsAt(paths: [String]) -> [ColorSet] {
paths.flatMap(assetsAt(path:))
}

func asset(namespace: String?, name: String, data: Data) -> ColorSet? {
guard let assetColorSet = try? JSONDecoder().decode(AssetColorSet.self, from: data) else {
return nil
Expand Down
12 changes: 8 additions & 4 deletions Sources/BlackboardFramework/Data Assets/BlackboardData.swift
Expand Up @@ -24,13 +24,15 @@

import Foundation

struct BlackboardData {
struct BlackboardData: Asset {

var namespace: String?
var name: String
var propertyName: String
var resourceName: String

var resourceName: String
var propertyName: String
var propertyPath: String

}

extension BlackboardData {
Expand All @@ -39,10 +41,12 @@ extension BlackboardData {
namespace = dataSet.namespace
name = dataSet.name

resourceName = dataSet.resourceName

propertyName = Naming.methodName(from: dataSet.name)
.removingSuffix("Data")

resourceName = Naming.namespace(from: dataSet.namespace, dataSet.name) ?? dataSet.name
propertyPath = Naming.propertyPath(namespace: namespace, propertyName: propertyName)
}

}
Expand Up @@ -28,14 +28,14 @@ extension SwiftSource {

// MARK: Data Assets

func appendDataAssets(data: [BlackboardData]) -> Self {
func appendDataAssets(data: [AssetItem<BlackboardData>]) -> Self {
appendHeading(filename: Filename.DataAsset, modules: ["Foundation"])
append("public struct DataAsset: Hashable") {
append("let name: String")
}
append()
append("public extension DataAsset") {
data.forEach { data in
appendAssetItems(data) { data in
append("static let \(data.propertyName) = DataAsset(name: \"\(data.resourceName)\")")
}
}
Expand All @@ -46,7 +46,7 @@ extension SwiftSource {

// MARK: NSDataAsset

func appendNSDataAsset(data: [BlackboardData]) -> Self {
func appendNSDataAsset(data: [AssetItem<BlackboardData>]) -> Self {
appendHeading(filename: Filename.NSDataAsset, modules: ["UIKit"], includeBundle: true)
append("public extension DataAsset") {
append("var dataAsset: NSDataAsset { NSDataAsset(asset: self) }")
Expand Down
6 changes: 5 additions & 1 deletion Sources/BlackboardFramework/Data Assets/DataSet.swift
Expand Up @@ -24,9 +24,13 @@

import Foundation

struct DataSet {
struct DataSet: Asset {
var namespace: String?
var name: String

var resourceName: String {
Naming.namespace(from: namespace, name)
}
}

extension DataSet {
Expand Down
4 changes: 0 additions & 4 deletions Sources/BlackboardFramework/Data Assets/DataSetFactory.swift
Expand Up @@ -28,10 +28,6 @@ class DataSetFactory: AssetSetFactory {

let pathExtension = "dataset"

func dataSetsAt(paths: [String]) -> [DataSet] {
paths.flatMap(assetsAt(path:))
}

func asset(namespace: String?, name: String, data: Data) -> DataSet? {
guard let assetDataSet = try? JSONDecoder().decode(AssetDataSet.self, from: data) else {
return nil
Expand Down

0 comments on commit f214018

Please sign in to comment.