github.com/MontFerret/ferret@v0.18.0/pkg/drivers/common/getter.go (about) 1 package common 2 3 import ( 4 "context" 5 6 "github.com/pkg/errors" 7 8 "github.com/MontFerret/ferret/pkg/drivers" 9 "github.com/MontFerret/ferret/pkg/runtime/core" 10 "github.com/MontFerret/ferret/pkg/runtime/values" 11 "github.com/MontFerret/ferret/pkg/runtime/values/types" 12 ) 13 14 func GetInPage(ctx context.Context, path []core.Value, page drivers.HTMLPage) (core.Value, core.PathError) { 15 if len(path) == 0 { 16 return page, nil 17 } 18 19 segmentIdx := 0 20 segment := path[segmentIdx] 21 22 if segment.Type() == types.String { 23 segment := segment.(values.String) 24 25 switch segment { 26 case "response": 27 resp, err := page.GetResponse(ctx) 28 29 if err != nil { 30 return nil, core.NewPathError( 31 errors.Wrap(err, "get response"), 32 0, 33 ) 34 } 35 36 out, pathErr := resp.GetIn(ctx, path[segmentIdx+1:]) 37 38 if pathErr != nil { 39 return values.None, core.NewPathErrorFrom(pathErr, segmentIdx) 40 } 41 42 return out, nil 43 case "mainFrame", "document": 44 out, pathErr := GetInDocument(ctx, path[segmentIdx+1:], page.GetMainFrame()) 45 46 if pathErr != nil { 47 return values.None, core.NewPathErrorFrom(pathErr, segmentIdx) 48 } 49 50 return out, nil 51 case "frames": 52 if len(path) == 1 { 53 out, err := page.GetFrames(ctx) 54 55 if err != nil { 56 return nil, core.NewPathError( 57 errors.Wrap(err, "get response"), 58 segmentIdx, 59 ) 60 } 61 62 return out, nil 63 } 64 65 segmentIdx = +1 66 idx := path[segmentIdx] 67 68 if !values.IsNumber(idx) { 69 return values.None, core.NewPathError( 70 core.TypeError(idx.Type(), types.Int, types.Float), 71 segmentIdx, 72 ) 73 } 74 75 value, err := page.GetFrame(ctx, values.ToInt(idx)) 76 77 if err != nil { 78 return values.None, core.NewPathError(err, segmentIdx) 79 } 80 81 if len(path) == 2 { 82 return value, nil 83 } 84 85 frame, err := drivers.ToDocument(value) 86 87 if err != nil { 88 return values.None, core.NewPathError(err, segmentIdx) 89 } 90 91 out, pathErr := GetInDocument(ctx, path[segmentIdx+1:], frame) 92 93 if err != nil { 94 return values.None, core.NewPathErrorFrom(pathErr, segmentIdx) 95 } 96 97 return out, nil 98 case "url", "URL": 99 return page.GetURL(), nil 100 case "cookies": 101 cookies, err := page.GetCookies(ctx) 102 103 if err != nil { 104 return values.None, core.NewPathError(err, segmentIdx) 105 } 106 107 if len(path) == 1 { 108 return cookies, nil 109 } 110 111 out, pathErr := cookies.GetIn(ctx, path[segmentIdx+1:]) 112 113 if err != nil { 114 return values.None, core.NewPathErrorFrom(pathErr, segmentIdx) 115 } 116 117 return out, nil 118 case "title": 119 return page.GetMainFrame().GetTitle(), nil 120 case "isClosed": 121 return page.IsClosed(), nil 122 default: 123 return GetInDocument(ctx, path, page.GetMainFrame()) 124 } 125 } 126 127 return GetInDocument(ctx, path, page.GetMainFrame()) 128 } 129 130 func GetInDocument(ctx context.Context, path []core.Value, doc drivers.HTMLDocument) (core.Value, core.PathError) { 131 if len(path) == 0 { 132 return doc, nil 133 } 134 135 var out core.Value 136 var err error 137 segmentIdx := 0 138 segment := path[segmentIdx] 139 140 if segment.Type() == types.String { 141 segment := segment.(values.String) 142 143 switch segment { 144 case "url", "URL": 145 return doc.GetURL(), nil 146 case "name": 147 return doc.GetName(), nil 148 case "title": 149 return doc.GetTitle(), nil 150 case "parent": 151 parent, err := doc.GetParentDocument(ctx) 152 153 if err != nil { 154 return values.None, core.NewPathError(err, segmentIdx) 155 } 156 157 if parent == nil { 158 return values.None, nil 159 } 160 161 if len(path) == 1 { 162 return parent, nil 163 } 164 165 out, pathErr := GetInDocument(ctx, path[segmentIdx+1:], parent) 166 167 if pathErr != nil { 168 return values.None, core.NewPathErrorFrom(pathErr, segmentIdx) 169 } 170 171 return out, nil 172 case "body", "head": 173 out, err := doc.QuerySelector(ctx, drivers.NewCSSSelector(segment)) 174 175 if err != nil { 176 return values.None, core.NewPathError(err, segmentIdx) 177 } 178 179 if out == values.None { 180 return out, nil 181 } 182 183 if len(path) == 1 { 184 return out, nil 185 } 186 187 el, err := drivers.ToElement(out) 188 189 if err != nil { 190 return values.None, core.NewPathError(err, segmentIdx) 191 } 192 193 out, pathErr := GetInElement(ctx, path[segmentIdx+1:], el) 194 195 if pathErr != nil { 196 return values.None, core.NewPathErrorFrom(pathErr, segmentIdx) 197 } 198 199 return out, nil 200 case "innerHTML": 201 out, err = doc.GetElement().GetInnerHTML(ctx) 202 case "innerText": 203 out, err = doc.GetElement().GetInnerText(ctx) 204 default: 205 return GetInNode(ctx, path, doc.GetElement()) 206 } 207 208 return values.ReturnOrNext(ctx, path, segmentIdx, out, err) 209 } 210 211 return GetInNode(ctx, path, doc.GetElement()) 212 } 213 214 func GetInElement(ctx context.Context, path []core.Value, el drivers.HTMLElement) (core.Value, core.PathError) { 215 if len(path) == 0 { 216 return el, nil 217 } 218 219 segmentIdx := 0 220 segment := path[segmentIdx] 221 222 if segment.Type() == types.String { 223 var out core.Value 224 var err error 225 226 segment := segment.(values.String) 227 228 switch segment { 229 case "innerText": 230 out, err = el.GetInnerText(ctx) 231 case "innerHTML": 232 out, err = el.GetInnerHTML(ctx) 233 case "value": 234 out, err = el.GetValue(ctx) 235 case "attributes": 236 if len(path) == 1 { 237 out, err = el.GetAttributes(ctx) 238 } else { 239 // e.g. attributes.href 240 segmentIdx++ 241 attrName := path[segmentIdx] 242 243 out, err = el.GetAttribute(ctx, values.ToString(attrName)) 244 } 245 case "style": 246 if len(path) == 1 { 247 out, err = el.GetStyles(ctx) 248 } else { 249 // e.g. style.color 250 segmentIdx++ 251 styleName := path[segmentIdx] 252 253 out, err = el.GetStyle(ctx, values.ToString(styleName)) 254 } 255 case "previousElementSibling": 256 out, err = el.GetPreviousElementSibling(ctx) 257 case "nextElementSibling": 258 out, err = el.GetNextElementSibling(ctx) 259 case "parentElement": 260 out, err = el.GetParentElement(ctx) 261 default: 262 return GetInNode(ctx, path, el) 263 } 264 265 return values.ReturnOrNext(ctx, path, segmentIdx, out, err) 266 } 267 268 return GetInNode(ctx, path, el) 269 } 270 271 func GetInNode(ctx context.Context, path []core.Value, node drivers.HTMLNode) (core.Value, core.PathError) { 272 if len(path) == 0 { 273 return node, nil 274 } 275 276 segmentIdx := 0 277 segment := path[segmentIdx] 278 279 var out core.Value 280 var err error 281 282 switch segment.Type() { 283 case types.Int: 284 out, err = node.GetChildNode(ctx, values.ToInt(segment)) 285 case types.String: 286 segment := segment.(values.String) 287 288 switch segment { 289 case "nodeType": 290 out, err = node.GetNodeType(ctx) 291 case "nodeName": 292 out, err = node.GetNodeName(ctx) 293 case "children": 294 if len(path) == 1 { 295 out, err = node.GetChildNodes(ctx) 296 } else { 297 segmentIdx++ 298 out, err = node.GetChildNode(ctx, values.ToInt(path[segmentIdx])) 299 } 300 case "length": 301 return node.Length(), nil 302 default: 303 return values.None, nil 304 } 305 default: 306 return values.None, core.NewPathError( 307 core.TypeError(segment.Type(), types.Int, types.String), 308 segmentIdx, 309 ) 310 } 311 312 return values.ReturnOrNext(ctx, path, segmentIdx, out, err) 313 }