<commit_before>use audio::ac97::AC97;
use audio::intelhda::IntelHDA;

use core::cell::UnsafeCell;

use common::debug;

use disk::ahci::Ahci;
use disk::ide::Ide;

use env::Environment;

use network::intel8254x::Intel8254x;
use network::rtl8139::Rtl8139;

use schemes::file::FileScheme;

use usb::ehci::Ehci;
use usb::ohci::Ohci;
use usb::uhci::Uhci;
use usb::xhci::Xhci;

use super::config::PciConfig;
use super::common::class::*;
use super::common::subclass::*;
use super::common::programming_interface::*;
use super::common::vendorid::*;
use super::common::deviceid::*;

/// PCI device
pub unsafe fn pci_device(env: &mut Environment,
                         mut pci: PciConfig,
                         class_id: u8,
                         subclass_id: u8,
                         interface_id: u8,
                         vendor_code: u16,
                         device_code: u16) {
    if class_id == MASS_STORAGE {
        if subclass_id == IDE {
            if let Some(module) = FileScheme::new(Ide::disks(pci)) {
                env.schemes.push(UnsafeCell::new(module));
            }
        } else if subclass_id == SATA && interface_id == AHCI {
            if let Some(module) = FileScheme::new(Ahci::disks(pci)) {
                env.schemes.push(UnsafeCell::new(module));
            }
        }
    } else if class_id == SERIAL_BUS && subclass_id == USB {
        if interface_id == XHCI {
            let base = pci.read(0x10) as usize;

            let mut module = box Xhci {
                pci: pci,
                base: base & 0xFFFFFFF0,
                memory_mapped: base & 1 == 0,
                irq: pci.read(0x3C) as u8 & 0xF,
            };
            module.init();
            env.schemes.push(UnsafeCell::new(module));
        } else if interface_id == EHCI {
            env.schemes.push(UnsafeCell::new(Ehci::new(pci)));
        } else if interface_id == OHCI {
            env.schemes.push(UnsafeCell::new(Ohci::new(pci)));
        } else if interface_id == UHCI {
            env.schemes.push(UnsafeCell::new(Uhci::new(pci)));
        } else {
            debug!("Unknown USB interface version {:X}\n", interface_id);
        }
    } else {
        match vendor_code {
            REALTEK => {
                match device_code {
                    RTL8139 => env.schemes.push(UnsafeCell::new(Rtl8139::new(pci))),
                    _ => (),
                }
            }
            INTEL => {
                match device_code {
                    GBE_82540EM => env.schemes.push(UnsafeCell::new(Intel8254x::new(pci))),
                    AC97_82801AA | AC97_ICH4 => env.schemes.push(UnsafeCell::new(AC97::new(pci))),
                    INTELHDA_ICH6 => {
                        let base = pci.read(0x10) as usize;
                        let mut module = box IntelHDA {
                            pci: pci,
                            base: base & 0xFFFFFFF0,
                            memory_mapped: base & 1 == 0,
                            irq: pci.read(0x3C) as u8 & 0xF,
                        };
                        module.init();
                        env.schemes.push(UnsafeCell::new(module));
                    }
                    _ => (),
                }
            }
            _ => (),
        }
    }
}

/// Initialize PCI session
pub unsafe fn pci_init(env: &mut Environment) {
    for bus in 0..256 {
        for slot in 0..32 {
            for func in 0..8 {
                let mut pci = PciConfig::new(bus as u8, slot as u8, func as u8);
                let id = pci.read(0);

                if (id & 0xFFFF) != 0xFFFF {
                    let class_id = pci.read(8);

                    debug!(" * PCI {}, {}, {}: ID {:X} CL {:X}",
                           bus,
                           slot,
                           func,
                           id,
                           class_id);

                    for i in 0..6 {
                        let bar = pci.read(i * 4 + 0x10);
                        if bar > 0 {
                            debug!(" BAR{}: {:X}", i, bar);

                            pci.write(i * 4 + 0x10, 0xFFFFFFFF);
                            let size = (0xFFFFFFFF - (pci.read(i * 4 + 0x10) & 0xFFFFFFF0)) + 1;
                            pci.write(i * 4 + 0x10, bar);

                            if size > 0 {
                                debug!(" {}", size);
                            }
                        }
                    }

                    debug::dl();

                    pci_device(env,
                               pci,
                               ((class_id >> 24) & 0xFF) as u8,
                               ((class_id >> 16) & 0xFF) as u8,
                               ((class_id >> 8) & 0xFF) as u8,
                               (id & 0xFFFF) as u16,
                               ((id >> 16) & 0xFFFF) as u16);
                }
            }
        }
    }
}
<commit_msg>Match PCI device by (Vendor, Device) tuple<commit_after>use audio::ac97::AC97;
use audio::intelhda::IntelHDA;

use core::cell::UnsafeCell;

use common::debug;

use disk::ahci::Ahci;
use disk::ide::Ide;

use env::Environment;

use network::intel8254x::Intel8254x;
use network::rtl8139::Rtl8139;

use schemes::file::FileScheme;

use usb::ehci::Ehci;
use usb::ohci::Ohci;
use usb::uhci::Uhci;
use usb::xhci::Xhci;

use super::config::PciConfig;
use super::common::class::*;
use super::common::subclass::*;
use super::common::programming_interface::*;
use super::common::vendorid::*;
use super::common::deviceid::*;

/// PCI device
pub unsafe fn pci_device(env: &mut Environment,
                         mut pci: PciConfig,
                         class_id: u8,
                         subclass_id: u8,
                         interface_id: u8,
                         vendor_code: u16,
                         device_code: u16) {
    if class_id == MASS_STORAGE {
        if subclass_id == IDE {
            if let Some(module) = FileScheme::new(Ide::disks(pci)) {
                env.schemes.push(UnsafeCell::new(module));
            }
        } else if subclass_id == SATA && interface_id == AHCI {
            if let Some(module) = FileScheme::new(Ahci::disks(pci)) {
                env.schemes.push(UnsafeCell::new(module));
            }
        }
    } else if class_id == SERIAL_BUS && subclass_id == USB {
        if interface_id == XHCI {
            let base = pci.read(0x10) as usize;

            let mut module = box Xhci {
                pci: pci,
                base: base & 0xFFFFFFF0,
                memory_mapped: base & 1 == 0,
                irq: pci.read(0x3C) as u8 & 0xF,
            };
            module.init();
            env.schemes.push(UnsafeCell::new(module));
        } else if interface_id == EHCI {
            env.schemes.push(UnsafeCell::new(Ehci::new(pci)));
        } else if interface_id == OHCI {
            env.schemes.push(UnsafeCell::new(Ohci::new(pci)));
        } else if interface_id == UHCI {
            env.schemes.push(UnsafeCell::new(Uhci::new(pci)));
        } else {
            debug!("Unknown USB interface version {:X}\n", interface_id);
        }
    } else {
        match (vendor_code, device_code) {
            (REALTEK, RTL8139) => env.schemes.push(UnsafeCell::new(Rtl8139::new(pci))),
            (INTEL, GBE_82540EM) => env.schemes.push(UnsafeCell::new(Intel8254x::new(pci))),
            (INTEL, AC97_82801AA) => env.schemes.push(UnsafeCell::new(AC97::new(pci))),
            (INTEL, AC97_ICH4) => env.schemes.push(UnsafeCell::new(AC97::new(pci))),
            (INTEL, INTELHDA_ICH6) => {
                let base = pci.read(0x10) as usize;
                let mut module = box IntelHDA {
                    pci: pci,
                    base: base & 0xFFFFFFF0,
                    memory_mapped: base & 1 == 0,
                    irq: pci.read(0x3C) as u8 & 0xF,
                };
                module.init();
                env.schemes.push(UnsafeCell::new(module));
            }
            _ => (),
        }
    }
}

