Skip to content

Commit 7d4b019

Browse files
committed
It works!
1 parent 2c70574 commit 7d4b019

File tree

3 files changed

+127
-198
lines changed

3 files changed

+127
-198
lines changed

src/bucket/internal.gleam

+13-30
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,11 @@ import bucket.{
55
}
66
import bucket/internal/xml
77
import gleam/dict.{type Dict}
8-
import gleam/function
98
import gleam/http
109
import gleam/http/request.{type Request, Request}
1110
import gleam/http/response.{type Response}
12-
import gleam/io
13-
import gleam/list
1411
import gleam/option
15-
import gleam/result
16-
import xmlm.{Data, ElementEnd, ElementStart}
12+
import xmlm
1713

1814
pub fn error_xml_syntax(e: xmlm.InputError) -> Result(a, BucketError) {
1915
Error(InvalidXmlSyntaxError(xmlm.input_error_to_string(e)))
@@ -84,33 +80,20 @@ fn s3_error_to_bucket_error(t) {
8480
}
8581

8682
pub fn forbidden_error(response: Response(BitArray)) -> Result(a, BucketError) {
87-
use input <- result.try(xml.start_parsing(response))
88-
8983
let parsed =
9084
xml.element("Error", S3Error(response.status, "", "", "", ""))
91-
|> xml.child(
92-
"Code",
93-
fn(error, code) { S3Error(..error, code:) },
94-
xml.text_element,
95-
)
96-
|> xml.child(
97-
"Message",
98-
fn(error, message) { S3Error(..error, message:) },
99-
xml.text_element,
100-
)
101-
|> xml.child(
102-
"Resource",
103-
fn(error, resource) { S3Error(..error, resource:) },
104-
xml.text_element,
105-
)
106-
|> xml.child(
107-
"RequestId",
108-
fn(error, request_id) { S3Error(..error, request_id:) },
109-
xml.text_element,
110-
)
111-
|> xml.child("HostId", fn(error, _) { error }, xml.text_element)
112-
|> xml.map_finish(s3_error_to_bucket_error)
113-
|> xml.parse(input)
85+
|> xml.keep_text("Code", fn(error, code) { S3Error(..error, code:) })
86+
|> xml.keep_text("Message", fn(error, message) {
87+
S3Error(..error, message:)
88+
})
89+
|> xml.keep_text("Resource", fn(error, resource) {
90+
S3Error(..error, resource:)
91+
})
92+
|> xml.keep_text("RequestId", fn(error, request_id) {
93+
S3Error(..error, request_id:)
94+
})
95+
|> xml.map(s3_error_to_bucket_error)
96+
|> xml.parse(response.body)
11497

11598
case parsed {
11699
Ok(e) -> Error(e)

src/bucket/internal/xml.gleam

+102-69
Original file line numberDiff line numberDiff line change
@@ -1,110 +1,131 @@
1-
import bucket.{
2-
type BucketError, type Credentials, InvalidXmlSyntaxError,
3-
UnexpectedXmlFormatError,
4-
}
1+
import bucket.{type BucketError, InvalidXmlSyntaxError, UnexpectedXmlFormatError}
52
import gleam/dict.{type Dict}
63
import gleam/function
7-
import gleam/http
8-
import gleam/http/request.{type Request, Request}
9-
import gleam/http/response.{type Response}
10-
import gleam/option
114
import gleam/result
12-
import xmlm.{Data, ElementEnd, ElementStart}
5+
import xmlm
136

147
pub fn error_xml_syntax(e: xmlm.InputError) -> Result(a, BucketError) {
158
Error(InvalidXmlSyntaxError(xmlm.input_error_to_string(e)))
169
}
1710

18-
pub fn error_xml_format(signal: xmlm.Signal) -> Result(a, BucketError) {
19-
Error(UnexpectedXmlFormatError(xmlm.signal_to_string(signal)))
11+
fn error_xml_format(signal: Signal) -> Result(a, BucketError) {
12+
Error(
13+
UnexpectedXmlFormatError(case signal {
14+
Open(name) -> "open " <> name
15+
Close -> "close"
16+
Data(data) -> data
17+
}),
18+
)
2019
}
2120

22-
pub fn start_parsing(
23-
response: Response(BitArray),
24-
) -> Result(xmlm.Input, BucketError) {
25-
let input = xmlm.from_bit_array(response.body)
21+
fn start_parsing(input: xmlm.Input) -> Result(xmlm.Input, BucketError) {
2622
case xmlm.signal(input) {
2723
Error(e) -> error_xml_syntax(e)
2824
Ok(#(xmlm.Dtd(_), input)) -> Ok(input)
2925
Ok(#(_, _)) -> Ok(input)
3026
}
3127
}
3228

33-
pub type ElementParser(parent) {
29+
pub type ElementParser(data, output) {
3430
ElementParser(
35-
tag: String,
36-
handler: fn(parent, xmlm.Input) ->
37-
Result(#(parent, xmlm.Input), BucketError),
38-
)
39-
}
40-
41-
pub type ElementParserBuilder(data) {
42-
ElementParserBuilder(
4331
data: data,
4432
tag: String,
33+
mapper: fn(data) -> output,
4534
children: Dict(
4635
String,
4736
fn(data, xmlm.Input) -> Result(#(data, xmlm.Input), BucketError),
4837
),
4938
)
5039
}
5140

41+
type Signal {
42+
Open(name: String)
43+
Close
44+
Data(String)
45+
}
46+
47+
fn signal(input: xmlm.Input) -> Result(#(Signal, xmlm.Input), xmlm.InputError) {
48+
case xmlm.signal(input) {
49+
Ok(#(xmlm.ElementStart(xmlm.Tag(xmlm.Name(_, name), _)), input)) ->
50+
Ok(#(Open(name), input))
51+
Ok(#(xmlm.ElementEnd, input)) -> Ok(#(Close, input))
52+
Ok(#(xmlm.Data(data), input)) -> Ok(#(Data(data), input))
53+
Ok(#(xmlm.Dtd(_), input)) -> signal(input)
54+
Error(e) -> Error(e)
55+
}
56+
}
57+
5258
pub fn parse(
53-
parser: fn(xmlm.Input) -> Result(#(output, xmlm.Input), BucketError),
54-
input: xmlm.Input,
59+
parser: ElementParser(data, output),
60+
input: BitArray,
5561
) -> Result(output, BucketError) {
56-
case parser(input) {
57-
Ok(#(data, _)) -> Ok(data)
62+
let input = xmlm.from_bit_array(input)
63+
use input <- result.try(start_parsing(input))
64+
use input <- result.try(case signal(input) {
65+
Error(e) -> error_xml_syntax(e)
66+
Ok(#(Open(tag), input)) if tag == parser.tag -> Ok(input)
67+
Ok(#(signal, _)) -> error_xml_format(signal)
68+
})
69+
case finish(parser)(input) {
70+
Ok(#(data, _)) -> Ok(parser.mapper(data))
5871
Error(e) -> Error(e)
5972
}
6073
}
6174

62-
pub fn finish(
63-
builder: ElementParserBuilder(data),
75+
pub fn map(
76+
builder: ElementParser(data, output1),
77+
mapper: fn(output1) -> output2,
78+
) -> ElementParser(data, output2) {
79+
let ElementParser(data:, mapper: prevous_mapper, tag:, children:) = builder
80+
ElementParser(data:, tag:, children:, mapper: fn(data) {
81+
mapper(prevous_mapper(data))
82+
})
83+
}
84+
85+
fn finish(
86+
builder: ElementParser(data, output),
6487
) -> fn(xmlm.Input) -> Result(#(data, xmlm.Input), BucketError) {
65-
map_finish(builder, function.identity)
66-
}
67-
68-
pub fn map_finish(
69-
builder: ElementParserBuilder(data),
70-
mapper: fn(data) -> output,
71-
) -> fn(xmlm.Input) -> Result(#(output, xmlm.Input), BucketError) {
72-
fn(input) {
73-
case xmlm.signal(input) {
74-
Error(e) -> error_xml_syntax(e)
75-
Ok(#(ElementStart(xmlm.Tag(xmlm.Name(_, name), _)), input))
76-
if name == builder.tag
77-
-> {
78-
case parse_element(builder.data, builder.children, input) {
79-
Ok(#(data, input)) -> Ok(#(mapper(data), input))
80-
Error(e) -> Error(e)
81-
}
82-
}
83-
Ok(#(signal, _)) -> error_xml_format(signal)
84-
}
85-
}
88+
fn(input) { parse_element(builder.data, builder.children, input) }
8689
}
8790

88-
pub fn child(
89-
builder: ElementParserBuilder(parent_data),
91+
pub fn keep_text(
92+
builder: ElementParser(parent_data, output),
9093
tag: String,
91-
reduce: fn(parent_data, child_data) -> parent_data,
92-
parse: fn(xmlm.Input) -> Result(#(child_data, xmlm.Input), BucketError),
93-
) -> ElementParserBuilder(parent_data) {
94+
reduce: fn(parent_data, String) -> parent_data,
95+
) -> ElementParser(parent_data, output) {
9496
let handler = fn(parent_data, input) {
95-
case parse(input) {
97+
case text_element(input) {
9698
Ok(#(child_data, input)) -> Ok(#(reduce(parent_data, child_data), input))
9799
Error(error) -> Error(error)
98100
}
99101
}
100-
ElementParserBuilder(
102+
ElementParser(
101103
..builder,
102104
children: dict.insert(builder.children, tag, handler),
103105
)
104106
}
105107

106-
pub fn element(tag: String, data: data) -> ElementParserBuilder(data) {
107-
ElementParserBuilder(tag:, data:, children: dict.new())
108+
pub fn keep(
109+
builder: ElementParser(parent_data, output),
110+
child: ElementParser(child_data, child_output),
111+
reduce: fn(parent_data, child_output) -> parent_data,
112+
) -> ElementParser(parent_data, output) {
113+
let parse = finish(child)
114+
let handler = fn(parent_data, input) {
115+
case parse(input) {
116+
Ok(#(child_data, input)) ->
117+
Ok(#(reduce(parent_data, child.mapper(child_data)), input))
118+
Error(error) -> Error(error)
119+
}
120+
}
121+
ElementParser(
122+
..builder,
123+
children: dict.insert(builder.children, child.tag, handler),
124+
)
125+
}
126+
127+
pub fn element(tag: String, data: data) -> ElementParser(data, data) {
128+
ElementParser(tag:, data:, children: dict.new(), mapper: function.identity)
108129
}
109130

110131
fn parse_element(
@@ -115,35 +136,47 @@ fn parse_element(
115136
),
116137
input: xmlm.Input,
117138
) {
118-
case xmlm.signal(input) {
119-
Ok(#(ElementEnd, input)) -> Ok(#(data, input))
139+
case signal(input) {
120140
Error(e) -> error_xml_syntax(e)
121-
Ok(#(ElementStart(xmlm.Tag(xmlm.Name(_, name), _)) as signal, input)) -> {
141+
Ok(#(Close, input)) -> Ok(#(data, input))
142+
Ok(#(Open(name), input)) -> {
122143
case dict.get(handlers, name) {
123144
Ok(handler) ->
124145
case handler(data, input) {
125146
Ok(#(data, input)) -> parse_element(data, handlers, input)
126147
Error(e) -> Error(e)
127148
}
128-
Error(_) -> error_xml_format(signal)
149+
Error(_) ->
150+
case skip(input, 0) {
151+
Ok(input) -> parse_element(data, handlers, input)
152+
Error(e) -> Error(e)
153+
}
129154
}
130155
}
131156
Ok(#(signal, _)) -> error_xml_format(signal)
132157
}
133158
}
134159

135-
pub fn text_element(
136-
input: xmlm.Input,
137-
) -> Result(#(String, xmlm.Input), BucketError) {
160+
fn skip(input: xmlm.Input, depth: Int) -> Result(xmlm.Input, BucketError) {
161+
case signal(input) {
162+
Ok(#(Close, input)) if depth <= 0 -> Ok(input)
163+
Ok(#(Close, input)) -> skip(input, depth - 1)
164+
Ok(#(Open(_), input)) -> skip(input, depth + 1)
165+
Ok(#(_, input)) -> skip(input, depth)
166+
Error(e) -> error_xml_syntax(e)
167+
}
168+
}
169+
170+
fn text_element(input: xmlm.Input) -> Result(#(String, xmlm.Input), BucketError) {
138171
parse_text_element("", input)
139172
}
140173

141174
fn parse_text_element(
142175
data: String,
143176
input: xmlm.Input,
144177
) -> Result(#(String, xmlm.Input), BucketError) {
145-
case xmlm.signal(input) {
146-
Ok(#(ElementEnd, input)) -> Ok(#(data, input))
178+
case signal(input) {
179+
Ok(#(Close, input)) -> Ok(#(data, input))
147180
Ok(#(Data(data), input)) -> parse_text_element(data, input)
148181
Error(e) -> error_xml_syntax(e)
149182
Ok(#(signal, _)) -> error_xml_format(signal)

0 commit comments

Comments
 (0)