Skip to content

Commit 2e080ea

Browse files
authored
feat: add basic conversion combinators to enable greater chaining of combinators (#27)
1 parent 0c7e5be commit 2e080ea

File tree

4 files changed

+75
-9
lines changed

4 files changed

+75
-9
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
!.github/**/*
1414
!flake.nix
1515
!flake.lock
16+
!.zed/*
1617

1718
# Allow Go files
1819
!*.go

basic.go

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,10 @@ SOFTWARE.
2222

2323
package chomp
2424

25-
import "strings"
25+
import (
26+
"fmt"
27+
"strings"
28+
)
2629

2730
// Tag must match a series of characters at the beginning of the input text,
2831
// in the exact order and case provided.
@@ -182,3 +185,47 @@ func Opt[T Result](c Combinator[T]) Combinator[T] {
182185
return rem, out, nil
183186
}
184187
}
188+
189+
// S wraps the result of the inner combinator within a string slice.
190+
// Combinators of differing return types can be successfully chained
191+
// together while using this conversion combinator.
192+
//
193+
// chomp.S(chomp.Until(","))("Hello, World!")
194+
// // (", World!", []string{"Hello"}, nil)
195+
func S(c Combinator[string]) Combinator[[]string] {
196+
return func(s string) (string, []string, error) {
197+
rem, ext, err := c(s)
198+
if err != nil {
199+
return rem, nil, err
200+
}
201+
202+
return rem, []string{ext}, err
203+
}
204+
}
205+
206+
// I extracts and returns a single string from the result of the inner combinator.
207+
// Combinators of differing return types can be successfully chained together while
208+
// using this conversion combinator.
209+
//
210+
// chomp.I(chomp.SepPair(
211+
// chomp.Tag("Hello"),
212+
// chomp.Tag(", "),
213+
// chomp.Tag("World")), 1)("Hello, World!")
214+
// // ("!", "World", nil)
215+
func I(c Combinator[[]string], i int) Combinator[string] {
216+
return func(s string) (string, string, error) {
217+
rem, ext, err := c(s)
218+
if err != nil {
219+
return rem, "", err
220+
}
221+
222+
if i < 0 || i >= len(ext) {
223+
return rem, "", ParserError{
224+
Err: fmt.Errorf("index %d is out of bounds within string slice of %d elements", i, len(ext)),
225+
Type: "i",
226+
}
227+
}
228+
229+
return rem, ext[i], nil
230+
}
231+
}

basic_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727

2828
"github.com/purpleclay/chomp"
2929
"github.com/stretchr/testify/assert"
30+
"github.com/stretchr/testify/require"
3031
)
3132

3233
func TestTag(t *testing.T) {
@@ -274,3 +275,22 @@ func TestOpt(t *testing.T) {
274275
assert.Equal(t, "", ext)
275276
assert.NoError(t, err)
276277
}
278+
279+
func TestS(t *testing.T) {
280+
rem, ext, err := chomp.S(chomp.Tag("hello"))("hello and good morning")
281+
282+
assert.Equal(t, " and good morning", rem)
283+
require.Len(t, ext, 1)
284+
assert.Equal(t, "hello", ext[0])
285+
assert.NoError(t, err)
286+
}
287+
288+
func TestI(t *testing.T) {
289+
rem, ext, err := chomp.I(
290+
chomp.Repeat(chomp.All(chomp.Until(" "), chomp.Tag(" ")), 3),
291+
2)("hello and good morning")
292+
293+
assert.Equal(t, "morning", rem)
294+
assert.Equal(t, "and", ext)
295+
assert.NoError(t, err)
296+
}

examples/gpg/main.go

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func (d GpgKeyDetails) String() string {
5151
buf.WriteString(fmt.Sprintf("key_id: %s\n", d.KeyID))
5252
buf.WriteString(fmt.Sprintf("created_on: %d (%s)\n", d.CreationDate, unixToRFC3339(int64(d.CreationDate))))
5353
if d.ExpirationDate > 0 {
54-
buf.WriteString(fmt.Sprintf("expires_on: %d\n", d.ExpirationDate)) // Formatted time
54+
buf.WriteString(fmt.Sprintf("expires_on: %d (%s)\n", d.ExpirationDate, unixToRFC3339(int64(d.ExpirationDate))))
5555
}
5656

5757
return buf.String()
@@ -138,11 +138,9 @@ func key() chomp.Combinator[[]string] {
138138
var rem string
139139
var err error
140140

141-
if rem, _, err = chomp.First(chomp.Tag("sec"), chomp.Tag("ssb"))(s); err != nil {
142-
return rem, nil, err
143-
}
144-
145-
if rem, _, err = chomp.Repeat(colon(), 4)(rem); err != nil {
141+
if rem, _, err = chomp.Pair(
142+
chomp.First(chomp.Tag("sec"), chomp.Tag("ssb")),
143+
chomp.Repeat(colon(), 4))(s); err != nil {
146144
return rem, nil, err
147145
}
148146

@@ -254,11 +252,11 @@ func user() chomp.Combinator[[]string] {
254252
}
255253

256254
func main() {
257-
colonFmt := `sec:-:4096:1:AAC7E54CBD73F690:1664450926:::-:::scESC:::+:::23::0:
255+
colonFmt := `sec:-:4096:1:AAC7E54CBD73F690:1664450926:1695986926::-:::scESC:::+:::23::0:
258256
fpr:::::::::28BF65E18407FD2966565284AAC7E54CBD73F690:
259257
grp:::::::::12E86CE47CEB942D2A65B4D02106657BA8D0C92B:
260258
uid:-::::1664450926::E6F81442C4BEE48D9ED3E6EE4CAC21231D3C25EB::albert.einstein <albert.einstein@emcsqua.red>::::::::::0:
261-
ssb:-:4096:1:17441D4227A0B812:1664450926::::::e:::+:::23:
259+
ssb:-:4096:1:17441D4227A0B812:1664450926:1695986926:::::e:::+:::23:
262260
fpr:::::::::26965E00791A52ECC33AE88917441D4227A0B812:
263261
grp:::::::::603DAFFC5AAE42C4B8BFCC99DD7CEDD5C443FFA0:
264262
`

0 commit comments

Comments
 (0)