/// Initialize PCI session
pub unsafe fn pci_init(env: &mut Environment) {
    for bus in 0..256 {
        for slot in 0..32 {
            for func in 0..8 {
                let mut pci = PciConfig::new(bus as u8, slot as u8, func as u8);
                let id = pci.read(0);

                if (id & 0xFFFF) != 0xFFFF {
                    let class_id = pci.read(8);

                    debug!(" * PCI {}, {}, {}: ID {:X} CL {:X}",
                           bus,
                           slot,
                           func,
                           id,
                           class_id);

                    for i in 0..6 {
                        let bar = pci.read(i * 4 + 0x10);
                        if bar > 0 {
                            debug!(" BAR{}: {:X}", i, bar);

                            pci.write(i * 4 + 0x10, 0xFFFFFFFF);
                            let size = (0xFFFFFFFF - (pci.read(i * 4 + 0x10) & 0xFFFFFFF0)) + 1;
                            pci.write(i * 4 + 0x10, bar);

                            if size > 0 {
                                debug!(" {}", size);
                            }
                        }
                    }

                    debug::dl();

                    pci_device(env,
                               pci,
                               ((class_id >> 24) & 0xFF) as u8,
                               ((class_id >> 16) & 0xFF) as u8,
                               ((class_id >> 8) & 0xFF) as u8,
                               (id & 0xFFFF) as u16,
                               ((id >> 16) & 0xFFFF) as u16);
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    counters,
    errors::JsonRpcError,
    methods::{build_registry, JsonRpcRequest, JsonRpcService, RpcRegistry},
    response::JsonRpcResponse,
};
use futures::future::join_all;
use libra_config::config::{NodeConfig, RoleType};
use libra_logger::{debug, error, info, Schema};
use libra_mempool::MempoolClientSender;
use libra_types::{chain_id::ChainId, ledger_info::LedgerInfoWithSignatures};
use rand::{rngs::OsRng, RngCore};
use serde_json::{map::Map, Value};
use std::{net::SocketAddr, sync::Arc};
use storage_interface::DbReader;
use tokio::runtime::{Builder, Runtime};
use warp::{
    reject::{self, Reject},
    Filter,
};

// Counter labels for runtime metrics
const LABEL_FAIL: &str = "fail";
const LABEL_SUCCESS: &str = "success";
const LABEL_BATCH: &str = "batch";
const LABEL_SINGLE: &str = "single";

#[derive(Schema)]
struct HttpRequestLog<'a> {
    #[schema(display)]
    remote_addr: Option<std::net::SocketAddr>,
    method: String,
    path: String,
    status: u16,
    referer: Option<&'a str>,
    user_agent: Option<&'a str>,
    #[schema(debug)]
    elapsed: std::time::Duration,
}

#[derive(Schema)]
struct RpcRequestLog {
    trace_id: u64,
    request: Value,
}

#[derive(Schema)]
struct RpcResponseLog<'a> {
    trace_id: u64,
    is_batch: bool,
    response_error: bool,
    response: &'a JsonRpcResponse,
}

/// Creates HTTP server (warp-based) that serves JSON RPC requests
/// Returns handle to corresponding Tokio runtime
pub fn bootstrap(
    address: SocketAddr,
    batch_size_limit: u16,
    page_size_limit: u16,
    content_len_limit: usize,
    libra_db: Arc<dyn DbReader>,
    mp_sender: MempoolClientSender,
    role: RoleType,
    chain_id: ChainId,
) -> Runtime {
    let runtime = Builder::new()
        .thread_name("json-rpc")
        .threaded_scheduler()
        .enable_all()
        .build()
        .expect("[json-rpc] failed to create runtime");

    let registry = Arc::new(build_registry());
    let service = JsonRpcService::new(
        libra_db,
        mp_sender,
        role,
        chain_id,
        batch_size_limit,
        page_size_limit,
    );

    let base_route = warp::any()
        .and(warp::post())
        .and(warp::header::exact("content-type", "application/json"))
        .and(warp::body::content_length_limit(content_len_limit as u64))
        .and(warp::body::json())
        .and(warp::any().map(move || service.clone()))
        .and(warp::any().map(move || Arc::clone(&registry)))
        .and_then(rpc_endpoint)
        .with(warp::log::custom(|info| {
            info!(HttpRequestLog {
                remote_addr: info.remote_addr(),
                method: info.method().to_string(),
                path: info.path().to_string(),
                status: info.status().as_u16(),
                referer: info.referer(),
                user_agent: info.user_agent(),
                elapsed: info.elapsed(),
            })
        }));

    // For now we still allow user to use "/", but user should start to move to "/v1" soon
    let route_root = warp::path::end().and(base_route.clone());

    let route_v1 = warp::path::path("v1")
        .and(warp::path::end())
        .and(base_route);

    let health_route = warp::path!("-" / "healthy")
        .and(warp::path::end())
        .map(|| "libra-node:ok");

    let full_route = health_route.or(route_v1.or(route_root));

    // Ensure that we actually bind to the socket first before spawning the
    // server tasks. This helps in tests to prevent races where a client attempts
    // to make a request before the server task is actually listening on the
    // socket.
    //
    // Note: we need to enter the runtime context first to actually bind, since
    //       tokio TcpListener can only be bound inside a tokio context.
    let server = runtime.enter(move || warp::serve(full_route).bind(address));
    runtime.handle().spawn(server);
    runtime
}

/// Creates JSON RPC endpoint by given node config
pub fn bootstrap_from_config(
    config: &NodeConfig,
    chain_id: ChainId,
    libra_db: Arc<dyn DbReader>,
    mp_sender: MempoolClientSender,
) -> Runtime {
    bootstrap(
        config.rpc.address,
        config.rpc.batch_size_limit,
        config.rpc.page_size_limit,
        config.rpc.content_length_limit,
        libra_db,
        mp_sender,
        config.base.role,
        chain_id,
    )
}

/// JSON RPC entry point
/// Handles all incoming rpc requests
/// Performs routing based on methods defined in `registry`
pub(crate) async fn rpc_endpoint(
    data: Value,
    service: JsonRpcService,
    registry: Arc<RpcRegistry>,
) -> Result<Box<dyn warp::Reply>, warp::Rejection> {
    let label = match data {
        Value::Array(_) => LABEL_BATCH,
        _ => LABEL_SINGLE,
    };
    let timer = counters::REQUEST_LATENCY
        .with_label_values(&[label])
        .start_timer();
    let ret = rpc_endpoint_without_metrics(data, service, registry).await;
    timer.stop_and_record();
    ret
}

async fn rpc_endpoint_without_metrics(
    data: Value,
    service: JsonRpcService,
    registry: Arc<RpcRegistry>,
) -> Result<Box<dyn warp::Reply>, warp::Rejection> {
    // take snapshot of latest version of DB to be used across all requests, especially for batched requests
    let ledger_info = service
        .get_latest_ledger_info()
        .map_err(|_| reject::custom(DatabaseError))?;

    let mut rng = OsRng;
    let trace_id = rng.next_u64();
    let request_log = RpcRequestLog {
        trace_id,
        request: data.clone(),
    };
    info!(request_log);

    let resp = Ok(if let Value::Array(requests) = data {
        match service.validate_batch_size_limit(requests.len()) {
            Ok(_) => {
                // batch API call
                let futures = requests.into_iter().map(|req| {
                    rpc_request_handler(
                        req,
                        service.clone(),
                        Arc::clone(&registry),
                        ledger_info.clone(),
                        LABEL_BATCH,
                        trace_id,
                    )
                });
                let responses = join_all(futures).await;
                for resp in &responses {
                    log_response(trace_id, &resp, true);
                }
                warp::reply::json(&responses)
            }
            Err(err) => {
                let mut resp = JsonRpcResponse::new(
                    service.chain_id(),
                    ledger_info.ledger_info().version(),
                    ledger_info.ledger_info().timestamp_usecs(),
                );
                set_response_error(&mut resp, err);
                log_response(trace_id, &resp, true);

                warp::reply::json(&resp)
            }
        }
    } else {
        // single API call
        let resp =
            rpc_request_handler(data, service, registry, ledger_info, LABEL_SINGLE, trace_id).await;
        log_response(trace_id, &resp, false);

        warp::reply::json(&resp)
    });

    Ok(Box::new(resp) as Box<dyn warp::Reply>)
}

fn log_response(trace_id: u64, resp: &JsonRpcResponse, is_batch: bool) {
    let log = RpcResponseLog {
        trace_id,
        is_batch,
        response_error: resp.error.is_some(),
        response: resp,
    };
    if is_internal_error(resp.error.clone()) {
        error!(log);
    } else {
        debug!(log);
    }
}

/// Handler of single RPC request
/// Performs validation and executes corresponding rpc handler
async fn rpc_request_handler(
    req: Value,
    service: JsonRpcService,
    registry: Arc<RpcRegistry>,
    ledger_info: LedgerInfoWithSignatures,
    request_type_label: &str,
    trace_id: u64,
) -> JsonRpcResponse {
    let request: Map<String, Value>;
    let mut response = JsonRpcResponse::new(
        service.chain_id(),
        ledger_info.ledger_info().version(),
        ledger_info.ledger_info().timestamp_usecs(),
    );

    match req {
        Value::Object(data) => {
            request = data;
        }
        _ => {
            set_response_error(&mut response, JsonRpcError::invalid_format());
            return response;
        }
    }

    // parse request id
    match parse_request_id(&request) {
        Ok(request_id) => {
            response.id = Some(request_id);
        }
        Err(err) => {
            set_response_error(&mut response, err);
            return response;
        }
    };

    // verify protocol version
    if let Err(err) = verify_protocol(&request) {
        set_response_error(&mut response, err);
        return response;
    }

    // parse parameters
    let params;
    match request.get("params") {
        Some(Value::Array(parameters)) => {
            params = parameters.to_vec();
        }
        _ => {
            set_response_error(&mut response, JsonRpcError::invalid_params(None));
            return response;
        }
    }

    let request_params = JsonRpcRequest {
        trace_id,
        ledger_info,
        params,
    };
    // get rpc handler
    match request.get("method") {
        Some(Value::String(name)) => match registry.get(name) {
            Some(handler) => {
                let timer = counters::METHOD_LATENCY
                    .with_label_values(&[request_type_label, name])
                    .start_timer();
                match handler(service, request_params).await {
                    Ok(result) => {
                        response.result = Some(result);
                        counters::REQUESTS
                            .with_label_values(&[name, LABEL_SUCCESS])
                            .inc();
                    }
                    Err(err) => {
                        // check for custom error
                        set_response_error(
                            &mut response,
                            err.downcast_ref::<JsonRpcError>()
                                .cloned()
                                .unwrap_or_else(|| JsonRpcError::internal_error(err.to_string())),
                        );
                        counters::REQUESTS
                            .with_label_values(&[name, LABEL_FAIL])
                            .inc();
                    }
                }
                timer.stop_and_record();
            }
            None => set_response_error(&mut response, JsonRpcError::method_not_found()),
        },
        _ => set_response_error(&mut response, JsonRpcError::method_not_found()),
    }

    response
}

// Sets the JSON RPC error value for a given response.
// If a counter label is supplied, also increments the invalid request counter using the label,
fn set_response_error(response: &mut JsonRpcResponse, error: JsonRpcError) {
    let err_code = error.code;
    if is_internal_error(Some(error.clone())) {
        counters::INTERNAL_ERRORS.inc();
    } else {
        let label = match err_code {
            -32600 => "invalid_request",
            -32601 => "method_not_found",
            -32602 => "invalid_params",
            -32604 => "invalid_format",
            -32700 => "parse_error",
            _ => "unexpected_code",
        };
        counters::INVALID_REQUESTS.with_label_values(&[label]).inc();
    }

    response.error = Some(error);
}

fn is_internal_error(error: Option<JsonRpcError>) -> bool {
    if let Some(e) = error {
        e.code <= -32000 && e.code >= -32099
    } else {
        false
    }
}

fn parse_request_id(request: &Map<String, Value>) -> Result<Value, JsonRpcError> {
    match request.get("id") {
        Some(req_id) => {
            if req_id.is_string() || req_id.is_number() || req_id.is_null() {
                Ok(req_id.clone())
            } else {
                Err(JsonRpcError::invalid_format())
            }
        }
        None => Ok(Value::Null),
    }
}

fn verify_protocol(request: &Map<String, Value>) -> Result<(), JsonRpcError> {
    if let Some(Value::String(protocol)) = request.get("jsonrpc") {
        if protocol == "2.0" {
            return Ok(());
        }
    }
    Err(JsonRpcError::invalid_request())
}

/// Warp rejection types
#[derive(Debug)]
struct DatabaseError;

impl Reject for DatabaseError {}
<commit_msg>address comments<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::{
    counters,
    errors::JsonRpcError,
    methods::{build_registry, JsonRpcRequest, JsonRpcService, RpcRegistry},
    response::JsonRpcResponse,
};
use futures::future::join_all;
use libra_config::config::{NodeConfig, RoleType};
use libra_logger::{info, log, Level, Schema};
use libra_mempool::MempoolClientSender;
use libra_types::{chain_id::ChainId, ledger_info::LedgerInfoWithSignatures};
use rand::{rngs::OsRng, RngCore};
use serde_json::{map::Map, Value};
use std::{net::SocketAddr, sync::Arc};
use storage_interface::DbReader;
use tokio::runtime::{Builder, Runtime};
use warp::{
    reject::{self, Reject},
    Filter,
};

// Counter labels for runtime metrics
const LABEL_FAIL: &str = "fail";
const LABEL_SUCCESS: &str = "success";
const LABEL_BATCH: &str = "batch";
const LABEL_SINGLE: &str = "single";

#[derive(Schema)]
struct HttpRequestLog<'a> {
    #[schema(display)]
    remote_addr: Option<std::net::SocketAddr>,
    method: String,
    path: String,
    status: u16,
    referer: Option<&'a str>,
    user_agent: Option<&'a str>,
    #[schema(debug)]
    elapsed: std::time::Duration,
}

#[derive(Schema)]
struct RpcRequestLog {
    trace_id: u64,
    request: Value,
}

#[derive(Schema)]
struct RpcResponseLog<'a> {
    trace_id: u64,
    is_batch: bool,
    response_error: bool,
    response: &'a JsonRpcResponse,
}

