You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: CHANGELOG.md
+4-4Lines changed: 4 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -9,10 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
10
10
### Added
11
11
12
-
- Added the `project` / `apply` projection library between a calcard `VCard` and an ergonomic TOML buffer.
12
+
- Added the `project` / `apply` projection library between a calcard `ICalendar` and an ergonomic TOML buffer.
13
13
14
-
`project` emits a fillable TOML form listing the modeled vCard vocabulary; fields are uncommented and empty (an empty value is ignored, like a removed line), prefilled when present, and carry an inline `# e.g. ...` hint only where the value is not self-evident (`geo`, `tz`, `impp`, `photo`, dates); the required `FN` is flagged `# required`. Typed properties (`email`, `tel`, `address`, `url`) keep a single section with their accepted `TYPE` values listed in a trailing comment. `apply` rebuilds modeled fields from the edited buffer and carries every unmodeled property (custom `X-*`, vendor extensions, Apple `item1.*` groups) over verbatim, since the TOML is an editing affordance rather than an interchange format.
14
+
`project` emits a fillable TOML form from the first `VEVENT`, listing the modeled vocabulary; fields are uncommented and empty (an empty value is ignored, like a removed line), prefilled when present, and carry an inline `# e.g. ...` hint only where the value is not self-evident. Cryptic date-times become a friendly `2026-06-13 14:00` (with all-day, UTC and a broken-out `dtstart_tz` time-zone key), and attendees expand into `value` / `cn` / `role` / `partstat` blocks. `UID` and `DTSTAMP` are not modeled: they are app-managed (seeded for new events, preserved otherwise) and cannot be set through the buffer. `apply` rebuilds modeled fields from the edited buffer and carries every unmodeled property (`UID`, `DTSTAMP`, `SEQUENCE`, custom `X-*`) and every sibling component (`VALARM`, `VTIMEZONE`) over verbatim, since the TOML is an editing affordance rather than an interchange format.
15
15
16
-
- Added the `tcard` CLI with two verbs.
16
+
- Added the `tcal` CLI with two verbs.
17
17
18
-
`template [SOURCE]` prints the TOML scaffold (blank or prefilled). `edit [SOURCE]` runs the full "project →`$EDITOR`→ apply" round-trip and emits the resulting vCard, writing a file source back in place. `SOURCE` resolves deterministically: `-` reads stdin, an existing file is read, otherwise the value is treated as literal vCard contents, and omitting it starts from a blank template. A `-V`/`--version` flag on each verb selects the target vCard version (the root `--version` stays the app version), and new (sourceless) cards are seeded with a fresh `urn:uuid` v4 `UID`.
18
+
`template [SOURCE]` prints the TOML scaffold (blank or prefilled). `edit [SOURCE]` runs the full "project to`$EDITOR`to apply" round-trip and emits the resulting iCalendar, writing a file source back in place. `SOURCE` resolves deterministically: `-` reads stdin, an existing file is read, otherwise the value is treated as literal iCalendar contents, and omitting it starts from a blank template. New (sourceless) events are seeded with a fresh `urn:uuid` v4 `UID` and a current `DTSTAMP`.
CLI and lib to edit [vCards](https://www.rfc-editor.org/rfc/rfc6350)as ergonomic TOML: the TOML vCard, à la [jCard](https://www.rfc-editor.org/rfc/rfc7095).
3
+
CLI and lib to edit calendar events ([iCalendar](https://www.rfc-editor.org/rfc/rfc5545)`VEVENT`) as ergonomic TOML: the TOML calendar, à la [jCal](https://www.rfc-editor.org/rfc/rfc7265).
4
4
5
-
vCard is already plain text, so there is nothing to compress; what hurts is its crypticness (positional structured values like `N:Doe;John;;;`, opaque `TYPE` params) and the sheer number of properties nobody remembers. tcard projects a vCard into a commented, prefilled TOML scaffold you edit in `$EDITOR`, then folds your edits back onto the original card.
5
+
iCalendar is already plain text, so there is nothing to compress; what hurts is its crypticness (date-times like `20260613T140000`, `;TZID=` parameters, opaque `PARTSTAT`/`ROLE` codes) and the sheer number of properties nobody remembers. tcal projects the `VEVENT` of an iCalendar into a commented, prefilled TOML scaffold you edit in `$EDITOR`, then folds your edits back onto the original calendar. Date-times become a friendly `2026-06-13 14:00` with the time zone on its own line.
6
6
7
7
This repository ships two layers:
8
8
9
-
- Low-level **library** projecting between a [calcard](https://crates.io/crates/calcard)`VCard` and TOML: `project` emits the scaffold, `apply` rebuilds modeled fields from the edited buffer while carrying every unmodeled property (custom `X-*`, vendor extensions) over verbatim.
10
-
- High-level **CLI** with two verbs: `template` prints the TOML scaffold (blank or prefilled), `edit` runs the full "project →`$EDITOR`→ apply" round-trip and emits the resulting vCard.
9
+
- Low-level **library** projecting between a [calcard](https://crates.io/crates/calcard)`ICalendar` and TOML: `project` emits the scaffold from the first `VEVENT`, `apply` rebuilds modeled fields from the edited buffer while carrying every unmodeled property (the app-managed `UID` and `DTSTAMP`, custom `X-*`) and every sibling component (`VALARM`, `VTIMEZONE`) over verbatim.
10
+
- High-level **CLI** with two verbs: `template` prints the TOML scaffold (blank or prefilled), `edit` runs the full "project to`$EDITOR`to apply" round-trip and emits the resulting iCalendar.
11
11
12
12
## Table of contents
13
13
@@ -28,151 +28,161 @@ This repository ships two layers:
28
28
29
29
## Features
30
30
31
-
-**vCard ↔ TOML projection**, backed by [calcard](https://crates.io/crates/calcard) (RFC 6350 / 6868 parser and writer).
32
-
-**Discoverable form**: every modeled property is listed and empty (an empty value is ignored, like a removed line), prefilled when present, with a comment only where the value is not self-evident. Structured values (`N`, `ADR`) expand into named, ordered components instead of bare semicolons; typed properties (`email`, `tel`, ...) list their accepted `TYPE` values inline; new cards are seeded with a fresh `UID`.
33
-
-**Lossless for unknown properties**: anything tcard does not model (`X-ABLabel`, `item1.*`, vendor extensions) is preserved verbatim through an `edit`. The TOML is an editing affordance, not an interchange format, so `apply` always works against the original card.
34
-
-**Photo as a URI**: `PHOTO` accepts any local file or remote URL; no base64 blobs in your editor.
35
-
-**Two verbs, no subcommand maze**: `template` always emits TOML, `edit` always emits a vCard; `SOURCE` resolves deterministically (`-` is stdin, an existing file is read, otherwise literal vCard contents, and omitting it starts a blank template).
31
+
-**iCalendar event to TOML projection**, backed by [calcard](https://crates.io/crates/calcard) (RFC 5545 parser and writer).
32
+
-**Friendly date-times**: the cryptic `20260613T140000` becomes `2026-06-13 14:00`, all-day events are `2026-06-13`, UTC values end in ` UTC`, and the time zone (`TZID`) moves onto its own `dtstart_tz` key.
33
+
-**Discoverable form**: every modeled property is listed and empty (an empty value is ignored, like a removed line), prefilled when present, with a comment only where the value is not self-evident. Recurrence (`rrule`) and attendees (`role`, `partstat`) carry their accepted values inline; new events are seeded with a fresh `UID` and `DTSTAMP`.
34
+
-**Lossless for everything unmodeled**: properties tcal does not list (the app-managed `UID` and `DTSTAMP`, `SEQUENCE`, custom `X-*`) and sibling components (`VALARM`, `VTIMEZONE`) are preserved verbatim through an `edit`. The TOML is an editing affordance, not an interchange format, so `apply` always works against the original calendar.
35
+
-**Two verbs, no subcommand maze**: `template` always emits TOML, `edit` always emits an iCalendar; `SOURCE` resolves deterministically (`-` is stdin, an existing file is read, otherwise literal iCalendar contents, and omitting it starts a blank template).
36
36
37
37
> [!TIP]
38
-
> tcard is written in [Rust](https://www.rust-lang.org/) and uses [cargo features](https://doc.rust-lang.org/cargo/reference/features.html) to gate the CLI. The default feature set is declared in [Cargo.toml](./Cargo.toml).
38
+
> tcal is written in [Rust](https://www.rust-lang.org/) and uses [cargo features](https://doc.rust-lang.org/cargo/reference/features.html) to gate the CLI. The default feature set is declared in [Cargo.toml](./Cargo.toml).
39
39
40
40
## Installation
41
41
42
42
### Pre-built binary
43
43
44
-
The CLI binary `tcard` can be installed from the latest [GitHub release](https://github.com/pimalaya/tcard/releases) using the install script:
44
+
The CLI binary `tcal` can be installed from the latest [GitHub release](https://github.com/pimalaya/tcal/releases) using the install script:
45
45
46
46
*As root:*
47
47
48
48
```sh
49
-
curl -sSL https://raw.githubusercontent.com/pimalaya/tcard/master/install.sh | sudo sh
49
+
curl -sSL https://raw.githubusercontent.com/pimalaya/tcal/master/install.sh | sudo sh
50
50
```
51
51
52
52
*As a regular user:*
53
53
54
54
```sh
55
-
curl -sSL https://raw.githubusercontent.com/pimalaya/tcard/master/install.sh | PREFIX=~/.local sh
55
+
curl -sSL https://raw.githubusercontent.com/pimalaya/tcal/master/install.sh | PREFIX=~/.local sh
56
56
```
57
57
58
-
For a more up-to-date version, check out the [pre-releases](https://github.com/pimalaya/tcard/actions/workflows/pre-releases.yml) GitHub workflow: pick the latest run and grab the artifact matching your OS. These are built from the `master` branch.
58
+
For a more up-to-date version, check out the [pre-releases](https://github.com/pimalaya/tcal/actions/workflows/pre-releases.yml) GitHub workflow: pick the latest run and grab the artifact matching your OS. These are built from the `master` branch.
59
59
60
60
> [!NOTE]
61
61
> Pre-built binaries are built with the default cargo features. If you need a different feature set, use another installation method.
62
62
63
63
### Cargo
64
64
65
65
```sh
66
-
cargo install tcard --locked --features cli
66
+
cargo install tcal --locked --features cli
67
67
```
68
68
69
69
You can also use the git repository for a more up-to-date (but less stable) version:
To use `tcard` as a library, add it to your `Cargo.toml`:
75
+
To use `tcal` as a library, add it to your `Cargo.toml`:
76
76
77
77
```toml
78
78
[dependencies]
79
-
tcard = { version = "0.0.1", default-features = false }
79
+
tcal = { version = "0.0.1", default-features = false }
80
80
```
81
81
82
-
Dropping the default `cli` feature gives a slim library build with no clap, no editor integration: just the `project` / `apply` projection over a calcard `VCard`.
82
+
Dropping the default `cli` feature gives a slim library build with no clap, no editor integration: just the `project` / `apply` projection over a calcard `ICalendar`.
83
83
84
84
### Nix
85
85
86
86
If you have the [Flakes](https://nixos.wiki/wiki/Flakes) feature enabled:
87
87
88
88
```sh
89
-
nix profile install github:pimalaya/tcard
89
+
nix profile install github:pimalaya/tcal
90
90
```
91
91
92
92
Or run without installing:
93
93
94
94
```sh
95
-
nix run github:pimalaya/tcard -- template <contact.vcf
95
+
nix run github:pimalaya/tcal -- template <event.ics
96
96
```
97
97
98
98
### Sources
99
99
100
100
```sh
101
-
git clone https://github.com/pimalaya/tcard
102
-
cdtcard
101
+
git clone https://github.com/pimalaya/tcal
102
+
cdtcal
103
103
nix run
104
104
```
105
105
106
106
## Usage
107
107
108
108
### Library
109
109
110
-
Project a vCard to TOML, then fold edits back:
110
+
Project a calendar event to TOML, then fold edits back:
111
111
112
112
```rust,ignore
113
-
use calcard::vcard::VCardVersion;
114
-
use tcard::{template, vcard};
113
+
use tcal::{ical, template};
115
114
116
-
let card = vcard::parse(input)?;
115
+
let calendar = ical::parse(input)?;
117
116
118
-
// Emit the prefilled, documented scaffold.
119
-
let scaffold = template::project(&card, VCardVersion::V4_0);
117
+
// Emit the prefilled, documented scaffold from the first VEVENT.
118
+
let scaffold = template::project(&calendar);
120
119
121
120
// ... user edits `scaffold` in an editor ...
122
121
123
-
// Rebuild modeled fields from the buffer; unknown properties of
124
-
// `card` are preserved verbatim.
125
-
let updated = template::apply(&card, &edited, VCardVersion::V4_0)?;
122
+
// Rebuild modeled fields from the buffer; unknown properties and
123
+
// sibling components of `calendar` are preserved verbatim.
124
+
let updated = template::apply(&calendar, &edited)?;
126
125
```
127
126
128
127
### CLI
129
128
130
129
Print a blank, fully-documented template:
131
130
132
131
```sh
133
-
tcard template
132
+
tcal template
134
133
```
135
134
136
-
Project an existing vCard to TOML (path, stdin via `-`, or literal contents):
135
+
Project an existing event to TOML (path, stdin via `-`, or literal contents):
137
136
138
137
```sh
139
-
tcard template contact.vcf
140
-
tcard template - <contact.vcf
138
+
tcal template event.ics
139
+
tcal template - <event.ics
141
140
```
142
141
143
-
Edit a vCard in `$EDITOR`. With a file source, the result is written back in place; otherwise it goes to stdout (or `--output`):
142
+
Edit an event in `$EDITOR`. With a file source, the result is written back in place; otherwise it goes to stdout (or `--output`):
144
143
145
144
```sh
146
-
tcard edit contact.vcf
147
-
tcard edit - <contact.vcf> updated.vcf
148
-
tcard template |$EDITOR /dev/stdin # inspect the scaffold first
145
+
tcal edit event.ics
146
+
tcal edit - <event.ics> updated.ics
147
+
tcal template |$EDITOR /dev/stdin # inspect the scaffold first
149
148
```
150
149
151
-
Start a new card from scratch and write it out:
150
+
Start a new event from scratch and write it out:
152
151
153
152
```sh
154
-
tcard edit --output alice.vcf
155
-
tcard edit --version 3.0 --output bob.vcf
153
+
tcal edit --output meeting.ics
156
154
```
157
155
158
156
## FAQ
159
157
160
158
<details>
161
-
<summary>How does `tcard edit` pick the editor?</summary>
159
+
<summary>Which calendar component does tcal edit?</summary>
162
160
163
-
The [edit](https://crates.io/crates/edit) crate resolves `$VISUAL` first, then `$EDITOR`, then an OS default. tcard does not expose a config override: set `VISUAL` / `EDITOR` in your shell rc file.
161
+
The first `VEVENT` of the iCalendar. Other components (`VTIMEZONE`, `VALARM`, additional events) are kept verbatim but not surfaced in the scaffold. `VTODO`, `VJOURNAL` and friends are out of scope for now.
164
162
</details>
165
163
166
164
<details>
167
-
<summary>Why did my card get reformatted on the first edit?</summary>
165
+
<summary>How do I write dates and times?</summary>
168
166
169
-
tcard serializes through calcard, which normalizes line folding and parameter casing (`TYPE=work` becomes `TYPE=WORK`). The first edit of a foreign card reflows it once; output is stable afterwards. Property values and every unmodeled property are preserved verbatim, so no data is lost: only whitespace and casing change.
167
+
Use `YYYY-MM-DD HH:MM` for a timed event (`2026-06-13 14:00`), `YYYY-MM-DD` alone for an all-day event, and append ` UTC` for a UTC value. For a zoned time, set the adjacent `dtstart_tz` / `dtend_tz` key to an IANA zone like `Europe/Paris`; leave it empty for UTC or floating time. A raw iCalendar value (`20260613T140000`) is accepted too.
170
168
</details>
171
169
172
170
<details>
173
-
<summary>What happens to properties tcard does not list?</summary>
171
+
<summary>How does `tcal edit` pick the editor?</summary>
174
172
175
-
They are kept verbatim. The scaffold only surfaces the modeled vocabulary, but `apply` carries every other property (custom `X-*`, Apple `item1.*` groups, vendor extensions) straight from the original card into the result.
173
+
The [edit](https://crates.io/crates/edit) crate resolves `$VISUAL` first, then `$EDITOR`, then an OS default. tcal does not expose a config override: set `VISUAL` / `EDITOR` in your shell rc file.
174
+
</details>
175
+
176
+
<details>
177
+
<summary>Why did my calendar get reformatted on the first edit?</summary>
178
+
179
+
tcal serializes through calcard, which normalizes line folding and parameter casing. The first edit of a foreign calendar reflows it once; output is stable afterwards. Property values, every unmodeled property and every sibling component are preserved verbatim, so no data is lost: only whitespace and casing change.
180
+
</details>
181
+
182
+
<details>
183
+
<summary>What happens to properties and components tcal does not list?</summary>
184
+
185
+
They are kept verbatim. The scaffold only surfaces the modeled `VEVENT` vocabulary, but `apply` carries every other property (`DTSTAMP`, `SEQUENCE`, custom `X-*`) and every sibling component (`VALARM`, `VTIMEZONE`) straight from the original calendar into the result.
Use `--log <level>` where `<level>` is one of `off`, `error`, `warn`, `info`, `debug`, `trace`:
182
192
183
193
```sh
184
-
tcard --log trace template contact.vcf
194
+
tcal --log trace template event.ics
185
195
```
186
196
187
197
The `RUST_LOG` environment variable, when set, overrides `--log` and supports per-target filters (see the [env_logger](https://docs.rs/env_logger/latest/env_logger/#enabling-logging) documentation). `RUST_BACKTRACE=1` enables full error backtraces. Logs are written to `stderr`.
@@ -210,7 +220,7 @@ This project is developed with AI assistance. This section documents how, so use
210
220
211
221
-**Limitations**: AI models occasionally produce code that compiles and passes tests but is subtly wrong: off-by-one errors, missed edge cases, plausible but nonexistent APIs, stale RFC references. The verification workflow catches most of this; it does not catch all of it. Bug reports are welcome and taken seriously.
0 commit comments