DigitalOcean Functions Challenge를 완료하기 위한 Rust CLI 작성

전주곡



my 및 my 에서 계속하려면 동일한 작업을 수행하는 Rust CLI 작성에 대해 자세히 설명하겠습니다DigitalOcean Functions Challenge.

코드



여기에서 테마를 감지하고 있지만 지난 두 번의 글과 마찬가지로 main.rs 명령으로 생성된 cargo new sharks 파일에 모든 것을 집어넣었습니다.

먼저 JSON을 통해 웹에 액세스하는 Rust로 개발을 수행한 경우 다음과 같은 몇 가지 필수 상자가 있습니다.
  • Reqwest은 웹 요청을 간단하게 보낼 수 있는 크레이트이고 차단 클라이언트를 사용했지만 Tokio에서 make Reqwest asynchronous까지 간단하게 사용할 수 있습니다.
  • Serde/serde_json/serde_derive은 직렬화/역직렬화를 위한 확실한 선택입니다.

  • CLI의 경우 개인적으로 Structopt 을 사용하는 것을 좋아하지만 편한 대로 자유롭게 사용하십시오.

    Cargo.toml 종속성은 다음과 같습니다.

    [dependencies]
    structopt = { version = "0.3" }
    reqwest = { version = "0.11", features = ["blocking", "json"]}
    serde = { version = "1.0" }
    serde_json = { version = "1.0" }
    serde_derive = { version = "1.0" }
    


    이제 이전 두 번의 글과 비슷한 것을 원한다는 것을 알고 API URL 상수와 일부 요청 및 응답 구조체로 시작했습니다.

    // src/main.rs
    
    use std::collections::HashMap;
    use serde_derive::{Serialize,Deserialize};
    
    const API_URL: &str = "https://functionschallenge.digitalocean.com/api/sammy";
    
    // Obviously, the serialize trait needs to be here so that
    // we can turn this into JSON later. I usually use debug here
    // as well just in case I need to do a little println!
    // debugging.
    #[derive(Debug, Serialize)]
    struct Request {
        name: String,
    
        // Because of the type keyword, we needed to name this
        // field something other than type, so _type suits me
        // just fine, but we have to tell serde that we want to
        // rename this field when we go to actually serialize
        // the JSON.
        #[serde(rename(serialize = "type"))]
        _type: String,
    }
    
    // Again, and obvious deserialize trait needed with the
    // response. And again, the almost obligatory Debug trait.
    #[derive(Debug, Deserialize)]
    struct Response {
        message: String,
    
        // In this case, we need to tell serde to give us the
        // "zero" value of a HashMap<String,Vec<String>> because
        // a successful response will not have this field, and
        // will cause the app to panic because this field doesn't 
        // get used.
        #[serde(default)]
        errors: HashMap<String, Vec<String>>
    }
    


    요청/응답의 경우 이것이 우리가 해야 할 전부입니다.

    이제 CLI 구조체를 정말 빠르게 설정하겠습니다.

    // src/main.rs
    ...
    
    use structopt::StructOpt;
    
    ...
    
    // I went ahead and created a "possible values" constant here
    // so that we have the CLI filter our sammy type input for us.
    
    const TYPE_VALUES: &[&str] = &[
        "sammy",
        "punk",
        "dinosaur",
        "retro",
        "pizza",
        "robot",
        "pony",
        "bootcamp",
        "xray"
    ];
    
    ...
    
    // In classic fashion, we have to derive the structopt trait
    // as well as the obligatory debug trait. The additional
    // structopt config follows it.
    #[derive(Debug, StructOpt)]
    #[structopt(name = "sharks", version = "0.1.0")]
    struct Opt {
        #[structopt(long = "name")]
        sammy_name: String,
    
        #[structopt(long = "type", possible_values(TYPE_VAULES))]
        sammy_type: String,
    }
    


    이제 우리가 해야 할 일은 main() 함수에서 모두 함께 가져오는 것입니다.

    // src/main.rs
    
    ...
    
    fn main() {
        // Build the opt struct from the arguments provided
        let opt: Opt = Opt::from_args();
    
        // Build a new request from those arguments
        let r: Request = Request {
            // notice the clones here, I needed that values later
            // in the output, so this was just the most painless
            // way to not fight the borrow checker :D
            name: opt.sammy_name.clone(),
            _type: opt.sammy_type.clone(),
        };
    
        // Grab a new blocking client. I don't care if this
        // blocks because of what this app is and does, but
        // you may care more. Feel free to try this async.
        let c = reqwest::blocking::Client::new();
    
        // Build the JSON from our Request struct
        let resp_body = serde_json::to_string(&r).unwrap();
    
        // Send the request
        let resp: Response = c.post(API_URL)
            .header("ACCEPT", "application/json") // Set a header
            .header("CONTENT-TYPE", "application/json") // Set a header
            .body(resp_body) // Set the body
            .send() // Send it
            .expect("failed to get response") // Don't fail
            .json() // Convert response to JSON
            .expect("failed to get payload"); // Don't fail
    
        // Here, I'm just doing a little error checking to see if
        // something exists. There are obviously far nicer ways
        // to do this, but again, this was fast and accomplished
        // the goal.
        if resp.errors.len() > 0 {
            println!("ERROR: {:#?}", resp.errors);
            return
        }
    
        // Give us a success message if it worked!
        println!("Successfully created Sammy: {} of type {}", opt.sammy_name, opt.sammy_type)
    }
    


    이 모든 것이 기록되었으므로 이제 cargo run -- --name <your sammy name> --type <your sammy type>를 수행할 수 있으며 예상대로 작동합니다.

    결론



    평소와 같이 전체 파일을 보려면 다음 저장소에서 자유롭게 확인하십시오.
    https://github.com/j4ng5y/digitalocean-functions-challenge/tree/main/rust

    좋은 웹페이지 즐겨찾기