#[macro_export]
macro_rules! log_response {
    ($trace_id: expr, $resp: expr, $is_batch: expr) => {
        let mut level = Level::Debug;
        if let Some(ref error) = $resp.error {
            if is_internal_error(error) {
                level = Level::Error
            }
        }
        log!(
            level,
            RpcResponseLog {
                trace_id: $trace_id,
                is_batch: $is_batch,
                response_error: $resp.error.is_some(),
                response: $resp,
            }
        );
    };
}

/// Creates HTTP server (warp-based) that serves JSON RPC requests
/// Returns handle to corresponding Tokio runtime
pub fn bootstrap(
    address: SocketAddr,
    batch_size_limit: u16,
    page_size_limit: u16,
    content_len_limit: usize,
    libra_db: Arc<dyn DbReader>,
    mp_sender: MempoolClientSender,
    role: RoleType,
    chain_id: ChainId,
) -> Runtime {
    let runtime = Builder::new()
        .thread_name("json-rpc")
        .threaded_scheduler()
        .enable_all()
        .build()
        .expect("[json-rpc] failed to create runtime");

    let registry = Arc::new(build_registry());
    let service = JsonRpcService::new(
        libra_db,
        mp_sender,
        role,
        chain_id,
        batch_size_limit,
        page_size_limit,
    );

    let base_route = warp::any()
        .and(warp::post())
        .and(warp::header::exact("content-type", "application/json"))
        .and(warp::body::content_length_limit(content_len_limit as u64))
        .and(warp::body::json())
        .and(warp::any().map(move || service.clone()))
        .and(warp::any().map(move || Arc::clone(&registry)))
        .and_then(rpc_endpoint)
        .with(warp::log::custom(|info| {
            info!(HttpRequestLog {
                remote_addr: info.remote_addr(),
                method: info.method().to_string(),
                path: info.path().to_string(),
                status: info.status().as_u16(),
                referer: info.referer(),
                user_agent: info.user_agent(),
                elapsed: info.elapsed(),
            })
        }));

    // For now we still allow user to use "/", but user should start to move to "/v1" soon
    let route_root = warp::path::end().and(base_route.clone());

    let route_v1 = warp::path::path("v1")
        .and(warp::path::end())
        .and(base_route);

    let health_route = warp::path!("-" / "healthy")
        .and(warp::path::end())
        .map(|| "libra-node:ok");

    let full_route = health_route.or(route_v1.or(route_root));

    // Ensure that we actually bind to the socket first before spawning the
    // server tasks. This helps in tests to prevent races where a client attempts
    // to make a request before the server task is actually listening on the
    // socket.
    //
    // Note: we need to enter the runtime context first to actually bind, since
    //       tokio TcpListener can only be bound inside a tokio context.
    let server = runtime.enter(move || warp::serve(full_route).bind(address));
    runtime.handle().spawn(server);
    runtime
}

/// Creates JSON RPC endpoint by given node config
pub fn bootstrap_from_config(
    config: &NodeConfig,
    chain_id: ChainId,
    libra_db: Arc<dyn DbReader>,
    mp_sender: MempoolClientSender,
) -> Runtime {
    bootstrap(
        config.rpc.address,
        config.rpc.batch_size_limit,
        config.rpc.page_size_limit,
        config.rpc.content_length_limit,
        libra_db,
        mp_sender,
        config.base.role,
        chain_id,
    )
}

/// JSON RPC entry point
/// Handles all incoming rpc requests
/// Performs routing based on methods defined in `registry`
pub(crate) async fn rpc_endpoint(
    data: Value,
    service: JsonRpcService,
    registry: Arc<RpcRegistry>,
) -> Result<Box<dyn warp::Reply>, warp::Rejection> {
    let label = match data {
        Value::Array(_) => LABEL_BATCH,
        _ => LABEL_SINGLE,
    };
    let timer = counters::REQUEST_LATENCY
        .with_label_values(&[label])
        .start_timer();
    let ret = rpc_endpoint_without_metrics(data, service, registry).await;
    timer.stop_and_record();
    ret
}

async fn rpc_endpoint_without_metrics(
    data: Value,
    service: JsonRpcService,
    registry: Arc<RpcRegistry>,
) -> Result<Box<dyn warp::Reply>, warp::Rejection> {
    // take snapshot of latest version of DB to be used across all requests, especially for batched requests
    let ledger_info = service
        .get_latest_ledger_info()
        .map_err(|_| reject::custom(DatabaseError))?;

    let mut rng = OsRng;
    let trace_id = rng.next_u64();
    info!(RpcRequestLog {
        trace_id,
        request: data.clone(),
    });

    let resp = Ok(if let Value::Array(requests) = data {
        match service.validate_batch_size_limit(requests.len()) {
            Ok(_) => {
                // batch API call
                let futures = requests.into_iter().map(|req| {
                    rpc_request_handler(
                        req,
                        service.clone(),
                        Arc::clone(&registry),
                        ledger_info.clone(),
                        LABEL_BATCH,
                        trace_id,
                    )
                });
                let responses = join_all(futures).await;
                for resp in &responses {
                    log_response!(trace_id, &resp, true);
                }
                warp::reply::json(&responses)
            }
            Err(err) => {
                let mut resp = JsonRpcResponse::new(
                    service.chain_id(),
                    ledger_info.ledger_info().version(),
                    ledger_info.ledger_info().timestamp_usecs(),
                );
                set_response_error(&mut resp, err);
                log_response!(trace_id, &resp, true);

                warp::reply::json(&resp)
            }
        }
    } else {
        // single API call
        let resp =
            rpc_request_handler(data, service, registry, ledger_info, LABEL_SINGLE, trace_id).await;
        log_response!(trace_id, &resp, false);

        warp::reply::json(&resp)
    });

    Ok(Box::new(resp) as Box<dyn warp::Reply>)
}

