Unverified Commit 7694f0ad authored by Nugine's avatar Nugine
Browse files

codegen: ops: SelectObjectContent

parent 50abbeaa
Loading
Loading
Loading
Loading
+80 −51
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@ use crate::xml::{is_xml_output, is_xml_payload};
use crate::{default, f, headers, o};
use crate::{dto, rust, smithy};

use std::cmp::Reverse;
use std::collections::{BTreeMap, BTreeSet, HashMap};
use std::ops::Not;

@@ -115,20 +116,6 @@ fn codegen_http(ops: &Operations, rust_types: &RustTypes, g: &mut Codegen) {
    codegen_header_value(ops, rust_types, g);

    for op in ops.values() {
        if op.name == "SelectObjectContent" {
            g.ln("#[allow(dead_code)] // TODO");
            g.ln(f!("pub struct {};", op.name));
            g.lf();

            g.ln("#[allow(dead_code)] // TODO");
            g.ln(f!("impl {} {{", op.name));
            codegen_op_http_de(op, rust_types, g);
            g.ln("}");
            g.lf();

            continue; // TODO: SelectObjectContent
        }

        g.ln(f!("pub struct {};", op.name));
        g.lf();

@@ -275,7 +262,10 @@ fn codegen_op_http_ser(op: &Operation, rust_types: &RustTypes, g: &mut Codegen)
                            }
                        }
                        "SelectObjectContentEventStream" => {
                            unimplemented!()
                            assert!(field.option_type);
                            g.ln(f!("if let Some(val) = x.{} {{", field.name));
                            g.ln("http::set_event_stream_body(&mut res, val);");
                            g.ln("}");
                        }
                        _ => {
                            if field.option_type {
@@ -665,10 +655,6 @@ struct Route<'a> {
fn collect_routes<'a>(ops: &'a Operations, rust_types: &'a RustTypes) -> HashMap<String, HashMap<PathPattern, Vec<Route<'a>>>> {
    let mut ans: HashMap<String, HashMap<PathPattern, Vec<Route<'_>>>> = default();
    for op in ops.values() {
        if op.name == "SelectObjectContent" {
            continue; // TODO: SelectObjectContent
        }

        let pat = PathPattern::parse(&op.http_uri);
        let map = ans.entry(op.http_method.clone()).or_default();
        let vec = map.entry(pat).or_default();
@@ -686,14 +672,25 @@ fn collect_routes<'a>(ops: &'a Operations, rust_types: &'a RustTypes) -> HashMap
    }
    for map in ans.values_mut() {
        for vec in map.values_mut() {
            vec.sort_by_key(|r| r.op.name.as_str());
            vec.reverse();
            vec.sort_by_key(|r| {
                let has_qt = r.query_tag.is_some();
                let has_qp = r.query_patterns.is_empty().not();

                let priority = match (has_qt, has_qp) {
                    (true, true) => 1,
                    (true, false) => 2,
                    (false, true) => 3,
                    (false, false) => 4,
                };

            vec.sort_by_key(|r| r.required_headers.len());
            vec.sort_by_key(|r| r.required_query_strings.len());
            vec.sort_by_key(|r| r.query_patterns.len());
            vec.sort_by_key(|r| r.query_tag.is_some() as u8);
            vec.reverse();
                (
                    priority,
                    Reverse(r.query_patterns.len()),
                    Reverse(r.required_query_strings.len()),
                    Reverse(r.required_headers.len()),
                    r.op.name.as_str(),
                )
            });
        }
    }
    ans
@@ -780,19 +777,24 @@ fn codegen_router(ops: &Operations, rust_types: &RustTypes, g: &mut Codegen) {
            match routes[method].get(&pattern) {
                None => g.ln("Err(super::unknown_operation())"),
                Some(group) => {
                    // DEBUG:
                    // NOTE: To debug the routing order, uncomment the lines below.
                    // {
                    //     println!("{} {:?}", method, pattern);
                    //     println!();
                    //     for route in group {
                    //         println!(
                    //         "{:<50} qt={:?}, qp={:?}, qs={:?}, hs={:?}",
                    //             "{:<80} qt={:<30} qp={:<30}, qs={:?}, hs={:?}",
                    //             route.op.name,
                    //         route.query_tag,
                    //         route.query_patterns,
                    //             f!("{:?}", route.query_tag.as_deref()),
                    //             f!("{:?}", route.query_patterns),
                    //             route.required_query_strings,
                    //             route.required_headers
                    //         );
                    //         println!("{}", route.op.http_uri);
                    //         println!();
                    //     }
                    //     println!("\n\n\n");
                    // }

                    assert!(group.is_empty().not());
                    if group.len() == 1 {
@@ -815,26 +817,53 @@ fn codegen_router(ops: &Operations, rust_types: &RustTypes, g: &mut Codegen) {

                        g.ln("if let Some(qs) = qs {");
                        for route in group {
                            if let Some(ref tag) = route.query_tag {
                            let has_qt = route.query_tag.is_some();
                            let has_qp = route.query_patterns.is_empty().not();

                            let qp = route.query_patterns.as_slice();

                            if has_qt {
                                let tag = route.query_tag.as_deref().unwrap();
                                assert!(tag.as_bytes().iter().all(|&x| x == b'-' || x.is_ascii_alphabetic()), "{tag}");
                                g.ln(f!("if qs.has(\"{tag}\") {{"));
                            }
                            if has_qp {
                                assert!(qp.len() <= 1);
                            }

                            match (has_qt, has_qp) {
                                (true, true) => {
                                    assert_eq!(route.op.name, "SelectObjectContent");

                                    let tag = route.query_tag.as_deref().unwrap();
                                    let (n, v) = qp.first().unwrap();

                                    g.ln(f!("if qs.has(\"{tag}\") && super::check_query_pattern(qs, \"{n}\",\"{v}\") {{"));
                                    succ(route, g, true);
                                    g.ln("}");
                                }
                                (true, false) => {
                                    let tag = route.query_tag.as_deref().unwrap();

                                    g.ln(f!("if qs.has(\"{tag}\") {{"));
                                    succ(route, g, true);
                                    g.ln("}");
                                }
                        for route in group {
                            let qp = route.query_patterns.as_slice();
                            assert!(qp.len() <= 1);
                            if let Some((ref n, ref v)) = qp.first() {
                                (false, true) => {
                                    let (n, v) = qp.first().unwrap();
                                    g.ln(f!("if super::check_query_pattern(qs, \"{n}\",\"{v}\") {{"));
                                    succ(route, g, true);
                                    g.ln("}");
                                }
                                (false, false) => {}
                            }
                        }
                        g.ln("}");

                        for route in group {
                            if route.query_tag.is_some() || route.query_patterns.is_empty().not() {
                            let has_qt = route.query_tag.is_some();
                            let has_qp = route.query_patterns.is_empty().not();

                            if has_qt || has_qp {
                                continue;
                            }

+5 −0
Original line number Diff line number Diff line
use super::Body;
use super::Response;

use crate::dto::SelectObjectContentEventStream;
use crate::dto::{Metadata, StreamingBlob, Timestamp, TimestampFormat};
use crate::error::{S3Error, S3Result};
use crate::http::{HeaderName, HeaderValue};
@@ -104,6 +105,10 @@ pub fn set_stream_body(res: &mut Response, stream: StreamingBlob) {
    *res.body_mut() = Body::from(stream);
}

pub fn set_event_stream_body(res: &mut Response, stream: SelectObjectContentEventStream) {
    *res.body_mut() = Body::from(stream.into_byte_stream());
}

pub fn add_opt_metadata(res: &mut Response, metadata: Option<Metadata>) -> S3Result {
    if let Some(map) = metadata {
        let mut buf = String::new();
+28 −2
Original line number Diff line number Diff line
@@ -4937,10 +4937,8 @@ impl super::Operation for RestoreObject {
    }
}

#[allow(dead_code)] // TODO
pub struct SelectObjectContent;

#[allow(dead_code)] // TODO
impl SelectObjectContent {
    pub fn deserialize_http(req: &mut http::Request) -> S3Result<SelectObjectContentInput> {
        let (bucket, key) = http::unwrap_object(req);
@@ -4967,6 +4965,31 @@ impl SelectObjectContent {
            request,
        })
    }

    pub fn serialize_http(x: SelectObjectContentOutput) -> S3Result<http::Response> {
        let mut res = http::Response::default();
        if let Some(val) = x.payload {
            http::set_event_stream_body(&mut res, val);
        }
        Ok(res)
    }
}

#[async_trait::async_trait]
impl super::Operation for SelectObjectContent {
    fn name(&self) -> &'static str {
        "SelectObjectContent"
    }

    async fn call(&self, s3: &dyn S3, req: &mut http::Request) -> S3Result<http::Response> {
        let input = Self::deserialize_http(req)?;
        let result = s3.select_object_content(input).await;
        let res = match result {
            Ok(output) => Self::serialize_http(output)?,
            Err(err) => super::serialize_error(err)?,
        };
        Ok(res)
    }
}

pub struct UploadPart;
@@ -5478,6 +5501,9 @@ pub fn resolve_route(
            }
            S3Path::Object { .. } => {
                if let Some(qs) = qs {
                    if qs.has("select") && super::check_query_pattern(qs, "select-type", "2") {
                        return Ok((&SelectObjectContent as &'static dyn super::Operation, true));
                    }
                    if qs.has("uploads") {
                        return Ok((&CreateMultipartUpload as &'static dyn super::Operation, false));
                    }