Skip to content

Commit 14fe4d5

Browse files
committed
Initial commit
0 parents  commit 14fe4d5

19 files changed

+722
-0
lines changed

.github/workflows/swift.yml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
name: Swift
2+
on: [push]
3+
jobs:
4+
build:
5+
name: Swift ${{ matrix.swift }} on ${{ matrix.os }}
6+
strategy:
7+
matrix:
8+
os: [macos-latest]
9+
swift: ["5.10"]
10+
runs-on: ${{ matrix.os }}
11+
steps:
12+
- uses: swift-actions/setup-swift@v2
13+
with:
14+
swift-version: ${{ matrix.swift }}
15+
- uses: actions/checkout@v4
16+
- name: Build
17+
run: swift build

.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.DS_Store
2+
/.build
3+
/Packages
4+
xcuserdata/
5+
DerivedData/
6+
.swiftpm/configuration/registries.json
7+
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
8+
.netrc

.swiftformat

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# 2 spaces
2+
--indent 2
3+
--smarttabs false
4+
# braces on same line
5+
--allman false
6+
# lf at eof
7+
--linebreaks lf
8+
# aligns arguments with indent level - makes swiftlint happier
9+
--wraparguments before-first
10+

.swiftlint.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
included:
2+
- Package.swift
3+
- Sources/plan
4+
- Tests/plan
5+
strict: true
6+
opt_in_rules:
7+
- indentation_width
8+
line_length: 100
9+
indentation_width:
10+
indentation_width: 2
11+
identifier_name:
12+
excluded: # excluded via string array
13+
- id
14+
- to
15+
trailing_comma:
16+
mandatory_comma: true