/// Handler of single RPC request
/// Performs validation and executes corresponding rpc handler
async fn rpc_request_handler(
    req: Value,
    service: JsonRpcService,
    registry: Arc<RpcRegistry>,
    ledger_info: LedgerInfoWithSignatures,
    request_type_label: &str,
    trace_id: u64,
) -> JsonRpcResponse {
    let request: Map<String, Value>;
    let mut response = JsonRpcResponse::new(
        service.chain_id(),
        ledger_info.ledger_info().version(),
        ledger_info.ledger_info().timestamp_usecs(),
    );

    match req {
        Value::Object(data) => {
            request = data;
        }
        _ => {
            set_response_error(&mut response, JsonRpcError::invalid_format());
            return response;
        }
    }

    // parse request id
    match parse_request_id(&request) {
        Ok(request_id) => {
            response.id = Some(request_id);
        }
        Err(err) => {
            set_response_error(&mut response, err);
            return response;
        }
    };

    // verify protocol version
    if let Err(err) = verify_protocol(&request) {
        set_response_error(&mut response, err);
        return response;
    }

    // parse parameters
    let params;
    match request.get("params") {
        Some(Value::Array(parameters)) => {
            params = parameters.to_vec();
        }
        _ => {
            set_response_error(&mut response, JsonRpcError::invalid_params(None));
            return response;
        }
    }

    let request_params = JsonRpcRequest {
        trace_id,
        ledger_info,
        params,
    };
    // get rpc handler
    match request.get("method") {
        Some(Value::String(name)) => match registry.get(name) {
            Some(handler) => {
                let timer = counters::METHOD_LATENCY
                    .with_label_values(&[request_type_label, name])
                    .start_timer();
                match handler(service, request_params).await {
                    Ok(result) => {
                        response.result = Some(result);
                        counters::REQUESTS
                            .with_label_values(&[name, LABEL_SUCCESS])
                            .inc();
                    }
                    Err(err) => {
                        // check for custom error
                        set_response_error(
                            &mut response,
                            err.downcast_ref::<JsonRpcError>()
                                .cloned()
                                .unwrap_or_else(|| JsonRpcError::internal_error(err.to_string())),
                        );
                        counters::REQUESTS
                            .with_label_values(&[name, LABEL_FAIL])
                            .inc();
                    }
                }
                timer.stop_and_record();
            }
            None => set_response_error(&mut response, JsonRpcError::method_not_found()),
        },
        _ => set_response_error(&mut response, JsonRpcError::method_not_found()),
    }

    response
}

// Sets the JSON RPC error value for a given response.
// If a counter label is supplied, also increments the invalid request counter using the label,
fn set_response_error(response: &mut JsonRpcResponse, error: JsonRpcError) {
    let err_code = error.code;
    if is_internal_error(&error) {
        counters::INTERNAL_ERRORS.inc();
    } else {
        let label = match err_code {
            -32600 => "invalid_request",
            -32601 => "method_not_found",
            -32602 => "invalid_params",
            -32604 => "invalid_format",
            -32700 => "parse_error",
            _ => "unexpected_code",
        };
        counters::INVALID_REQUESTS.with_label_values(&[label]).inc();
    }

    response.error = Some(error);
}

fn is_internal_error(e: &JsonRpcError) -> bool {
    e.code <= -32000 && e.code >= -32099
}

fn parse_request_id(request: &Map<String, Value>) -> Result<Value, JsonRpcError> {
    match request.get("id") {
        Some(req_id) => {
            if req_id.is_string() || req_id.is_number() || req_id.is_null() {
                Ok(req_id.clone())
            } else {
                Err(JsonRpcError::invalid_format())
            }
        }
        None => Ok(Value::Null),
    }
}

fn verify_protocol(request: &Map<String, Value>) -> Result<(), JsonRpcError> {
    if let Some(Value::String(protocol)) = request.get("jsonrpc") {
        if protocol == "2.0" {
            return Ok(());
        }
    }
    Err(JsonRpcError::invalid_request())
}

/// Warp rejection types
#[derive(Debug)]
struct DatabaseError;

impl Reject for DatabaseError {}
<|endoftext|>


<commit_before>/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use fnv::FnvBuildHasher;
use indexmap::IndexMap;
use std::fmt::Result as FmtResult;

#[derive(Default, Clone)]
pub struct TopLevelStatements(IndexMap<String, TopLevelStatement, FnvBuildHasher>);
#[derive(Clone)]
pub enum TopLevelStatement {
    ImportStatement { name: String, path: String },
    VariableDefinition(String),
}

impl std::fmt::Display for TopLevelStatement {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> FmtResult {
        match self {
            TopLevelStatement::ImportStatement { name, path } => {
                write!(f, "import {} from '{}';\n", name, path)?
            }
            TopLevelStatement::VariableDefinition(text) => write!(f, "{}", text)?,
        };
        Ok(())
    }
}

impl TopLevelStatements {
    pub fn insert(&mut self, symbol: String, import_statement: TopLevelStatement) {
        self.0.insert(symbol, import_statement);
    }

    pub fn contains(&self, symbol: &str) -> bool {
        self.0.contains_key(symbol)
    }

    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }
}

impl std::fmt::Display for TopLevelStatements {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> FmtResult {
        for (_, statement) in self.0.iter() {
            write!(f, "{}", statement)?;
        }

        Ok(())
    }
}
<commit_msg>Ensure stable order of imports in generated artifacts<commit_after>/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use fnv::FnvBuildHasher;
use indexmap::IndexMap;
use std::fmt::Result as FmtResult;

#[derive(Default, Clone)]
pub struct TopLevelStatements(IndexMap<String, TopLevelStatement, FnvBuildHasher>);
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum TopLevelStatement {
    ImportStatement { name: String, path: String },
    VariableDefinition(String),
}

impl std::fmt::Display for TopLevelStatement {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> FmtResult {
        match self {
            TopLevelStatement::ImportStatement { name, path } => {
                write!(f, "import {} from '{}';\n", name, path)?
            }
            TopLevelStatement::VariableDefinition(text) => write!(f, "{}", text)?,
        };
        Ok(())
    }
}

impl TopLevelStatements {
    pub fn insert(&mut self, symbol: String, import_statement: TopLevelStatement) {
        self.0.insert(symbol, import_statement);
    }

    pub fn contains(&self, symbol: &str) -> bool {
        self.0.contains_key(symbol)
    }

    pub fn is_empty(&self) -> bool {
        self.0.is_empty()
    }
}

