From b5dce1fb6e331a1365ccf93b491c07eb52eafe21 Mon Sep 17 00:00:00 2001 From: "Mikhail A.Z." Date: Sun, 26 Oct 2025 13:22:15 +0700 Subject: [PATCH] ADDED: regular update --- .github/workflows/main.yml | 24 +- APPROBATION.md | 2 +- CHANGELOG.md | 10 + LICENSE | 4 +- PDMStar.swift | 12 +- PDMSupportingStar.swift | 2 +- Package.swift | 6 +- README.md | 97 ++++--- Sources/PerseusDarkMode/CPLStar.swift | 331 +++++++++++++++++----- Sources/PerseusDarkMode/TheDarkness.swift | 10 +- 10 files changed, 358 insertions(+), 140 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 5a5e2b2..a8351b1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -12,12 +12,12 @@ on: jobs: macOS-build: - runs-on: macos-15 + runs-on: macos-latest steps: - name: Checkout the code uses: actions/checkout@v4 - - name: Select Xcode 16.2 - run: sudo xcode-select -s "/Applications/Xcode_16.2.app" +# - name: Select Xcode 16.2 +# run: sudo xcode-select -s "/Applications/Xcode_16.2.app" - uses: mxcl/xcodebuild@v3 with: platform: macOS @@ -28,12 +28,12 @@ jobs: upload-logs: always macOS-test: - runs-on: macos-15 + runs-on: macos-latest steps: - name: Checkout the code uses: actions/checkout@v4 - - name: Select Xcode 16.2 - run: sudo xcode-select -s "/Applications/Xcode_16.2.app" +# - name: Select Xcode 16.2 +# run: sudo xcode-select -s "/Applications/Xcode_16.2.app" - uses: mxcl/xcodebuild@v3 with: platform: macOS @@ -44,12 +44,12 @@ jobs: upload-logs: always iOS-build: - runs-on: macos-15 + runs-on: macos-latest steps: - name: Checkout the code uses: actions/checkout@v4 - - name: Select Xcode 16.2 - run: sudo xcode-select -s "/Applications/Xcode_16.2.app" +# - name: Select Xcode 16.2 +# run: sudo xcode-select -s "/Applications/Xcode_16.2.app" - uses: mxcl/xcodebuild@v3 with: platform: iOS @@ -60,12 +60,12 @@ jobs: upload-logs: always iOS-test: - runs-on: macos-15 + runs-on: macos-latest steps: - name: Checkout the code uses: actions/checkout@v4 - - name: Select Xcode 16.2 - run: sudo xcode-select -s "/Applications/Xcode_16.2.app" +# - name: Select Xcode 16.2 +# run: sudo xcode-select -s "/Applications/Xcode_16.2.app" - uses: mxcl/xcodebuild@v3 with: platform: iOS diff --git a/APPROBATION.md b/APPROBATION.md index eb3dc39..3c7db99 100644 --- a/APPROBATION.md +++ b/APPROBATION.md @@ -1,4 +1,4 @@ -# Approbation Matrix / PerseusDarkMode 2.0.0 && 2.0.1 && 2.0.2 && 2.0.3 && 2.0.4 +# Approbation Matrix / PerseusDarkMode 2.0.0 && 2.0.1 && 2.0.2 && 2.0.3 && 2.0.4 && 2.1.0 > NOTE: To catch all log messages Mac Console should be started first then after a little while the logged app. diff --git a/CHANGELOG.md b/CHANGELOG.md index bb497a8..83176a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 Dates in this file meets Gregorian calendar. Date in format YYYY-MM-DD. +## [2.1.0] - [2025-10-30], PerseusDarkMode + +### Added + +- Contents section to README. + +### Updated + +- CPL dependency to v1.6.0. + ## [2.0.4] - [2025-07-22], PerseusDarkMode ### Changed diff --git a/LICENSE b/LICENSE index ae281d0..27130df 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,7 @@ MIT License -Copyright © 7530 - 7533 Mikhail A. Zhigulin of Novosibirsk -Copyright © 7533 PerseusRealDeal +Copyright © 7530 - 7534 Mikhail A. Zhigulin of Novosibirsk +Copyright © 7533 - 7534 PerseusRealDeal The year starts from the creation of the world according to a Slavic calendar. September, the 1st of Slavic year. diff --git a/PDMStar.swift b/PDMStar.swift index fd31910..0ba552d 100644 --- a/PDMStar.swift +++ b/PDMStar.swift @@ -1,26 +1,26 @@ // // PDMStar.swift -// Version: 2.0.4 +// Version: 2.1.0 // // Standalone PerseusDarkMode. // // // For iOS and macOS only. Use Stars to adopt for the specifics you need. // -// DESC: THE DARKNESS YOU CAN FORCE. +// DESC: THE DARKNESS YOU CAN FORCE // // Created by Mikhail Zhigulin in 7530. // -// Copyright © 7530 - 7533 Mikhail Zhigulin of Novosibirsk -// Copyright © 7533 PerseusRealDeal +// Copyright © 7530 - 7534 Mikhail Zhigulin of Novosibirsk +// Copyright © 7533 - 7534 PerseusRealDeal // // All rights reserved. // // // MIT License // -// Copyright © 7530 - 7533 Mikhail A. Zhigulin of Novosibirsk -// Copyright © 7533 PerseusRealDeal +// Copyright © 7530 - 7534 Mikhail A. Zhigulin of Novosibirsk +// Copyright © 7533 - 7534 PerseusRealDeal // // The year starts from the creation of the world according to a Slavic calendar. // September, the 1st of Slavic year. diff --git a/PDMSupportingStar.swift b/PDMSupportingStar.swift index 8cb4d68..11ec0e7 100644 --- a/PDMSupportingStar.swift +++ b/PDMSupportingStar.swift @@ -1,6 +1,6 @@ // // PDMSupportingStar.swift -// Version: 2.0.4 +// Version: 2.1.0 // // The Darkness Support (PerseusUISystemKit previously) // diff --git a/Package.swift b/Package.swift index 0eecd8e..a66cb4a 100644 --- a/Package.swift +++ b/Package.swift @@ -1,14 +1,14 @@ // swift-tools-version:5.7 /* Package.swift - Version: 2.0.4 + Version: 2.1.0 For iOS and macOS only. Use Stars to adopt for the specifics you need. Created by Mikhail Zhigulin in 7530. - Copyright © 7530 - 7533 Mikhail A. Zhigulin of Novosibirsk - Copyright © 7533 PerseusRealDeal + Copyright © 7530 - 7534 Mikhail A. Zhigulin of Novosibirsk + Copyright © 7533 - 7534 PerseusRealDeal Licensed under the MIT license. See LICENSE file. All rights reserved. diff --git a/README.md b/README.md index 1b2395e..66c0337 100644 --- a/README.md +++ b/README.md @@ -1,49 +1,75 @@ # PerseusDarkMode — Xcode 14.2+ -> [`iOS approbation app`](https://github.com/perseusrealdeal/TheOneRing) [`macOS approbation app`](https://github.com/perseusrealdeal/Arkenstone) - -> The light-weight darkness in Swift you can force. Hereinafter PDM stands for `P`erseus `D`ark `M`ode.
- -> - To build option kinda `Night/Day/System Mode` or `On/Off/System Dark Mode`.
-> - To be awared of Dark Mode changes if you need.
- -> `PDM` is a single author and personale solution developed in `person-to-person` relationship paradigm. - [![Actions Status](https://github.com/perseusrealdeal/PerseusDarkMode/actions/workflows/main.yml/badge.svg)](https://github.com/perseusrealdeal/PerseusDarkMode/actions/workflows/main.yml) [![Style](https://github.com/perseusrealdeal/PerseusDarkMode/actions/workflows/swiftlint.yml/badge.svg)](https://github.com/perseusrealdeal/PerseusDarkMode/actions/workflows/swiftlint.yml) -[![Version](https://img.shields.io/badge/Version-2.0.4-green.svg)](/CHANGELOG.md) -[![Platforms](https://img.shields.io/badge/Platforms-macOS%2010.13+Cocoa_|_iOS%2011.0+UIKit-orange.svg)](https://en.wikipedia.org/wiki/List_of_Apple_products) +[![Version](https://img.shields.io/badge/Version-2.1.0-green.svg)](/CHANGELOG.md) +[![Platforms](https://img.shields.io/badge/Platforms-macOS%2010.13+_|_iOS%2011.0+-orange.svg)](https://en.wikipedia.org/wiki/List_of_Apple_products) [![Xcode 14.2](https://img.shields.io/badge/Xcode-14.2+-red.svg)](https://en.wikipedia.org/wiki/Xcode) [![Swift 5.7](https://img.shields.io/badge/Swift-5.7-red.svg)](https://www.swift.org) [![License](http://img.shields.io/:License-MIT-blue.svg)](/LICENSE) +> Home-made product. The light-weight darkness in Swift you can force. + +> `1:` Build option kinda `Night/Day/System Mode` or `On/Off/System Dark Mode`.
+> `2:` Be awared of Dark Mode changes if you need. + +> `PDM` is a single author and personale solution developed in `P2P` relationship paradigm. + ## Integration Capabilities [![Standalone](https://img.shields.io/badge/Standalone-available-informational.svg)](/PDMStar.swift) [![Swift Package Manager compatible](https://img.shields.io/badge/Swift%20Package%20Manager-compatible-4BC51D.svg)](/Package.swift) -> Use Stars to adopt [`PDM`](/PDMStar.swift) for the specifics you need. - ## Dependencies -[![ConsolePerseusLogger](http://img.shields.io/:ConsolePerseusLogger-1.5.1-green.svg)](https://github.com/perseusrealdeal/ConsolePerseusLogger.git) +[![ConsolePerseusLogger](http://img.shields.io/:ConsolePerseusLogger-1.6.0-green.svg)](https://github.com/perseusrealdeal/ConsolePerseusLogger.git) -# Support Code +## Support Code [![Standalone](https://img.shields.io/badge/Standalone-available-informational.svg)](/PDMSupportingStar.swift) [![License](http://img.shields.io/:License-Unlicense-green.svg)](http://unlicense.org/) -> [`PDMSupportingStar.swift`](/PDMSupportingStar.swift) is a peace of code a widly helpful in accord with PDM. +> [`PDMSupportingStar.swift`](/PDMSupportingStar.swift) is a peace of code a widly helpful in accord with PDM.
+> `PDMSupportingStar.swift` goes as an external part of PDM. + +## Our Terms + +> [`CPL`](https://github.com/perseusrealdeal/ConsolePerseusLogger.git) stands for `C`onsole `P`erseus `L`ogger.
+> [`PGK`](https://github.com/perseusrealdeal/PerseusGeoKit.git) stands for `P`erseus `G`eo `K`it.
+> [`PDM`](https://github.com/perseusrealdeal/PerseusDarkMode.git) stands for `P`erseus `D`ark `M`ode.
+> `P2P` stands for `P`erson-`to`-`P`erson.
+> [`A3`](https://docs.google.com/document/d/1K2jOeIknKRRpTEEIPKhxO2H_1eBTof5uTXxyOm5g6nQ) stands for `A`pple `A`pps `A`pprobation.
+> [`T3`](https://github.com/perseusrealdeal/TheTechnologicalTree) stands for `T`he `T`echnological `T`ree. + +## PDM in Use + +> `In approbation:` [`iOS app`](https://github.com/perseusrealdeal/TheOneRing) [`macOS app`](https://github.com/perseusrealdeal/Arkenstone)
+> `In business:` [`The Dark Moon`](https://github.com/perseusrealdeal/TheDarkMoon) -> PDMSupportingStar.swift goes as an external part of PDM. +> `For details:` [`Approbation and A3 Environment`](/APPROBATION.md) / [`CHANGELOG`](/CHANGELOG.md)
-## Approbation Matrix +# Contents -> [`A3 Environment and Approbation`](/APPROBATION.md) / [`CHANGELOG`](/CHANGELOG.md) for details. +* [In brief](#In-brief) +* [Build requirements](#Build-requirements) +* [First-party software](#First-party-software) +* [Third-party software](#Third-party-software) +* [Installation](#Installation) + * [Cocoa macOS project](#Cocoa-macOS-project) + * [UIKit iOS project](#UIKit-iOS-project) +* [Usage](#Usage) + * [Force Dark Mode](#Force-Dark-Mode) + * [Get awared of DarkMode changes](#Get-awared-of-DarkMode-changes) + * [DarkMode change sample](#DarkMode-change-sample) +* [Points taken into account](#Points-taken-into-account) +* [License MIT](#License-MIT) + * [Other Required License Notices](#Other-Required-License-Notices) +* [Credits](#Credits) +* [Author](#Author) -## In brief > Idea to use, the Why +# In brief -> THE DARKNESS YOU CAN FORCE.
+> THE DARKNESS YOU CAN FORCE
@@ -64,7 +90,7 @@ > [!IMPORTANT] > Screenshots taken from Approbation Apps [`iOS`](https://github.com/perseusrealdeal/TheOneRing) and [`macOS`](https://github.com/perseusrealdeal/Arkenstone). -## Build requirements +# Build requirements - [macOS Monterey 12.7.6+](https://apps.apple.com/by/app/macos-monterey/id1576738294) / [Xcode 14.2+](https://developer.apple.com/services-account/download?path=/Developer_Tools/Xcode_14.2/Xcode_14.2.xip) @@ -74,7 +100,7 @@ | Type | Name | License | | ---- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | -| Star | [ConsolePerseusLogger](https://github.com/perseusrealdeal/ConsolePerseusLogger) / [1.5.1](https://github.com/perseusrealdeal/ConsolePerseusLogger/releases/tag/1.5.1) | MIT | +| Star | [ConsolePerseusLogger](https://github.com/perseusrealdeal/ConsolePerseusLogger) / [1.6.0](https://github.com/perseusrealdeal/ConsolePerseusLogger/releases/tag/1.6.0) | MIT | # Third-party software @@ -93,7 +119,7 @@ > Swift Package Manager: `https://github.com/perseusrealdeal/PerseusDarkMode` -## Steps for Cocoa macOS project +## Cocoa macOS project `Step 2:` In the AppDelegate when `applicationDidFinishLaunching` call `force` @@ -139,7 +165,7 @@ class MainWindowController: NSWindowController, NSWindowDelegate { ``` -## Steps for UIKit iOS project +## UIKit iOS project `Step 2:` In the AppDelegate when `didFinishLaunchingWithOptions` call `force` @@ -231,11 +257,11 @@ DarkModeAgent.force(.off) // It's a sunny day for the app. The `force` will change the appearance of your app immediately including system components and will make run all custom DarkMode code `makeUp()`. -## Get awared of DarkMode Changes +## Get awared of DarkMode changes -> To declare custom DarkMode sensitive code that runs every time if DarkMode Changes register the object or creat a DarkMode trigger: +> To declare custom DarkMode sensitive code that runs every time if DarkMode Changes register the object or create a DarkMode trigger: -`Use Case -` Register an object to be notified on changes +`Use Case 1:` Register an object to be notified on changes ```swift @@ -252,7 +278,7 @@ class DarkModeSensitiveObject { ``` -`Use Case -` Creat a DarkMode trigger and give it an action +`Use Case 2:` Create a DarkMode trigger and give it an action ```swift @@ -273,9 +299,9 @@ class DarkModeSensitiveObject { ``` -## React to DarkMode Changes +## DarkMode change sample -`Use Case -` Custom DarkMode Sensitive Color +`Use Case:` Custom DarkMode sensitive color ```swift @@ -309,11 +335,12 @@ extension Color { ``` -> Use Custom DarkMode sensitive color. +> And use custom DarkMode sensitive color: ```swift -// Runs every time if the DarkMode changes. Use KVO (DarkModeObserver) or be registered by DarkModeAgent. +// Runs every time if the DarkMode changes. +// Use KVO (DarkModeObserver) or be registered with DarkModeAgent. @objc private func makeUp() { self.backgroundColor = .customRed } @@ -330,8 +357,8 @@ extension Color { # License MIT -Copyright © 7530 - 7533 Mikhail A. Zhigulin of Novosibirsk
-Copyright © 7533 PerseusRealDeal +Copyright © 7530 - 7534 Mikhail A. Zhigulin of Novosibirsk
+Copyright © 7533 - 7534 PerseusRealDeal - The year starts from the creation of the world according to a Slavic calendar. - September, the 1st of Slavic year. It means that "Sep 01, 2024" is the beginning of 7533. diff --git a/Sources/PerseusDarkMode/CPLStar.swift b/Sources/PerseusDarkMode/CPLStar.swift index c8bf903..9bb6c61 100644 --- a/Sources/PerseusDarkMode/CPLStar.swift +++ b/Sources/PerseusDarkMode/CPLStar.swift @@ -1,11 +1,10 @@ // // CPLStar.swift -// Version: 1.5.1 +// Version: 1.6.0 // // Standalone ConsolePerseusLogger. // -// -// For iOS and macOS only. Use Stars to adopt for the specifics you need. +// For iOS and macOS. Use Stars to adopt for the specifics you need. // // DESC: USE LOGGER LIKE A VARIABLE ANYWHERE YOU WANT. // @@ -13,16 +12,19 @@ // // Created by Mikhail Zhigulin in 7531. // -// Copyright © 7531 - 7533 Mikhail A. Zhigulin of Novosibirsk -// Copyright © 7531 - 7533 PerseusRealDeal +// BASED_ON_LOGGER: https://gist.github.com/PerseusRealDeal/df456a9825fcface44eca738056eb6d5 +// BASED_ON_REPORT: https://gist.github.com/PerseusRealDeal/9a4118301b59d43969d8edf5ebc3a571 +// +// Copyright © 7531 - 7534 Mikhail A. Zhigulin of Novosibirsk +// Copyright © 7531 - 7534 PerseusRealDeal // // All rights reserved. // // // MIT License // -// Copyright © 7531 - 7533 Mikhail A. Zhigulin of Novosibirsk -// Copyright © 7531 - 7533 PerseusRealDeal +// Copyright © 7531 - 7534 Mikhail A. Zhigulin of Novosibirsk +// Copyright © 7531 - 7534 PerseusRealDeal // // The year starts from the creation of the world according to a Slavic calendar. // September, the 1st of Slavic year. @@ -52,19 +54,27 @@ import Foundation import os // swiftlint:disable type_name -typealias log = PerseusLogger // In SPM package should be not public except TheOne. +typealias log = PerseusLogger // Not public in SPM package, except TheOne. // swiftlint:enable type_name -public typealias ConsoleObject = (subsystem: String, category: String) -public typealias LocalTime = (date: String, time: String) -public typealias PIDandTID = (pid: String, tid: String) // PID and Thread ID. - -public typealias MessageDelegate = ( - (String, PerseusLogger.Level, LocalTime, PIDandTID) -> Void -) +// Not public in SPM package, except TheOne. +protocol PerseusDelegatedMessage: AnyObject { + var message: String { get set } +} public class PerseusLogger { + // MARK: - Typealiases + + public typealias ConsoleObject = (subsystem: String, category: String) + public typealias LocalTime = (date: String, time: String, timeUTC: TimeInterval) + public typealias PIDandTID = (pid: String, tid: String) // PID and Thread ID. + public typealias Directives = (fileName: String, line: UInt) // #file and #line. + + public typealias MessageDelegate = ( + (String, Level, LocalTime, PIDandTID, User, Directives) -> Void + ) + // MARK: - Constants private static let SUBSYSTEM = "Perseus" @@ -72,18 +82,24 @@ public class PerseusLogger { // MARK: - Specifics - public enum Status: String, Decodable { + public enum Status: String, Decodable, CaseIterable { case on case off } - public enum Output: String, Decodable { + public enum Output: String, Decodable, CaseIterable { case standard // In Use: Swift.print(""). - case consoleapp - case custom // In Use: customActionOnMessage?(_:_:_:_:). + case consoleapp // In Use: Logger structure from iOS 14.0, macOS 11.0, NSLog otherwise. + case custom // In Use: customActionOnMessage?(_:_:_:_:_:_:). + } + + // log.message("Notification...", .notice, .custom, .enduser) + public enum User: String, Decodable, CaseIterable { + case enduser // Ignores status turned == .off; level == .notice is recommended. + case operative } - public enum Level: Int, CustomStringConvertible, Decodable { + public enum Level: Int, CustomStringConvertible, Decodable, CaseIterable { public var description: String { switch self { @@ -122,20 +138,20 @@ public class PerseusLogger { case fault = 1 } - public enum TimeMultiply: String, Decodable { + public enum TimeMultiply: String, Decodable, CaseIterable { // case millisecond // -3. // case microsecond // -6. case nanosecond // -9. } - public enum TIDNumber: String, Decodable { + public enum TIDNumber: String, Decodable, CaseIterable { case hexadecimal case decimal } - public enum MessageFormat: String, Decodable { + public enum MessageFormat: String, Decodable, CaseIterable { - case short + case short // Depends on message details visibility flags. // marks true, time false, owner false, directives false // [DEBUG] message @@ -164,16 +180,16 @@ public class PerseusLogger { // marks false, time false, owner false, directives false // message - case full + case full // Forcefully. No matter what message details visibility flags are. // [DEBUG] [2025-04-17] [20:31:53:630918979] [6317:0x2519d] message, file: File.swift, line: 29 - case textonly + case textonly // Forcefully. No matter what message details visibility flags are. // message } // MARK: - Properties - public static var customActionOnMessage: MessageDelegate? + public static var customActionOnMessage: PerseusLogger.MessageDelegate? #if DEBUG public static var turned = Status.on @@ -188,17 +204,22 @@ public class PerseusLogger { public static var subsecond = TimeMultiply.nanosecond public static var tidnumber = TIDNumber.hexadecimal + // MARK: - Message Details Visibility Flags + public static var format = MessageFormat.short - public static var marks = true // Controls tags [TYPE] [DATE] [TIME]. - public static var time = false // + [DATE] [TIME] to message. Depends on format and marks. - public static var owner = false // + [PID:TID] to message. Depends on format. - public static var directives = false // + File# and Line# to message. Depends on format. + // [TYPE] [DATE] [TIME] [PID:TID] message, file: #, line: # + public static var marks = true // [TYPE] + public static var time = false // [DATE] [TIME] Depends on format and marks + public static var owner = false // [PID:TID] Depends on format + public static var directives = false // file# and line# Depends on format #if targetEnvironment(simulator) - public static var debugIsInfo = true // Shows DEBUG message as INFO in macOS Console.app. + public static var debugIsInfo = true // Shows DEBUG message as INFO in macOS Console. #endif + // MARK: - Special Properties + public static var logObject: ConsoleObject? { didSet { @@ -237,52 +258,26 @@ public class PerseusLogger { // MARK: - Contract - public static func loadConfig(_ profile: ProfileCPL) -> Bool { - if let data = profile.json.data(using: .utf8) { - if let jsonConfig = decodeJsonProfile(data) { - reloadOptions(jsonConfig) - return true - } - log.message("Failed to decode CPL json config data!", .error) - return false - } - log.message("Failed to load CPL config data!", .error) - return false - } - - public static func loadConfig(_ json: URL) -> Bool { - if FileManager.default.fileExists(atPath: json.relativePath) { - if let data = try? Data(contentsOf: json) { - if let jsonConfig = decodeJsonProfile(data) { - reloadOptions(jsonConfig) - return true - } - log.message("Failed to decode CPL json config data!", .error) - return false - } - log.message("Failed to load CPL config data!", .error) - return false - } - log.message("CPL config file doesn't exist!", .error) - return false - } - public static func message(_ text: @autoclosure () -> String, _ type: Level = .debug, _ oput: Output = PerseusLogger.output, + _ user: User = .operative, _ file: StaticString = #file, _ line: UInt = #line) { - guard turned == .on, type.rawValue <= level.rawValue else { return } + guard turned == .on || user == .enduser, type.rawValue <= level.rawValue + else { + return + } var message = "" // Path. let withDirectives = (format == .full) ? true : directives && (format != .textonly) + let fileName = (file.description as NSString).lastPathComponent if withDirectives { - let fileName = (file.description as NSString).lastPathComponent message = "\(text()), file: \(fileName), line: \(line)" } else { message = "\(text())" @@ -291,10 +286,10 @@ public class PerseusLogger { // PID and TID. let withOwnerId = (format == .full) ? true : owner && (format != .textonly) - let idtuple = getPIDandTID() + let idTuple = getPIDandTID() if withOwnerId { - message = "[\(idtuple.pid):\(idtuple.tid)] \(message)" + message = "[\(idTuple.pid):\(idTuple.tid)] \(message)" } // Time. @@ -314,12 +309,43 @@ public class PerseusLogger { // Print. if oput == .custom { - customActionOnMessage?(message, type, localTime, idtuple) + let directives: Directives = (fileName: fileName, line: line) + customActionOnMessage?(message, type, localTime, idTuple, user, directives) } else { print(message, type, oput) } } + public static func loadConfig(_ profile: ProfileCPL) -> Bool { + if let data = profile.json.data(using: .utf8) { + if let jsonConfig = decodeJsonProfile(data) { + reloadOptions(jsonConfig) + return true + } + log.message("Failed to decode CPL json config data!", .error) + return false + } + log.message("Failed to load CPL config data!", .error) + return false + } + + public static func loadConfig(_ json: URL) -> Bool { + if FileManager.default.fileExists(atPath: json.relativePath) { + if let data = try? Data(contentsOf: json) { + if let jsonConfig = decodeJsonProfile(data) { + reloadOptions(jsonConfig) + return true + } + log.message("Failed to decode CPL json config data!", .error) + return false + } + log.message("Failed to load CPL config data!", .error) + return false + } + log.message("CPL config file doesn't exist!", .error) + return false + } + // MARK: - Implementation // swiftlint:disable:next cyclomatic_complexity @@ -388,14 +414,19 @@ public class PerseusLogger { private static func getLocalTime() -> LocalTime { - guard let timezone = TimeZone(secondsFromGMT: 0) else { return ("TIME", "TIME") } + guard + let timezone = TimeZone(secondsFromGMT: 0) + else { + return ("TIME", "TIME", 0.0) + } var calendar = Calendar.current calendar.timeZone = timezone calendar.locale = Locale(identifier: "en_US_POSIX") - let current = Date(timeIntervalSince1970: (Date().timeIntervalSince1970 + + let UTC = Date().timeIntervalSince1970 + let current = Date(timeIntervalSince1970: (UTC + Double(TimeZone.current.secondsFromGMT()))) let details: Set = @@ -410,7 +441,10 @@ public class PerseusLogger { guard let year = components.year, let month = components.month?.toPrint, - let day = components.day?.toPrint else { return ("TIME", "TIME") } + let day = components.day?.toPrint + else { + return ("TIME", "TIME", 0.0) + } let date = "\(year)-\(month)-\(day)" @@ -420,11 +454,14 @@ public class PerseusLogger { let hour = components.hour?.toPrint, // Always in 24-hour. let minute = components.minute?.toPrint, let second = components.second?.toPrint, - let subsecond = components.nanosecond?.multiply else { return ("TIME", "TIME") } + let subsecond = components.nanosecond?.multiply + else { + return ("TIME", "TIME", 0.0) + } let time = "\(hour):\(minute):\(second):\(subsecond)" - return (date: date, time: time) + return (date: date, time: time, timeUTC: UTC) } private static func getPIDandTID() -> PIDandTID { @@ -445,7 +482,7 @@ public class PerseusLogger { private static func reloadOptions(_ newValue: JsonOptionsCPL) { logObject = (newValue.subsystem, newValue.category) - // turned = newValue.turned // Ignored, only manually! + // turned = newValue.turned // Only manually! level = newValue.level output = newValue.output subsecond = newValue.subsecond @@ -477,11 +514,15 @@ private extension Int { private extension UInt64 { var hex: String { - return "0x\(String(format: "%02x", self))" + + let value = self + let valueFormated = String(format: "%02x", value) + + return valueFormated } } -// MARK: - Configuration Profiles +// MARK: - JSON Profiles private struct JsonOptionsCPL: CustomStringConvertible, Decodable { let subsystem: String @@ -594,3 +635,143 @@ private let defaultDebugProfile = "debugIsInfo" : true } """ + +// MARK: - Log Report + +extension PerseusLogger { + + public class Report: NSObject { + + // MARK: - Internals + + private var delegate: PerseusDelegatedMessage? // Delegate for end-user messages. + private var report = "" // Last messages. + + // MARK: - Properties + + @objc public dynamic var lastMessage: String = "" { + didSet { + resizeReportIfNeeded() + appendLastMessageToReport() + } + } + + public var messageDelegate: AnyObject? { + didSet { + delegate = messageDelegate as? PerseusDelegatedMessage + } + } + + public var text: String { report } + + // MARK: - Constants + + public let limit: Int + public let newLine: String + +#if os(iOS) + public static let limitDefault = 1000 +#elseif os(macOS) + public static let limitDefault = 3000 +#endif + + // MARK: - Initializer + + public init(limited: Int = Report.limitDefault, _ newLine: String = "\r\n--\r\n") { + self.limit = limited + self.newLine = newLine + } + + // MARK: - Contract + + // swiftlint:disable:next function_parameter_count + public func report(_ text: String, + _ type: Level, + _ localTime: LocalTime, + _ owner: PIDandTID, + _ user: User, + _ dirs: Directives) { + + let text = text.replacingOccurrences(of: "\(type.tag) ", with: "") + lastMessage = "[\(localTime.date)] [\(localTime.time)] \(type.tag)\r\n\(text)" + + if user == .enduser { + delegate?.message = text + } + } + + public func clear() { + report = "" + } + + // MARK: - Realization + + private func resizeReportIfNeeded() { + + let lmCount = lastMessage.count + let nlCount = newLine.count + + // Can the last message be reported? + guard lmCount != 0, lmCount < limit else { + return + } + + // Should the report be resized? + let length = lmCount + report.count + (report.isEmpty ? 0 : nlCount) + guard limit - length < 0 else { + return + } + + // What length to remove? + let messages = report.components(separatedBy: newLine) + let messagesCount = messages.count - 1 + + var lengthToRemove = 0 + var itemCount = 0 + + for item in messages { + + itemCount += 1 + let newLineLength = messagesCount == 0 ? 0 : nlCount + + lengthToRemove += (item.count + newLineLength) + + if itemCount == messagesCount, messagesCount > 2 { + lengthToRemove -= nlCount // There's no new line in the report end + } + + // Is it enough? + let reportAfter = report.count - lengthToRemove + let lastMessageAfter = reportAfter != 0 ? nlCount + lmCount : lmCount + + if limit - (reportAfter + lastMessageAfter) >= 0 { + break + } + } + + // Final check + guard report.count >= lengthToRemove else { + return + } + + // Free space + report.removeFirst(lengthToRemove) + } + + private func appendLastMessageToReport() { + + guard lastMessage.isEmpty == false, lastMessage.count < limit else { + return + } + + let newLinelength = report.isEmpty ? 0 : newLine.count + let length = (lastMessage.count + report.count + newLinelength) + + guard limit - length >= 0 else { + return + } + + report.append(report.isEmpty ? lastMessage : newLine + lastMessage) + } + } +} diff --git a/Sources/PerseusDarkMode/TheDarkness.swift b/Sources/PerseusDarkMode/TheDarkness.swift index fce37c5..32b18e2 100644 --- a/Sources/PerseusDarkMode/TheDarkness.swift +++ b/Sources/PerseusDarkMode/TheDarkness.swift @@ -5,20 +5,20 @@ // // For iOS and macOS only. Use Stars to adopt for the specifics you need. // -// DESC: THE DARKNESS YOU CAN FORCE. +// DESC: THE DARKNESS YOU CAN FORCE // // Created by Mikhail Zhigulin in 7530. // -// Copyright © 7530 - 7533 Mikhail Zhigulin of Novosibirsk -// Copyright © 7533 PerseusRealDeal +// Copyright © 7530 - 7534 Mikhail Zhigulin of Novosibirsk +// Copyright © 7533 - 7534 PerseusRealDeal // // All rights reserved. // // // MIT License // -// Copyright © 7530 - 7533 Mikhail A. Zhigulin of Novosibirsk -// Copyright © 7533 PerseusRealDeal +// Copyright © 7530 - 7534 Mikhail A. Zhigulin of Novosibirsk +// Copyright © 7533 - 7534 PerseusRealDeal // // The year starts from the creation of the world according to a Slavic calendar. // September, the 1st of Slavic year.