1
- import bucket . {
2
- type BucketError , type Credentials , InvalidXmlSyntaxError ,
3
- UnexpectedXmlFormatError ,
4
- }
1
+ import bucket . { type BucketError , InvalidXmlSyntaxError , UnexpectedXmlFormatError }
5
2
import gleam/dict . { type Dict }
6
3
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
11
4
import gleam/result
12
- import xmlm . { Data , ElementEnd , ElementStart }
5
+ import xmlm
13
6
14
7
pub fn error_xml_syntax ( e : xmlm . InputError ) -> Result ( a, BucketError ) {
15
8
Error ( InvalidXmlSyntaxError ( xmlm . input_error_to_string ( e ) ) )
16
9
}
17
10
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
+ )
20
19
}
21
20
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 ) {
26
22
case xmlm . signal ( input ) {
27
23
Error ( e ) -> error_xml_syntax ( e )
28
24
Ok ( # ( xmlm . Dtd ( _ ) , input ) ) -> Ok ( input )
29
25
Ok ( # ( _ , _ ) ) -> Ok ( input )
30
26
}
31
27
}
32
28
33
- pub type ElementParser ( parent ) {
29
+ pub type ElementParser ( data , output ) {
34
30
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 (
43
31
data : data,
44
32
tag : String ,
33
+ mapper : fn ( data) -> output,
45
34
children : Dict (
46
35
String ,
47
36
fn ( data, xmlm . Input ) -> Result ( # ( data, xmlm . Input ) , BucketError ) ,
48
37
) ,
49
38
)
50
39
}
51
40
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
+
52
58
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 ,
55
61
) -> 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 ) )
58
71
Error ( e ) -> Error ( e )
59
72
}
60
73
}
61
74
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) ,
64
87
) -> 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 ) }
86
89
}
87
90
88
- pub fn child (
89
- builder : ElementParserBuilder ( parent_data) ,
91
+ pub fn keep_text (
92
+ builder : ElementParser ( parent_data, output ) ,
90
93
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) {
94
96
let handler = fn ( parent_data , input ) {
95
- case parse ( input ) {
97
+ case text_element ( input ) {
96
98
Ok ( # ( child_data , input ) ) -> Ok ( # ( reduce ( parent_data , child_data ) , input ) )
97
99
Error ( error ) -> Error ( error )
98
100
}
99
101
}
100
- ElementParserBuilder (
102
+ ElementParser (
101
103
.. builder ,
102
104
children : dict . insert ( builder . children , tag , handler ) ,
103
105
)
104
106
}
105
107
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 )
108
129
}
109
130
110
131
fn parse_element (
@@ -115,35 +136,47 @@ fn parse_element(
115
136
) ,
116
137
input : xmlm . Input ,
117
138
) {
118
- case xmlm . signal ( input ) {
119
- Ok ( # ( ElementEnd , input ) ) -> Ok ( # ( data , input ) )
139
+ case signal ( input ) {
120
140
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 ) ) -> {
122
143
case dict . get ( handlers , name ) {
123
144
Ok ( handler ) ->
124
145
case handler ( data , input ) {
125
146
Ok ( # ( data , input ) ) -> parse_element ( data , handlers , input )
126
147
Error ( e ) -> Error ( e )
127
148
}
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
+ }
129
154
}
130
155
}
131
156
Ok ( # ( signal , _ ) ) -> error_xml_format ( signal )
132
157
}
133
158
}
134
159
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 ) {
138
171
parse_text_element ( "" , input )
139
172
}
140
173
141
174
fn parse_text_element (
142
175
data : String ,
143
176
input : xmlm . Input ,
144
177
) -> 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 ) )
147
180
Ok ( # ( Data ( data ) , input ) ) -> parse_text_element ( data , input )
148
181
Error ( e ) -> error_xml_syntax ( e )
149
182
Ok ( # ( signal , _ ) ) -> error_xml_format ( signal )
0 commit comments