impl std::fmt::Display for TopLevelStatements {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> FmtResult {
        let mut statements = self.0.values().collect::<Vec<_>>();
        statements.sort();
        for statement in statements {
            write!(f, "{}", statement)?;
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>fn test_str(exe: &str, s: &str) {
    use std::process::{Command, Stdio};
    use std::io::Write;
    use std::str::from_utf8;
    let mut cmd = Command::new(exe)
        .stdin(Stdio::piped())
        .stderr(Stdio::piped())
        .stdout(Stdio::piped())
        .spawn()
        .unwrap();
    println!("started cmd");
    write!(cmd.stdin.as_mut().unwrap(), "{}", s).unwrap();
    println!("wrote to stdin");
    let res = cmd.wait_with_output().unwrap();
    println!("stdout: {}", from_utf8(&res.stdout).unwrap());
    println!("stderr: {}", from_utf8(&res.stderr).unwrap());
    println!("status: {:?}", res.status);
    assert!(res.status.success());
}

#[test]
#[cfg(feature="nightly")]
fn run() {
    test_str("target/debug/test_tuple", "42, 99, Ä");
}

#[test]
#[should_panic]
fn no_ws_replacement() {
    test_str("target/debug/test_tuple", "42,\t99,\nÄ");
}

#[test]
#[should_panic]
fn run_fail() {
    test_str("target/debug/test_tuple", "42,99, Ä");
}

#[test]
fn run_read() {
    test_str("target/debug/test_read", "the answer™: 42");
}

#[test]
fn run_read_simple() {
    test_str("target/debug/test_read_simple", "99\n");
}
<commit_msg>fix test suite for examples<commit_after>fn test_str(exe: &str, s: &str) {
    use std::process::{Command, Stdio};
    use std::io::Write;
    use std::str::from_utf8;
    let mut cmd = Command::new(exe)
        .stdin(Stdio::piped())
        .stderr(Stdio::piped())
        .stdout(Stdio::piped())
        .spawn()
        .unwrap();
    println!("started cmd");
    write!(cmd.stdin.as_mut().unwrap(), "{}", s).unwrap();
    println!("wrote to stdin");
    let res = cmd.wait_with_output().unwrap();
    println!("stdout: {}", from_utf8(&res.stdout).unwrap());
    println!("stderr: {}", from_utf8(&res.stderr).unwrap());
    println!("status: {:?}", res.status);
    assert!(res.status.success());
}

#[test]
#[cfg(feature="nightly")]
fn run() {
    test_str("target/debug/test_tuple", "42, 99, Ä");
}

#[test]
#[should_panic]
fn no_ws_replacement() {
    test_str("target/debug/test_tuple", "42,\t99,\nÄ");
}

#[test]
#[should_panic]
fn run_fail() {
    test_str("target/debug/test_tuple", "42,99, Ä");
}

#[test]
fn run_read() {
    test_str("target/debug/examples/test_read", "the answer™: 42");
}

#[test]
fn run_read_simple() {
    test_str("target/debug/examples/test_read_simple", "99\n");
}
<|endoftext|>


<commit_before>#[macro_use] extern crate log;
extern crate lapin_futures as lapin;
extern crate futures;
extern crate tokio_core;
extern crate env_logger;

use futures::future::Future;
use futures::Stream;
use tokio_core::reactor::Core;
use tokio_core::net::TcpStream;
use lapin::client::ConnectionOptions;
use lapin::channel::{BasicConsumeOptions,BasicGetOptions,BasicPublishOptions,BasicProperties,QueueDeclareOptions};

fn main() {
  env_logger::init().unwrap();
  let mut core = Core::new().unwrap();

  let handle = core.handle();
  let addr = "127.0.0.1:5672".parse().unwrap();

  core.run(
    TcpStream::connect(&addr, &handle).and_then(|stream| {
      lapin::client::Client::connect(stream, &ConnectionOptions::default())
    }).and_then(|client| {

      client.create_channel().and_then(|channel| {
        let id = channel.id;
        info!("created channel with id: {}", id);

        channel.queue_declare("hello", &QueueDeclareOptions::default()).and_then(move |_| {
          info!("channel {} declared queue {}", id, "hello");

          channel.basic_publish("hello", b"hello from tokio", &BasicPublishOptions::default(), BasicProperties::default())
        })
      }).and_then(move |_| {
        client.create_channel()
      }).and_then(|channel| {
        let id = channel.id;
        info!("created channel with id: {}", id);

        let c = channel.clone();
        channel.queue_declare("hello", &QueueDeclareOptions::default()).and_then(move |_| {
          info!("channel {} declared queue {}", id, "hello");

          channel.basic_consume("hello", "my_consumer", &BasicConsumeOptions::default())
        }).and_then(|stream| {
          info!("got consumer stream");

          stream.for_each(move |message| {
            debug!("got message: {:?}", message);
            info!("decoded message: {:?}", std::str::from_utf8(&message.data).unwrap());
            c.basic_ack(message.delivery_tag);
            Ok(())
          })
        })
      })
    })
  ).unwrap();
}
<commit_msg>futures: add properties in publish eample to test them<commit_after>#[macro_use] extern crate log;
extern crate lapin_futures as lapin;
extern crate futures;
extern crate tokio_core;
extern crate env_logger;

use futures::future::Future;
use futures::Stream;
use tokio_core::reactor::Core;
use tokio_core::net::TcpStream;
use lapin::client::ConnectionOptions;
use lapin::channel::{BasicConsumeOptions,BasicGetOptions,BasicPublishOptions,BasicProperties,QueueDeclareOptions};

fn main() {
  env_logger::init().unwrap();
  let mut core = Core::new().unwrap();

  let handle = core.handle();
  let addr = "127.0.0.1:5672".parse().unwrap();

  core.run(
    TcpStream::connect(&addr, &handle).and_then(|stream| {
      lapin::client::Client::connect(stream, &ConnectionOptions::default())
    }).and_then(|client| {

      client.create_channel().and_then(|channel| {
        let id = channel.id;
        info!("created channel with id: {}", id);

        channel.queue_declare("hello", &QueueDeclareOptions::default()).and_then(move |_| {
          info!("channel {} declared queue {}", id, "hello");

          channel.basic_publish("hello", b"hello from tokio", &BasicPublishOptions::default(), BasicProperties::default().with_user_id("guest".to_string()).with_reply_to("foobar".to_string()))
        })
      }).and_then(move |_| {
        client.create_channel()
      }).and_then(|channel| {
        let id = channel.id;
        info!("created channel with id: {}", id);

        let c = channel.clone();
        channel.queue_declare("hello", &QueueDeclareOptions::default()).and_then(move |_| {
          info!("channel {} declared queue {}", id, "hello");

          channel.basic_consume("hello", "my_consumer", &BasicConsumeOptions::default())
        }).and_then(|stream| {
          info!("got consumer stream");

          stream.for_each(move |message| {
            debug!("got message: {:?}", message);
            info!("decoded message: {:?}", std::str::from_utf8(&message.data).unwrap());
            c.basic_ack(message.delivery_tag);
            Ok(())
          })
        })
      })
    })
  ).unwrap();
}
<|endoftext|>


<commit_before>// TODO: Remove this in the future.
#![allow(dead_code)]

use super::{Expression, ParseState, ParseResult};

macro_rules! and( ( $ex:expr ) => ( {
    use base;
    base::And::new( & $ex ) } ); )

pub struct And<'a> {
  expr: &'a Expression
}

impl<'a> And<'a> {
  pub fn new( expr: &'a Expression ) -> And<'a> {
    And { expr: expr }
  }
}

impl<'a> Expression for And<'a> {
  fn apply<'a>( &self, parse_state: &ParseState<'a> ) ->
      Option< ParseResult<'a> > {
    match self.expr.apply( parse_state ) {
      Some( _ ) => Some( ParseResult::fromParseState( *parse_state ) ),
      _ => None
    }
  }
}

#[cfg(test)]
mod tests {
  use base::{ParseResult, Expression};

  #[test]
  fn And_Match_WithLiteral() {
    let orig_state = input_state!( "foo" );
    match and!( lit!( "foo" ) ).apply( &orig_state ) {
      Some( ParseResult{ nodes: nodes,
                         parse_state: parse_state } ) => {
        assert!( nodes.is_empty() );
        assert_eq!( parse_state, orig_state );
      }
      _ => fail!( "No match." )
    }
  }

  #[test]
  fn And_Match_WithCharClass() {
    let orig_state = input_state!( "c" );
    match and!( class!( "a-z" ) ).apply( &orig_state ) {
      Some( ParseResult{ nodes: nodes,
                         parse_state: parse_state } ) => {
        assert!( nodes.is_empty() );
        assert_eq!( parse_state, orig_state );
      }
      _ => fail!( "No match." )
    }
  }

  #[test]
  fn And_NoMatch() {
    assert!( and!( class!( "a-z" ) ).apply( &input_state!( "0" ) ).is_none() )
    assert!( and!( lit!( "x" ) ).apply( &input_state!( "y" ) ).is_none() )
  }
}

<commit_msg>Removed some dead code<commit_after>use super::{Expression, ParseState, ParseResult};

macro_rules! and( ( $ex:expr ) => ( {
    use base;
    base::And::new( & $ex ) } ); )

pub struct And<'a> {
  expr: &'a Expression
}

impl<'a> And<'a> {
  pub fn new( expr: &'a Expression ) -> And<'a> {
    And { expr: expr }
  }
}

impl<'a> Expression for And<'a> {
  fn apply<'a>( &self, parse_state: &ParseState<'a> ) ->
      Option< ParseResult<'a> > {
    match self.expr.apply( parse_state ) {
      Some( _ ) => Some( ParseResult::fromParseState( *parse_state ) ),
      _ => None
    }
  }
}

#[cfg(test)]
mod tests {
  use base::{ParseResult, Expression};

  #[test]
  fn And_Match_WithLiteral() {
    let orig_state = input_state!( "foo" );
    match and!( lit!( "foo" ) ).apply( &orig_state ) {
      Some( ParseResult{ nodes: nodes,
                         parse_state: parse_state } ) => {
        assert!( nodes.is_empty() );
        assert_eq!( parse_state, orig_state );
      }
      _ => fail!( "No match." )
    }
  }

  #[test]
  fn And_Match_WithCharClass() {
    let orig_state = input_state!( "c" );
    match and!( class!( "a-z" ) ).apply( &orig_state ) {
      Some( ParseResult{ nodes: nodes,
                         parse_state: parse_state } ) => {
        assert!( nodes.is_empty() );
        assert_eq!( parse_state, orig_state );
      }
      _ => fail!( "No match." )
    }
  }

  #[test]
  fn And_NoMatch() {
    assert!( and!( class!( "a-z" ) ).apply( &input_state!( "0" ) ).is_none() )
    assert!( and!( lit!( "x" ) ).apply( &input_state!( "y" ) ).is_none() )
  }
}

<|endoftext|>


<commit_before>use gltf_derive::Validate;
use serde_derive::{Serialize, Deserialize};
use serde::{de, ser};
use std::fmt;
use crate::validation::{Checked, Error, Validate};
use crate::{accessor, extensions, scene, Extras, Index, Path, Root};

/// All valid animation interpolation algorithms.
pub const VALID_INTERPOLATIONS: &'static [&'static str] = &[
    "LINEAR",
    "STEP",
    "CATMULLROMSPLINE",
    "CUBICSPLINE",
];

/// All valid animation property names.
pub const VALID_PROPERTIES: &'static [&'static str] = &[
    "translation",
    "rotation",
    "scale",
    "weights",
];

/// Specifies an interpolation algorithm.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize)]
pub enum Interpolation {
    /// Linear interpolation.
    ///
    /// The animated values are linearly interpolated between keyframes.
    /// When targeting a rotation, spherical linear interpolation (slerp) should be
    /// used to interpolate quaternions. The number output of elements must equal
    /// the number of input elements.
    Linear = 1,

    /// Step interpolation.
    ///
    /// The animated values remain constant to the output of the first keyframe,
    /// until the next keyframe. The number of output elements must equal the number
    /// of input elements.
    Step,

    /// Uniform Catmull-Rom spline interpolation.
    ///
    /// The animation's interpolation is computed using a uniform Catmull-Rom spline.
    /// The number of output elements must equal two more than the number of input
    /// elements. The first and last output elements represent the start and end
    /// tangents of the spline. There must be at least four keyframes when using this
    /// interpolation.
    CatmullRomSpline,

    /// Cubic spline interpolation.
    ///
    /// The animation's interpolation is computed using a uniform Catmull-Rom spline.
    /// The number of output elements must equal two more than the number of input
    /// elements. The first and last output elements represent the start and end
    /// tangents of the spline. There must be at least four keyframes when using this
    /// interpolation.
    CubicSpline,
}

/// Specifies a property to animate.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize)]
pub enum Property {
    /// XYZ translation vector.
    Translation = 1,

    /// XYZW rotation quaternion.
    Rotation,

    /// XYZ scale vector.
    Scale,

    /// Weights of morph targets.
    MorphTargetWeights,
}

/// A keyframe animation.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Animation {
    /// Extension specific data.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub extensions: Option<extensions::animation::Animation>,

    /// Optional application specific data.
    #[serde(default)]
    #[cfg_attr(feature = "extras", serde(skip_serializing_if = "Option::is_none"))]
    pub extras: Extras,

    /// An array of channels, each of which targets an animation's sampler at a
    /// node's property.
    ///
    /// Different channels of the same animation must not have equal targets.
    #[serde(skip_serializing_if = "Vec::is_empty")]
    pub channels: Vec<Channel>,

    /// Optional user-defined name for this object.
    #[cfg(feature = "names")]
    #[cfg_attr(feature = "names", serde(skip_serializing_if = "Option::is_none"))]
    pub name: Option<String>,

    /// An array of samplers that combine input and output accessors with an
    /// interpolation algorithm to define a keyframe graph (but not its target).
    #[serde(skip_serializing_if = "Vec::is_empty")]
    pub samplers: Vec<Sampler>,
}

/// Targets an animation's sampler at a node's property.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Channel {
    /// The index of a sampler in this animation used to compute the value for the
    /// target.
    pub sampler: Index<Sampler>,

    /// The index of the node and TRS property to target.
    pub target: Target,

    /// Extension specific data.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub extensions: Option<extensions::animation::Channel>,

    /// Optional application specific data.
    #[serde(default)]
    #[cfg_attr(feature = "extras", serde(skip_serializing_if = "Option::is_none"))]
    pub extras: Extras,
}

/// The index of the node and TRS property that an animation channel targets.
#[derive(Clone, Debug, Deserialize, Serialize, Validate)]
pub struct Target {
    /// Extension specific data.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub extensions: Option<extensions::animation::Target>,

    /// Optional application specific data.
    #[serde(default)]
    #[cfg_attr(feature = "extras", serde(skip_serializing_if = "Option::is_none"))]
    pub extras: Extras,

    /// The index of the node to target.
    pub node: Index<scene::Node>,

    /// The name of the node's property to modify or the 'weights' of the
    /// morph targets it instantiates.
    pub path: Checked<Property>,
}

/// Defines a keyframe graph but not its target.
#[derive(Clone, Debug, Deserialize, Serialize, Validate)]
pub struct Sampler {
    /// Extension specific data.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub extensions: Option<extensions::animation::Sampler>,

    /// Optional application specific data.
    #[serde(default)]
    #[cfg_attr(feature = "extras", serde(skip_serializing_if = "Option::is_none"))]
    pub extras: Extras,

    /// The index of an accessor containing keyframe input values, e.g., time.
    pub input: Index<accessor::Accessor>,

    /// The interpolation algorithm.
    #[serde(default)]
    pub interpolation: Checked<Interpolation>,

    /// The index of an accessor containing keyframe output values.
    pub output: Index<accessor::Accessor>,
}

impl Validate for Animation {
    fn validate<P, R>(&self, root: &Root, path: P, report: &mut R)
    where
        P: Fn() -> Path,
        R: FnMut(&dyn Fn() -> Path, Error),
    {
        self.samplers.validate(root, || path().field("samplers"), report);
        for (index, channel) in self.channels.iter().enumerate() {
            if channel.sampler.value() as usize >= self.samplers.len() {
                let path = || path().field("channels").index(index).field("sampler");
                report(&path, Error::IndexOutOfBounds);
            }
        }
    }
}

impl Default for Interpolation {
    fn default() -> Self {
        Interpolation::Linear
    }
}

impl<'de> de::Deserialize<'de> for Checked<Interpolation> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where D: de::Deserializer<'de>
    {
        struct Visitor;
        impl<'de> de::Visitor<'de> for Visitor {
            type Value = Checked<Interpolation>;

            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
                write!(f, "any of: {:?}", VALID_INTERPOLATIONS)
            }

            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
                where E: de::Error
            {
                use self::Interpolation::*;
                use crate::validation::Checked::*;
                Ok(match value {
                    "LINEAR" => Valid(Linear),
                    "STEP" => Valid(Step),
                    "CATMULLROMSPLINE" => Valid(CatmullRomSpline),
                    "CUBICSPLINE" => Valid(CubicSpline),
                    _ => Invalid,
                })
            }
        }
        deserializer.deserialize_str(Visitor)
    }
}

impl ser::Serialize for Interpolation {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: ser::Serializer
    {
        serializer.serialize_str(match *self {
            Interpolation::Linear => "LINEAR",
            Interpolation::Step => "STEP",
            Interpolation::CatmullRomSpline => "CATMULLROMSPLINE",
            Interpolation::CubicSpline => "CUBICSPLINE",
        })
    }
}

impl<'de> de::Deserialize<'de> for Checked<Property> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where D: de::Deserializer<'de>
    {
        struct Visitor;
        impl<'de> de::Visitor<'de> for Visitor {
            type Value = Checked<Property>;

            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
                write!(f, "any of: {:?}", VALID_PROPERTIES)
            }

            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
                where E: de::Error
            {
                use self::Property::*;
                use crate::validation::Checked::*;
                Ok(match value {
                    "translation" => Valid(Translation),
                    "rotation" => Valid(Rotation),
                    "scale" => Valid(Scale),
                    "weights" => Valid(MorphTargetWeights),
                    _ => Invalid,
                })
            }
        }
        deserializer.deserialize_str(Visitor)
    }
}

impl ser::Serialize for Property {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: ser::Serializer
    {
        serializer.serialize_str(match *self {
            Property::Translation => "translation",
            Property::Rotation => "rotation",
            Property::Scale => "scale",
            Property::MorphTargetWeights => "weights",
        })
    }
}
<commit_msg>Fix cubic spline docs<commit_after>use gltf_derive::Validate;
use serde_derive::{Serialize, Deserialize};
use serde::{de, ser};
use std::fmt;
use crate::validation::{Checked, Error, Validate};
use crate::{accessor, extensions, scene, Extras, Index, Path, Root};

/// All valid animation interpolation algorithms.
pub const VALID_INTERPOLATIONS: &'static [&'static str] = &[
    "LINEAR",
    "STEP",
    "CATMULLROMSPLINE",
    "CUBICSPLINE",
];

/// All valid animation property names.
pub const VALID_PROPERTIES: &'static [&'static str] = &[
    "translation",
    "rotation",
    "scale",
    "weights",
];

/// Specifies an interpolation algorithm.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize)]
pub enum Interpolation {
    /// Linear interpolation.
    ///
    /// The animated values are linearly interpolated between keyframes.
    /// When targeting a rotation, spherical linear interpolation (slerp) should be
    /// used to interpolate quaternions. The number output of elements must equal
    /// the number of input elements.
    Linear = 1,

    /// Step interpolation.
    ///
    /// The animated values remain constant to the output of the first keyframe,
    /// until the next keyframe. The number of output elements must equal the number
    /// of input elements.
    Step,

    /// Uniform Catmull-Rom spline interpolation.
    ///
    /// The animation's interpolation is computed using a uniform Catmull-Rom spline.
    /// The number of output elements must equal two more than the number of input
    /// elements. The first and last output elements represent the start and end
    /// tangents of the spline. There must be at least four keyframes when using this
    /// interpolation.
    CatmullRomSpline,

    /// Cubic spline interpolation.
    ///
    /// The animation's interpolation is computed using a cubic spline with specified
    /// tangents. The number of output elements must equal three times the number of
    /// input elements. For each input element, the output stores three elements, an
    /// in-tangent, a spline vertex, and an out-tangent. There must be at least two
    /// keyframes when using this interpolation
    CubicSpline,
}

/// Specifies a property to animate.
#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize)]
pub enum Property {
    /// XYZ translation vector.
    Translation = 1,

    /// XYZW rotation quaternion.
    Rotation,

    /// XYZ scale vector.
    Scale,

    /// Weights of morph targets.
    MorphTargetWeights,
}

/// A keyframe animation.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Animation {
    /// Extension specific data.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub extensions: Option<extensions::animation::Animation>,

    /// Optional application specific data.
    #[serde(default)]
    #[cfg_attr(feature = "extras", serde(skip_serializing_if = "Option::is_none"))]
    pub extras: Extras,

    /// An array of channels, each of which targets an animation's sampler at a
    /// node's property.
    ///
    /// Different channels of the same animation must not have equal targets.
    #[serde(skip_serializing_if = "Vec::is_empty")]
    pub channels: Vec<Channel>,

    /// Optional user-defined name for this object.
    #[cfg(feature = "names")]
    #[cfg_attr(feature = "names", serde(skip_serializing_if = "Option::is_none"))]
    pub name: Option<String>,

    /// An array of samplers that combine input and output accessors with an
    /// interpolation algorithm to define a keyframe graph (but not its target).
    #[serde(skip_serializing_if = "Vec::is_empty")]
    pub samplers: Vec<Sampler>,
}

/// Targets an animation's sampler at a node's property.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Channel {
    /// The index of a sampler in this animation used to compute the value for the
    /// target.
    pub sampler: Index<Sampler>,

    /// The index of the node and TRS property to target.
    pub target: Target,

    /// Extension specific data.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub extensions: Option<extensions::animation::Channel>,

    /// Optional application specific data.
    #[serde(default)]
    #[cfg_attr(feature = "extras", serde(skip_serializing_if = "Option::is_none"))]
    pub extras: Extras,
}

/// The index of the node and TRS property that an animation channel targets.
#[derive(Clone, Debug, Deserialize, Serialize, Validate)]
pub struct Target {
    /// Extension specific data.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub extensions: Option<extensions::animation::Target>,

    /// Optional application specific data.
    #[serde(default)]
    #[cfg_attr(feature = "extras", serde(skip_serializing_if = "Option::is_none"))]
    pub extras: Extras,

    /// The index of the node to target.
    pub node: Index<scene::Node>,

    /// The name of the node's property to modify or the 'weights' of the
    /// morph targets it instantiates.
    pub path: Checked<Property>,
}

/// Defines a keyframe graph but not its target.
#[derive(Clone, Debug, Deserialize, Serialize, Validate)]
pub struct Sampler {
    /// Extension specific data.
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub extensions: Option<extensions::animation::Sampler>,

    /// Optional application specific data.
    #[serde(default)]
    #[cfg_attr(feature = "extras", serde(skip_serializing_if = "Option::is_none"))]
    pub extras: Extras,

    /// The index of an accessor containing keyframe input values, e.g., time.
    pub input: Index<accessor::Accessor>,

    /// The interpolation algorithm.
    #[serde(default)]
    pub interpolation: Checked<Interpolation>,

    /// The index of an accessor containing keyframe output values.
    pub output: Index<accessor::Accessor>,
}

impl Validate for Animation {
    fn validate<P, R>(&self, root: &Root, path: P, report: &mut R)
    where
        P: Fn() -> Path,
        R: FnMut(&dyn Fn() -> Path, Error),
    {
        self.samplers.validate(root, || path().field("samplers"), report);
        for (index, channel) in self.channels.iter().enumerate() {
            if channel.sampler.value() as usize >= self.samplers.len() {
                let path = || path().field("channels").index(index).field("sampler");
                report(&path, Error::IndexOutOfBounds);
            }
        }
    }
}

impl Default for Interpolation {
    fn default() -> Self {
        Interpolation::Linear
    }
}

impl<'de> de::Deserialize<'de> for Checked<Interpolation> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where D: de::Deserializer<'de>
    {
        struct Visitor;
        impl<'de> de::Visitor<'de> for Visitor {
            type Value = Checked<Interpolation>;

            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
                write!(f, "any of: {:?}", VALID_INTERPOLATIONS)
            }

            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
                where E: de::Error
            {
                use self::Interpolation::*;
                use crate::validation::Checked::*;
                Ok(match value {
                    "LINEAR" => Valid(Linear),
                    "STEP" => Valid(Step),
                    "CATMULLROMSPLINE" => Valid(CatmullRomSpline),
                    "CUBICSPLINE" => Valid(CubicSpline),
                    _ => Invalid,
                })
            }
        }
        deserializer.deserialize_str(Visitor)
    }
}

impl ser::Serialize for Interpolation {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: ser::Serializer
    {
        serializer.serialize_str(match *self {
            Interpolation::Linear => "LINEAR",
            Interpolation::Step => "STEP",
            Interpolation::CatmullRomSpline => "CATMULLROMSPLINE",
            Interpolation::CubicSpline => "CUBICSPLINE",
        })
    }
}

impl<'de> de::Deserialize<'de> for Checked<Property> {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where D: de::Deserializer<'de>
    {
        struct Visitor;
        impl<'de> de::Visitor<'de> for Visitor {
            type Value = Checked<Property>;

            fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
                write!(f, "any of: {:?}", VALID_PROPERTIES)
            }

            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
                where E: de::Error
            {
                use self::Property::*;
                use crate::validation::Checked::*;
                Ok(match value {
                    "translation" => Valid(Translation),
                    "rotation" => Valid(Rotation),
                    "scale" => Valid(Scale),
                    "weights" => Valid(MorphTargetWeights),
                    _ => Invalid,
                })
            }
        }
        deserializer.deserialize_str(Visitor)
    }
}

impl ser::Serialize for Property {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: ser::Serializer
    {
        serializer.serialize_str(match *self {
            Property::Translation => "translation",
            Property::Rotation => "rotation",
            Property::Scale => "scale",
            Property::MorphTargetWeights => "weights",
        })
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use db::migration::Migrator;

use error::Result;

pub fn migrate(migrator: &mut Migrator) -> Result<()> {
    migrator
        .migrate("originsrv",
                 r#"CREATE SEQUENCE IF NOT EXISTS origin_channel_id_seq;"#)?;
    migrator
        .migrate("originsrv",
                 r#"CREATE TABLE origin_channels (
                    id bigint PRIMARY KEY DEFAULT next_id_v1('origin_channel_id_seq'),
                    origin_id bigint REFERENCES origins(id),
                    owner_id bigint,
                    name text,
                    created_at timestamptz DEFAULT now(),
                    updated_at timestamptz,
                    UNIQUE(origin_id, name)
             )"#)?;
    migrator
        .migrate("originsrv",
                 r#"CREATE TABLE origin_channel_packages (
                    channel_id bigint REFERENCES origin_channels(id) ON DELETE CASCADE,
                    package_id bigint REFERENCES origin_packages(id),
                    created_at timestamptz DEFAULT now(),
                    updated_at timestamptz,
                    PRIMARY KEY (channel_id, package_id)
             )"#)?;
    migrator
        .migrate("originsrv",
                 r#"CREATE OR REPLACE FUNCTION insert_origin_channel_v1 (
                    occ_origin_id bigint,
                    occ_owner_id bigint,
                    occ_name text
                 ) RETURNS SETOF origin_channels AS $$
                     BEGIN
                         RETURN QUERY INSERT INTO origin_channels (origin_id, owner_id, name)
                                VALUES (occ_origin_id, occ_owner_id, occ_name)
                                RETURNING *;
                         RETURN;
                     END
                 $$ LANGUAGE plpgsql VOLATILE"#)?;
    migrator
        .migrate("originsrv",
                 r#"CREATE OR REPLACE FUNCTION get_origin_channel_v1 (
                    ocg_origin text,
                    ocg_name text
                 ) RETURNS SETOF origin_channels AS $$
                    BEGIN
                        RETURN QUERY SELECT origin_channels.*
                          FROM origins INNER JOIN origin_channels ON origins.id = origin_channels.origin_id
                          WHERE origins.name=ocg_origin AND origin_channels.name = ocg_name;
                        RETURN;
                    END
                    $$ LANGUAGE plpgsql STABLE"#)?;
    migrator
        .migrate("originsrv",
                 r#"CREATE OR REPLACE FUNCTION get_origin_channels_for_origin_v1 (
                   occ_origin_id bigint
                 ) RETURNS SETOF origin_channels AS $$
                    BEGIN
                        RETURN QUERY SELECT * FROM origin_channels WHERE origin_id = occ_origin_id
                          ORDER BY name ASC;
                        RETURN;
                    END
                    $$ LANGUAGE plpgsql STABLE"#)?;
    migrator.migrate("originsrv",
                     r#"CREATE OR REPLACE FUNCTION promote_origin_package_v1 (
                    opp_channel_id bigint,
                    opp_package_id bigint
                 ) RETURNS void AS $$
                    BEGIN
                        INSERT INTO origin_channel_packages (channel_id, package_id) VALUES (opp_channel_id, opp_package_id);
                    END
                 $$ LANGUAGE plpgsql VOLATILE"#)?;
    migrator.migrate("originsrv",
                           r#"CREATE OR REPLACE FUNCTION delete_origin_channel_v1 (
                    channel_id bigint
                 ) RETURNS void AS $$
                    BEGIN
                        DELETE FROM origin_channels WHERE id = channel_id;
                    END
                    $$ LANGUAGE plpgsql VOLATILE"#)?;

    migrator
        .migrate("originsrv",
                 r#"CREATE OR REPLACE FUNCTION get_origin_channel_package_v1 (
                    op_origin text,
                    op_channel text,
                    op_ident text
                 ) RETURNS SETOF origin_packages AS $$
                    BEGIN
                        RETURN QUERY SELECT op.*
                          FROM origin_packages op
                          INNER JOIN origin_channel_packages ocp on ocp.package_id = op.id
                          INNER JOIN origin_channels oc on ocp.channel_id = oc.id
                          INNER JOIN origins o on oc.origin_id = o.id
                          WHERE op.ident = op_ident AND o.name = op_origin AND oc.name = op_channel;
                        RETURN;
                    END
                    $$ LANGUAGE plpgsql STABLE"#)?;
    migrator
        .migrate("originsrv",
                 r#"CREATE OR REPLACE FUNCTION get_origin_channel_package_latest_v1 (
                    op_origin text,
                    op_channel text,
                    op_ident text,
                    op_target text
                 ) RETURNS SETOF origin_packages AS $$
                    BEGIN
                        RETURN QUERY SELECT op.*
                          FROM origin_packages op
                          INNER JOIN origin_channel_packages ocp on ocp.package_id = op.id
                          INNER JOIN origin_channels oc on ocp.channel_id = oc.id
                          INNER JOIN origins o on oc.origin_id = o.id
                          WHERE o.name = op_origin
                          AND oc.name = op_channel
                          AND op.ident LIKE (op_ident  || '%')
                          AND op.target = op_target
                          ORDER BY op.ident DESC
                          LIMIT 1;
                        RETURN;
                    END
                    $$ LANGUAGE plpgsql STABLE"#)?;
    migrator
        .migrate("originsrv",
                 r#"CREATE OR REPLACE FUNCTION get_origin_channel_packages_for_channel_v1 (
                    op_origin text,
                    op_channel text,
                    op_ident text,
                    op_limit bigint,
                    op_offset bigint
                 ) RETURNS TABLE(total_count bigint, ident text) AS $$
                    BEGIN
                        RETURN QUERY SELECT COUNT(*) OVER () AS total_count, op.ident
                          FROM origin_packages op
                          INNER JOIN origin_channel_packages ocp on ocp.package_id = op.id
                          INNER JOIN origin_channels oc on ocp.channel_id = oc.id
                          INNER JOIN origins o on oc.origin_id = o.id
                          WHERE o.name = op_origin
                          AND oc.name = op_channel
                          AND op.ident LIKE (op_ident  || '%')
                          ORDER BY ident ASC
                          LIMIT op_limit OFFSET op_offset;
                        RETURN;
                    END
                    $$ LANGUAGE plpgsql STABLE"#)?;
    Ok(())
}
<commit_msg>Don't fail on non-unique channel promote<commit_after>// Copyright (c) 2016-2017 Chef Software Inc. and/or applicable contributors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use db::migration::Migrator;

use error::Result;

pub fn migrate(migrator: &mut Migrator) -> Result<()> {
    migrator
        .migrate("originsrv",
                 r#"CREATE SEQUENCE IF NOT EXISTS origin_channel_id_seq;"#)?;
    migrator
        .migrate("originsrv",
                 r#"CREATE TABLE origin_channels (
                    id bigint PRIMARY KEY DEFAULT next_id_v1('origin_channel_id_seq'),
                    origin_id bigint REFERENCES origins(id),
                    owner_id bigint,
                    name text,
                    created_at timestamptz DEFAULT now(),
                    updated_at timestamptz,
                    UNIQUE(origin_id, name)
             )"#)?;
    migrator
        .migrate("originsrv",
                 r#"CREATE TABLE origin_channel_packages (
                    channel_id bigint REFERENCES origin_channels(id) ON DELETE CASCADE,
                    package_id bigint REFERENCES origin_packages(id),
                    created_at timestamptz DEFAULT now(),
                    updated_at timestamptz,
                    PRIMARY KEY (channel_id, package_id)
             )"#)?;
    migrator
        .migrate("originsrv",
                 r#"CREATE OR REPLACE FUNCTION insert_origin_channel_v1 (
                    occ_origin_id bigint,
                    occ_owner_id bigint,
                    occ_name text
                 ) RETURNS SETOF origin_channels AS $$
                     BEGIN
                         RETURN QUERY INSERT INTO origin_channels (origin_id, owner_id, name)
                                VALUES (occ_origin_id, occ_owner_id, occ_name)
                                RETURNING *;
                         RETURN;
                     END
                 $$ LANGUAGE plpgsql VOLATILE"#)?;
    migrator
        .migrate("originsrv",
                 r#"CREATE OR REPLACE FUNCTION get_origin_channel_v1 (
                    ocg_origin text,
                    ocg_name text
                 ) RETURNS SETOF origin_channels AS $$
                    BEGIN
                        RETURN QUERY SELECT origin_channels.*
                          FROM origins INNER JOIN origin_channels ON origins.id = origin_channels.origin_id
                          WHERE origins.name=ocg_origin AND origin_channels.name = ocg_name;
                        RETURN;
                    END
                    $$ LANGUAGE plpgsql STABLE"#)?;
    migrator
        .migrate("originsrv",
                 r#"CREATE OR REPLACE FUNCTION get_origin_channels_for_origin_v1 (
                   occ_origin_id bigint
                 ) RETURNS SETOF origin_channels AS $$
                    BEGIN
                        RETURN QUERY SELECT * FROM origin_channels WHERE origin_id = occ_origin_id
                          ORDER BY name ASC;
                        RETURN;
                    END
                    $$ LANGUAGE plpgsql STABLE"#)?;
    migrator.migrate("originsrv",
                     r#"CREATE OR REPLACE FUNCTION promote_origin_package_v1 (
                    opp_channel_id bigint,
                    opp_package_id bigint
                 ) RETURNS void AS $$
                        INSERT INTO origin_channel_packages (channel_id, package_id) VALUES (opp_channel_id, opp_package_id)
                        ON CONFLICT ON CONSTRAINT origin_channel_packages_pkey DO NOTHING;
                 $$ LANGUAGE SQL VOLATILE"#)?;
    migrator
        .migrate("originsrv",
                 r#"CREATE OR REPLACE FUNCTION delete_origin_channel_v1 (
                    channel_id bigint
                 ) RETURNS void AS $$
                    BEGIN
                        DELETE FROM origin_channels WHERE id = channel_id;
                    END
                    $$ LANGUAGE plpgsql VOLATILE"#)?;

    migrator
        .migrate("originsrv",
                 r#"CREATE OR REPLACE FUNCTION get_origin_channel_package_v1 (
                    op_origin text,
                    op_channel text,
                    op_ident text
                 ) RETURNS SETOF origin_packages AS $$
                    BEGIN
                        RETURN QUERY SELECT op.*
                          FROM origin_packages op
                          INNER JOIN origin_channel_packages ocp on ocp.package_id = op.id
                          INNER JOIN origin_channels oc on ocp.channel_id = oc.id
                          INNER JOIN origins o on oc.origin_id = o.id
                          WHERE op.ident = op_ident AND o.name = op_origin AND oc.name = op_channel;
                        RETURN;
                    END
                    $$ LANGUAGE plpgsql STABLE"#)?;
    migrator
        .migrate("originsrv",
                 r#"CREATE OR REPLACE FUNCTION get_origin_channel_package_latest_v1 (
                    op_origin text,
                    op_channel text,
                    op_ident text,
                    op_target text
                 ) RETURNS SETOF origin_packages AS $$
                    BEGIN
                        RETURN QUERY SELECT op.*
                          FROM origin_packages op
                          INNER JOIN origin_channel_packages ocp on ocp.package_id = op.id
                          INNER JOIN origin_channels oc on ocp.channel_id = oc.id
                          INNER JOIN origins o on oc.origin_id = o.id
                          WHERE o.name = op_origin
                          AND oc.name = op_channel
                          AND op.ident LIKE (op_ident  || '%')
                          AND op.target = op_target
                          ORDER BY op.ident DESC
                          LIMIT 1;
                        RETURN;
                    END
                    $$ LANGUAGE plpgsql STABLE"#)?;
    migrator
        .migrate("originsrv",
                 r#"CREATE OR REPLACE FUNCTION get_origin_channel_packages_for_channel_v1 (
                    op_origin text,
                    op_channel text,
                    op_ident text,
                    op_limit bigint,
                    op_offset bigint
                 ) RETURNS TABLE(total_count bigint, ident text) AS $$
                    BEGIN
                        RETURN QUERY SELECT COUNT(*) OVER () AS total_count, op.ident
                          FROM origin_packages op
                          INNER JOIN origin_channel_packages ocp on ocp.package_id = op.id
                          INNER JOIN origin_channels oc on ocp.channel_id = oc.id
                          INNER JOIN origins o on oc.origin_id = o.id
                          WHERE o.name = op_origin
                          AND oc.name = op_channel
                          AND op.ident LIKE (op_ident  || '%')
                          ORDER BY ident ASC
                          LIMIT op_limit OFFSET op_offset;
                        RETURN;
                    END
                    $$ LANGUAGE plpgsql STABLE"#)?;
    Ok(())
}
<|endoftext|>