DEVELOPMENT.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Development
2+
3+
**Requirements**
4+
5+
```
6+
brew install swiftformat
7+
brew install swiftlint
8+
brew install go-task
9+
```
10+
11+
## Tasks
12+
13+
- `task build` Build project
14+
- `task run` Run example
15+
- `task test` Run tests
16+
- `task format` Format using rules in `.swiftformat`
17+
- `task lint` Lint using rules in `.swiftlint`
18+
- `task install` Install app in `$HOME/.local/bin/`
19+
- `task uninstall` Removes app from `$HOME/.local/bin/`
20+
- `task artifacts` Produces artifact in `.build/release/`
21+
- `task tag` Pushes git tag from `VERSION`
22+
- `task release` Creates GitHub release from artifacts
23+
- `task sha` Prints hashes from artifacts
24+
- `task clean` Removes build directory `.build`
25+
26+
## Release
27+
28+
1. Increase version number in `VERSION`
29+
2. `task release` to tag and push
30+
3. `task sha` to print hashes to stdout
31+
4. Make changes in [homebrew-made](https://github.com/oschrenk/homebrew-made) and push
32+
5. `brew update` to update taps
33+
6. `brew upgrade` to upgrade formula
34+
35+
## Issues
36+
37+
### `xcrun: error: unable to lookup item 'PlatformPath'`
38+
39+
```
40+
xcrun: error: unable to lookup item 'PlatformPath' from command line tools installation
41+
xcrun: error: unable to lookup item 'PlatformPath' in SDK '/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk'
42+
```
43+
44+
Try fixing the SDK path (yours appears incorrect):
45+
46+
`$ xcrun --show-sdk-path --sdk macosx`
47+
48+
You might have this result:
49+
50+
`/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk`
51+
52+
Switch the default SDK location by invoking:
53+
54+
`$ sudo xcode-select -switch /Applications/Xcode.app/Contents/Developer`
55+
56+
See also https://stackoverflow.com/a/43418980

Package.resolved

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// swift-tools-version: 5.10
2+
// The swift-tools-version declares the minimum version of Swift required to build this package.
3+
4+
import PackageDescription
5+
6+
let package = Package(
7+
name: "plan",
8+
platforms: [
9+
.macOS(.v14),
10+
],
11+
dependencies: [
12+
.package(url: "https://github.com/apple/swift-argument-parser", exact: "1.5.0"),
13+
],
14+
targets: [
15+
.executableTarget(
16+
name: "plan",
17+
dependencies: [
18+
.product(name: "ArgumentParser", package: "swift-argument-parser"),
19+
],
20+
path: "Sources"
21+
),
22+
.testTarget(
23+
name: "test",
24+
dependencies: ["plan"],
25+
path: "Tests"
26+
),
27+
]
28+
)

README.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# README
2+
3+
A macOS tool to fetch calendar events as json
4+
5+
## Features
6+
7+
- returns json by default
8+
- extracts leading emojis from event title and returns them as separate description and emoji, in addition to full title
9+
10+
## Usage
11+
12+
`plan today` Returns all events for today
13+
14+
```json
15+
[
16+
{
17+
"id": "UUID:UUID",
18+
"calendar": {
19+
"id": "UUID",
20+
"label": "Some calendar"
21+
},
22+
"label": "full title",
23+
"legend": {
24+
"description": "title without leading emoji"
25+
"emoji: "🏆",
26+
},
27+
"starts_at": "",
28+
"starts_in": "",
29+
"ends_at": "",
30+
"ends_in": ""
31+
},
32+
...
33+
]
34+
```
35+
36+
`plan next` Returns the current or next event within the next hour
37+
38+
```json
39+
[
40+
{
41+
"id": "UUID:UUID",
42+
"calendar": {
43+
"id": "UUID",
44+
"label": "Some calendar"
45+
},
46+
"label": "full title",
47+
"legend": {
48+
"description": "title without leading emoji",
49+
"emoji: "🏆",
50+
},
51+
"starts_at": "",
52+
"starts_in": -20,
53+
"ends_at": "",
54+
"ends_in": 5
55+
},
56+
...
57+
]
58+
```
59+
60+
## Installation
61+
62+
**Via Github**
63+
64+
- installs to `$HOME/.local/bin/plan` (make sure it's in `$PATH`)
65+
66+
67+
```
68+
git clone [email protected]:oschrenk/plan.swift.git
69+
cd plan.swift
70+
task install
71+
```
72+
73+
**Via homebrew**
74+
75+
```
76+
brew tap oschrenk/made [email protected]:oschrenk/homebrew-made
77+
brew install oschrenk/made/plan
78+
```
79+

Sources/Cal.swift

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import EventKit
2+
3+
struct Cal: Codable {
4+
let id: String
5+
let label: String
6+
}
7+
8+
extension EKCalendar {
9+
func asCal() -> Cal {
10+
let id = calendarIdentifier
11+
let label = title
12+
13+
return Cal(
14+
id: id,
15+
label: label
16+
)
17+
}
18+
}
19+
20+
extension [Cal] {
21+
func printAsJson() {
22+
let dateFormatter = DateFormatter()
23+
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssXXXXX"
24+
let encoder = JSONEncoder()
25+
encoder.dateEncodingStrategy = .formatted(dateFormatter)
26+
encoder.outputFormatting = .prettyPrinted
27+
do {
28+
let json = try String(
29+
decoding: encoder.encode(self),
30+
as: UTF8.self
31+
)
32+
print(json)
33+
} catch {
34+
print("fail")
35+
}
36+
}
37+
}

Sources/Cli.swift

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import ArgumentParser
2+
import Foundation
3+
4+
@main
5+
struct Cli: ParsableCommand {
6+
static var configuration = CommandConfiguration(
7+
abstract: "Control Plan",
8+
subcommands: [
9+
Today.self,
10+
Next.self,
11+
Calendars.self,
12+
]
13+
)
14+
15+
mutating func run() {}
16+
}
17+
18+
/// `plan today`
19+
///
20+
/// List today's events
21+
struct Today: ParsableCommand {
22+
static var configuration = CommandConfiguration(
23+
abstract: "List todays schedule"
24+
)
25+
26+
mutating func run() {
27+
Plan().today().printAsJson()
28+
}
29+
}
30+
31+
/// `plan calendars`
32+
///
33+
/// List available calendars
34+
struct Calendars: ParsableCommand {
35+
static var configuration = CommandConfiguration(
36+
abstract: "List available calendars"
37+
)
38+
39+
mutating func run() {
40+
Plan().calendars().printAsJson()
41+
}
42+
}
43+
44+
/// `plan next`
45+
///
46+
/// List next event(s)
47+
struct Next: ParsableCommand {
48+
static var configuration = CommandConfiguration(
49+
abstract: "List next event(s)"
50+
)
51+
52+
@Option(help: ArgumentHelp(
53+
"Fetch events within <m> minutes.",
54+
discussion: "Only fetch events that already started or start within <m> minutes.",
55+
valueName: "m"
56+
)) var within: Int = 60
57+
58+
@Flag(help: "Ignore") var ignoreAllDayEvents: Bool = false
59+
60+
@Option(help: ArgumentHelp(
61+
"Ignore titles matching the given pattern <p>",
62+
discussion: "Titles matching the given regex pattern will be ignored",
63+
valueName: "p"
64+
)) var ignorePatternTitle: String = ""
65+
66+
mutating func run() {
67+
Plan().next(
68+
within: within,
69+
ignoreAllDayEvents: ignoreAllDayEvents,
70+
ignorePatternTitle: ignorePatternTitle
71+
).printAsJson()
72+
}
73+
}

0 commit comments

Comments
 (0)