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  }