28

Introducing @now/rust

 5 years ago
source link: https://www.tuicool.com/articles/hit/Z3UrI3m
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Last year, Rust was voted Stack Overflow's most loved language for the second year in a row. That isn’t surprising at all, given its incredible performance, memory efficiency, safety, and, combined with Cargo, its commitment towards developer productivity.

Today, we're proud to announce official support for Rust through @now/rust .

Rust is widely respected for its blazing fast speed

Our mission at ZEIT is to make cloud computing accessible for all. Rust has seen rapid growth in its adoption, and we're proud to be able to support the community.

To demonstrate the @now/rust Builder, we built Rust Scraper , a web scraper that utilises Servo — a high performance browser engine from Mozilla. You pass in a URL and a CSS selector through its query parameters, and it returns scraped data. In this blog post, we walk through its creation and deployment.

6JZnamr.png!web

Pass in a URL and a CSS selector through its query parameters, and Rust Scraper returns scraped data

We start by creating a Rust data structure called ElementResponse for each individual element, and another called ResponseBody for a collection of ElementResponse s. Creating these data structures help us define the shape of our JSON response. When we respond with ResponseBody , the serializer is derived automatically, and the lambda serializes to JSON for us.
use serde_derive::Serialize;

#[derive(Debug, Serialize)]
struct ElementResponse {
  innerHTML: String,
  innerText: Vec<String>,
}

#[derive(Debug, Serialize)]
struct ResponseBody {
  selector: String,
  elements: Vec<ElementResponse>,
}

Structs defined within index.rs

With our primary data structures in place, we can now focus on our core operation — extracting selectors from the HTML. We make use of scraper to help parse and query CSS selectors.

use scraper::{Html, Selector};

fn extract_selector(html: &String, selector_str: &str) -> ResponseBody {
  let fragment = Html::parse_document(html);
  let selector = Selector::parse(selector_str).unwrap();

  let elements: Vec<_> = fragment
    .select(&selector)
    .map(|el| ElementResponse {
        innerHTML: el.inner_html(),
        innerText: el.text().map(|text| text.to_string()).collect(),
    })
    .collect();
  let res = ResponseBody {
    selector: selector_str.to_string(),
    elements,
  };
  res
}

extract_selector function within index.rs . It takes an HTML string and CSS selector string as input and returns a ResponseBody with the scraped elements.

Now that we have a mechanism to parse the elements, we need to create an HTTP request handler that parses the incoming request, passes it to extract_selector , and responds with the final data we're interested in.

For Rust projects on Now, we need to call the function handler .

use http::{self, StatusCode};
use reqwest::{header, Client};
use serde_json;
use std::collections::HashMap;
use url::Url;

fn handler(request: Request<()>) -> http::Result<Response<String>> {
  let uri_str = request.uri().to_string();
  let url = Url::parse(&uri_str).unwrap();

  // Create a hash map of query parameters
  let hash_query: HashMap<_, _> = url.query_pairs().to_owned().collect();

  match (hash_query.get("url"), hash_query.get("selector")) {
    (Some(ref url), Some(ref selector)) => {
      // Ensure URL is valid
      let url = format!("http://{}", url);
      let url = Url::parse(&url).expect("Failed to parse URL");

      // GET request the passed URL
      let client = Client::new();
      let mut res = client
        .get(url)
        .header(header::ACCEPT, "text/html")
        .send()
        .expect("Failed to send HTTP request");

      assert_eq!(res.status(), StatusCode::OK);

      // Extract HTML string from response
      let page_html = res.text().expect("Failed to get response HTML");

      // Extract elements based on HTML string
      let content = extract_selector(&page_html, selector);
      let content_str = serde_json::to_string_pretty(&content).expect("Failed to serialize to JSON");
      let response = Response::builder()
        .status(StatusCode::OK)
        .header(header::CONTENT_TYPE, "application/json")
        .body(content_str)
        .expect("Failed to render response");
      Ok(response)
    }

    _ => Response::builder()
        .status(StatusCode::BAD_REQUEST)
        .body("`selector` and `url` are required query params".to_string()),
  }
}

handler function within index.rs .

With the application-specific heavy lifting done, we can now configure the project for deployment.

First we list our dependencies within Cargo.toml :
[package]
name = "rust-scraper"
version = "0.1.0"
edition = "2018"

[dependencies]
http = "0.1"
lambda_runtime = "*"
scraper = "*"
url = "*"
reqwest = "*"
serde = "*"
serde_json = "*"
serde_derive = "*"

Cargo.toml lists all the dependencies used within index.rs .

Finally, we add in our now.json configuration :

{
  "version": 2,
  "name": "rust-scraper",
  "builds": [{
    "src": "index.rs",
    "use": "@now/rust",
  }]
}

now.json specifies the use of `@now/rust` Builder.

With Nowinstalled, we're ready to deploy:

$ now

With some additional optimization work to cache several popular Rust packages, deployments should normally be fast. Ongoing work on the Rust compiler currently makes deploying Rust projects with additional dependencies, that are not cached, take longer.

The source for this demo is publicly available on GitHub .

Now that @now/rust is officially supported, we can't wait to learn about the Rust apps you build with it! As always, please send us your thoughts, feedback, and comments around @now/rust over Twitter or on Spectrum . The @now/rust Builder is completely open source . We welcome contributions and encourage you to create your own Builders for your favourite technology - we have adetailed guide in place to help you.

For further reading on Rust and deploying with Now, the community offers the following incredible resources:

  1. The official Rust book is the most comprehensive resource for learning Rust.
  2. Rust by Example helps you learn the language by solving little Rust exercises online.
  3. Idiomatic Rust is a peer-reviewed collection of resources about writing idiomatic Rust.
  4. @now/rust docs explains when and how to use the Rust Builder to deploy to the Now platform.
We'd like to thank Nathan Rajlich for his work on @now/rust , Franco Arza and Jose Rago for their work on the demo website , and Prosper Otemuyiwa for his work on the docs.

Special note of thanks to Steve Klabnik , Chris Krycho , Jonathan Turner , Santiago Pastorino and Florian Gilcher for their guidance in helping navigate the Rust community and resources.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK