<commit_before>pub mod actions;
pub mod boot_source;
pub mod drive;
pub mod instance_info;
pub mod logger;
pub mod machine_configuration;
pub mod net;

use serde_json::Value;
use std::{fmt, result};

use futures::sync::oneshot;
use http_service::{empty_response, json_fault_message, json_response};
use hyper;
use hyper::{Method, StatusCode};

use data_model::vm::{BlockDeviceConfig, DriveError, MachineConfiguration, PatchDrivePayload};

use self::actions::ActionBody;
use self::boot_source::BootSourceBody;
use self::logger::APILoggerDescription;
use self::net::NetworkInterfaceBody;

pub type SyncOutcomeSender = oneshot::Sender<Box<GenerateResponse + Send>>;
pub type SyncOutcomeReceiver = oneshot::Receiver<Box<GenerateResponse + Send>>;

pub enum ParsedRequest {
    Dummy,
    GetInstanceInfo,
    GetMMDS,
    PatchMMDS(Value),
    PutMMDS(Value),
    Sync(SyncRequest, SyncOutcomeReceiver),
}

pub trait IntoParsedRequest {
    fn into_parsed_request(self, method: Method) -> result::Result<ParsedRequest, String>;
}

// Sync requests have outcomes which implement this trait. The idea is for each outcome to be a
// struct which is cheaply and quickly instantiated by the VMM thread, then passed back the the API
// thread, and then unpacked into a http response using the implementation of
// the generate_response() method.
pub trait GenerateResponse {
    fn generate_response(&self) -> hyper::Response;
}

// This allows us to return a boxed Result directly as a SyncOutcome, if both the ok and the error
// types implement the GenerateResponse trait.
impl<T, U> GenerateResponse for result::Result<T, U>
where
    T: GenerateResponse,
    U: GenerateResponse,
{
    fn generate_response(&self) -> hyper::Response {
        match *self {
            Ok(ref v) => v.generate_response(),
            Err(ref e) => e.generate_response(),
        }
    }
}

impl GenerateResponse for () {
    fn generate_response(&self) -> hyper::Response {
        empty_response(StatusCode::NoContent)
    }
}

// This enum contains messages for the VMM which represent sync requests. They each contain various
// bits of information (ids, paths, etc.), together with an OutcomeSender, which is always present.
pub enum SyncRequest {
    GetMachineConfiguration(SyncOutcomeSender),
    PatchDrive(Value, SyncOutcomeSender),
    PutBootSource(BootSourceBody, SyncOutcomeSender),
    PutDrive(BlockDeviceConfig, SyncOutcomeSender),
    PutLogger(APILoggerDescription, SyncOutcomeSender),
    PutMachineConfiguration(MachineConfiguration, SyncOutcomeSender),
    PutNetworkInterface(NetworkInterfaceBody, SyncOutcomeSender),
    RescanBlockDevice(ActionBody, SyncOutcomeSender),
    StartInstance(SyncOutcomeSender),
}

impl fmt::Debug for SyncRequest {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "SyncRequest")
    }
}

// TODO: we should move toward having both the ok status and various possible sync request errors
// in this file, because there are many common sync outcomes.

#[derive(PartialEq)]
pub enum OkStatus {
    Created,
    Ok,
    NoContent,
}

impl GenerateResponse for OkStatus {
    fn generate_response(&self) -> hyper::Response {
        use self::OkStatus::*;
        match *self {
            Created => empty_response(StatusCode::Created),
            Ok => empty_response(StatusCode::Ok),
            NoContent => empty_response(StatusCode::NoContent),
        }
    }
}

// Potential errors associated with sync requests.
#[derive(Debug)]
pub enum Error {
    DriveOperationFailed(DriveError),
    GuestCIDAlreadyInUse,
    InstanceStartFailed(ErrorType, String),
    InvalidPayload,
    OperationFailed,
    OperationNotAllowedPreBoot,
    UpdateNotAllowedPostBoot,
    UpdateNotImplemented,
}

/// We need to implement to string because we are sending the errors to the clients by
/// using json_fault_message function that expects a type which implements AsRef<str>.
/// When using the default Debug formatting, we end up with invalid jsons because we don't
/// escape the quotes in the error message.
impl ToString for Error {
    fn to_string(&self) -> String {
        match self {
            Error::InstanceStartFailed(_, ref err_msg) => err_msg.replace("\"", ""),
            _ => {
                let err_msg = format!("{:?}", self);
                err_msg.replace("\"", "")
            }
        }
    }
}

#[derive(Debug)]
pub enum ErrorType {
    UserError,
    InternalError,
}

impl GenerateResponse for Error {
    fn generate_response(&self) -> hyper::Response {
        use self::Error::*;
        match *self {
            DriveOperationFailed(ref e) => e.generate_response(),
            GuestCIDAlreadyInUse => json_response(
                StatusCode::BadRequest,
                json_fault_message("The specified guest CID is already in use."),
            ),
            InstanceStartFailed(ref error_type, _) => {
                let status_code = match error_type {
                    ErrorType::InternalError => StatusCode::InternalServerError,
                    ErrorType::UserError => StatusCode::BadRequest,
                };

                json_response(status_code, json_fault_message(self.to_string()))
            }
            InvalidPayload => json_response(
                StatusCode::BadRequest,
                json_fault_message("The request payload is invalid."),
            ),
            OperationFailed => json_response(
                StatusCode::BadRequest,
                json_fault_message(format!("The operation failed.")),
            ),
            OperationNotAllowedPreBoot => json_response(
                StatusCode::Forbidden,
                json_fault_message(format!("The operation is now allowed before boot.")),
            ),
            UpdateNotAllowedPostBoot => json_response(
                StatusCode::Forbidden,
                json_fault_message("The update operation is not allowed after boot."),
            ),
            UpdateNotImplemented => json_response(
                StatusCode::InternalServerError,
                json_fault_message("Update operation is not implemented yet."),
            ),
        }
    }
}

pub type Result<T> = result::Result<T, Error>;

impl IntoParsedRequest for PatchDrivePayload {
    fn into_parsed_request(self, method: Method) -> result::Result<ParsedRequest, String> {
        match method {
            Method::Patch => {
                let (sender, receiver) = oneshot::channel();
                Ok(ParsedRequest::Sync(
                    SyncRequest::PatchDrive(self.fields, sender),
                    receiver,
                ))
            }
            _ => Err(format!("Invalid method {}!", method)),
        }
    }
}

#[cfg(test)]
impl PartialEq for ParsedRequest {
    fn eq(&self, other: &ParsedRequest) -> bool {
        match (self, other) {
            (
                &ParsedRequest::Sync(ref sync_req, _),
                &ParsedRequest::Sync(ref other_sync_req, _),
            ) => sync_req == other_sync_req,
            (&ParsedRequest::Dummy, &ParsedRequest::Dummy) => true,
            (&ParsedRequest::GetInstanceInfo, &ParsedRequest::GetInstanceInfo) => true,
            (&ParsedRequest::GetMMDS, &ParsedRequest::GetMMDS) => true,
            (&ParsedRequest::PutMMDS(ref val), &ParsedRequest::PutMMDS(ref other_val)) => {
                val == other_val
            }
            (&ParsedRequest::PatchMMDS(ref val), &ParsedRequest::PatchMMDS(ref other_val)) => {
                val == other_val
            }
            _ => false,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use futures::{Future, Stream};
    use hyper::{Body, Response};
    use serde_json::{self, Map};
    use std;

    // Implementation for the "==" operator.
    // Can't derive PartialEq directly because the sender members can't be compared.
    impl PartialEq for SyncRequest {
        fn eq(&self, other: &SyncRequest) -> bool {
            match (self, other) {
                (
                    &SyncRequest::PatchDrive(ref payload, _),
                    &SyncRequest::PatchDrive(ref other_payload, _),
                ) => payload == other_payload,
                (
                    &SyncRequest::PutBootSource(ref bsb, _),
                    &SyncRequest::PutBootSource(ref other_bsb, _),
                ) => bsb == other_bsb,
                (
                    &SyncRequest::PutDrive(ref ddesc, _),
                    &SyncRequest::PutDrive(ref other_ddesc, _),
                ) => ddesc == other_ddesc,
                (
                    &SyncRequest::PutLogger(ref logdesc, _),
                    &SyncRequest::PutLogger(ref other_logdesc, _),
                ) => logdesc == other_logdesc,
                (
                    &SyncRequest::PutMachineConfiguration(ref mcb, _),
                    &SyncRequest::PutMachineConfiguration(ref other_mcb, _),
                ) => mcb == other_mcb,
                (
                    &SyncRequest::PutNetworkInterface(ref netif, _),
                    &SyncRequest::PutNetworkInterface(ref other_netif, _),
                ) => netif == other_netif,
                (
                    &SyncRequest::RescanBlockDevice(ref req, _),
                    &SyncRequest::RescanBlockDevice(ref other_req, _),
                ) => req == other_req,
                (&SyncRequest::StartInstance(_), &SyncRequest::StartInstance(_)) => true,
                _ => false,
            }
        }
    }

    #[test]
    fn test_into_parsed_request() {
        let mut fields = Map::<String, Value>::new();
        fields.insert(String::from("drive_id"), Value::String(String::from("foo")));
        fields.insert(String::from("is_read_only"), Value::Bool(true));
        let pdp = PatchDrivePayload {
            fields: Value::Object(fields),
        };
        let (sender, receiver) = oneshot::channel();

        assert!(
            pdp.clone()
                .into_parsed_request(Method::Patch)
                .eq(&Ok(ParsedRequest::Sync(
                    SyncRequest::PatchDrive(pdp.fields.clone(), sender),
                    receiver
                )))
        );

        assert!(pdp.into_parsed_request(Method::Put).is_err());
    }

    #[test]
    fn test_generate_response_okstatus() {
        let mut ret = OkStatus::Created.generate_response();
        assert_eq!(ret.status(), StatusCode::Created);

        ret = OkStatus::NoContent.generate_response();
        assert_eq!(ret.status(), StatusCode::NoContent);
    }

    fn get_body(
        response: Response<Body>,
    ) -> std::result::Result<serde_json::Value, serde_json::Error> {
        let body = response
            .body()
            .map_err(|_| ())
            .fold(vec![], |mut acc, chunk| {
                acc.extend_from_slice(&chunk);
                Ok(acc)
            }).and_then(|v| String::from_utf8(v).map_err(|_| ()));
        serde_json::from_str::<Value>(body.wait().unwrap().as_ref())
    }

    #[test]
    fn test_generate_response_error() {
        let mut ret =
            Error::DriveOperationFailed(DriveError::InvalidBlockDeviceID).generate_response();
        assert_eq!(ret.status(), StatusCode::BadRequest);
        assert!(get_body(ret).is_ok());

        ret = Error::GuestCIDAlreadyInUse.generate_response();
        assert_eq!(ret.status(), StatusCode::BadRequest);
        assert!(get_body(ret).is_ok());

        ret = Error::InstanceStartFailed(
            ErrorType::InternalError,
            "Dummy error message.".to_string(),
        ).generate_response();
        assert_eq!(ret.status(), StatusCode::InternalServerError);
        assert!(get_body(ret).is_ok());

        ret = Error::InstanceStartFailed(ErrorType::UserError, "Dummy error message.".to_string())
            .generate_response();
        assert_eq!(ret.status(), StatusCode::BadRequest);
        assert!(get_body(ret).is_ok());

        ret = Error::InvalidPayload.generate_response();
        assert_eq!(ret.status(), StatusCode::BadRequest);
        assert!(get_body(ret).is_ok());

        ret = Error::OperationFailed.generate_response();
        assert_eq!(ret.status(), StatusCode::BadRequest);
        assert!(get_body(ret).is_ok());

        ret = Error::OperationNotAllowedPreBoot.generate_response();
        assert_eq!(ret.status(), StatusCode::Forbidden);
        assert!(get_body(ret).is_ok());

        ret = Error::UpdateNotAllowedPostBoot.generate_response();
        assert_eq!(ret.status(), StatusCode::Forbidden);
        assert!(get_body(ret).is_ok());

        ret = Error::UpdateNotImplemented.generate_response();
        assert_eq!(ret.status(), StatusCode::InternalServerError);
        assert!(get_body(ret).is_ok());
    }
}
<commit_msg>api_server: removed OkStatus<commit_after>pub mod actions;
pub mod boot_source;
pub mod drive;
pub mod instance_info;
pub mod logger;
pub mod machine_configuration;
pub mod net;

use serde_json::Value;
use std::{fmt, result};

use futures::sync::oneshot;
use http_service::{empty_response, json_fault_message, json_response};
use hyper;
use hyper::{Method, StatusCode};

use data_model::vm::{BlockDeviceConfig, DriveError, MachineConfiguration, PatchDrivePayload};

use self::actions::ActionBody;
use self::boot_source::BootSourceBody;
use self::logger::APILoggerDescription;
use self::net::NetworkInterfaceBody;

pub type SyncOutcomeSender = oneshot::Sender<Box<GenerateResponse + Send>>;
pub type SyncOutcomeReceiver = oneshot::Receiver<Box<GenerateResponse + Send>>;

pub enum ParsedRequest {
    Dummy,
    GetInstanceInfo,
    GetMMDS,
    PatchMMDS(Value),
    PutMMDS(Value),
    Sync(SyncRequest, SyncOutcomeReceiver),
}

pub trait IntoParsedRequest {
    fn into_parsed_request(self, method: Method) -> result::Result<ParsedRequest, String>;
}

// Sync requests have outcomes which implement this trait. The idea is for each outcome to be a
// struct which is cheaply and quickly instantiated by the VMM thread, then passed back the the API
// thread, and then unpacked into a http response using the implementation of
// the generate_response() method.
pub trait GenerateResponse {
    fn generate_response(&self) -> hyper::Response;
}

// This allows us to return a boxed Result directly as a SyncOutcome, if both the ok and the error
// types implement the GenerateResponse trait.
impl<T, U> GenerateResponse for result::Result<T, U>
where
    T: GenerateResponse,
    U: GenerateResponse,
{
    fn generate_response(&self) -> hyper::Response {
        match *self {
            Ok(ref v) => v.generate_response(),
            Err(ref e) => e.generate_response(),
        }
    }
}

impl GenerateResponse for () {
    fn generate_response(&self) -> hyper::Response {
        empty_response(StatusCode::NoContent)
    }
}

// This enum contains messages for the VMM which represent sync requests. They each contain various
// bits of information (ids, paths, etc.), together with an OutcomeSender, which is always present.
pub enum SyncRequest {
    GetMachineConfiguration(SyncOutcomeSender),
    PatchDrive(Value, SyncOutcomeSender),
    PutBootSource(BootSourceBody, SyncOutcomeSender),
    PutDrive(BlockDeviceConfig, SyncOutcomeSender),
    PutLogger(APILoggerDescription, SyncOutcomeSender),
    PutMachineConfiguration(MachineConfiguration, SyncOutcomeSender),
    PutNetworkInterface(NetworkInterfaceBody, SyncOutcomeSender),
    RescanBlockDevice(ActionBody, SyncOutcomeSender),
    StartInstance(SyncOutcomeSender),
}

impl fmt::Debug for SyncRequest {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "SyncRequest")
    }
}

// Potential errors associated with sync requests.
#[derive(Debug)]
pub enum Error {
    DriveOperationFailed(DriveError),
    GuestCIDAlreadyInUse,
    InstanceStartFailed(ErrorType, String),
    InvalidPayload,
    OperationFailed,
    OperationNotAllowedPreBoot,
    UpdateNotAllowedPostBoot,
    UpdateNotImplemented,
}

/// We need to implement to string because we are sending the errors to the clients by
/// using json_fault_message function that expects a type which implements AsRef<str>.
/// When using the default Debug formatting, we end up with invalid jsons because we don't
/// escape the quotes in the error message.
impl ToString for Error {
    fn to_string(&self) -> String {
        match self {
            Error::InstanceStartFailed(_, ref err_msg) => err_msg.replace("\"", ""),
            _ => {
                let err_msg = format!("{:?}", self);
                err_msg.replace("\"", "")
            }
        }
    }
}

#[derive(Debug)]
pub enum ErrorType {
    UserError,
    InternalError,
}

impl GenerateResponse for Error {
    fn generate_response(&self) -> hyper::Response {
        use self::Error::*;
        match *self {
            DriveOperationFailed(ref e) => e.generate_response(),
            GuestCIDAlreadyInUse => json_response(
                StatusCode::BadRequest,
                json_fault_message("The specified guest CID is already in use."),
            ),
            InstanceStartFailed(ref error_type, _) => {
                let status_code = match error_type {
                    ErrorType::InternalError => StatusCode::InternalServerError,
                    ErrorType::UserError => StatusCode::BadRequest,
                };

                json_response(status_code, json_fault_message(self.to_string()))
            }
            InvalidPayload => json_response(
                StatusCode::BadRequest,
                json_fault_message("The request payload is invalid."),
            ),
            OperationFailed => json_response(
                StatusCode::BadRequest,
                json_fault_message(format!("The operation failed.")),
            ),
            OperationNotAllowedPreBoot => json_response(
                StatusCode::Forbidden,
                json_fault_message(format!("The operation is now allowed before boot.")),
            ),
            UpdateNotAllowedPostBoot => json_response(
                StatusCode::Forbidden,
                json_fault_message("The update operation is not allowed after boot."),
            ),
            UpdateNotImplemented => json_response(
                StatusCode::InternalServerError,
                json_fault_message("Update operation is not implemented yet."),
            ),
        }
    }
}

pub type Result<T> = result::Result<T, Error>;

impl IntoParsedRequest for PatchDrivePayload {
    fn into_parsed_request(self, method: Method) -> result::Result<ParsedRequest, String> {
        match method {
            Method::Patch => {
                let (sender, receiver) = oneshot::channel();
                Ok(ParsedRequest::Sync(
                    SyncRequest::PatchDrive(self.fields, sender),
                    receiver,
                ))
            }
            _ => Err(format!("Invalid method {}!", method)),
        }
    }
}

#[cfg(test)]
impl PartialEq for ParsedRequest {
    fn eq(&self, other: &ParsedRequest) -> bool {
        match (self, other) {
            (
                &ParsedRequest::Sync(ref sync_req, _),
                &ParsedRequest::Sync(ref other_sync_req, _),
            ) => sync_req == other_sync_req,
            (&ParsedRequest::Dummy, &ParsedRequest::Dummy) => true,
            (&ParsedRequest::GetInstanceInfo, &ParsedRequest::GetInstanceInfo) => true,
            (&ParsedRequest::GetMMDS, &ParsedRequest::GetMMDS) => true,
            (&ParsedRequest::PutMMDS(ref val), &ParsedRequest::PutMMDS(ref other_val)) => {
                val == other_val
            }
            (&ParsedRequest::PatchMMDS(ref val), &ParsedRequest::PatchMMDS(ref other_val)) => {
                val == other_val
            }
            _ => false,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use futures::{Future, Stream};
    use hyper::{Body, Response};
    use serde_json::{self, Map};
    use std;

    // Implementation for the "==" operator.
    // Can't derive PartialEq directly because the sender members can't be compared.
    impl PartialEq for SyncRequest {
        fn eq(&self, other: &SyncRequest) -> bool {
            match (self, other) {
                (
                    &SyncRequest::PatchDrive(ref payload, _),
                    &SyncRequest::PatchDrive(ref other_payload, _),
                ) => payload == other_payload,
                (
                    &SyncRequest::PutBootSource(ref bsb, _),
                    &SyncRequest::PutBootSource(ref other_bsb, _),
                ) => bsb == other_bsb,
                (
                    &SyncRequest::PutDrive(ref ddesc, _),
                    &SyncRequest::PutDrive(ref other_ddesc, _),
                ) => ddesc == other_ddesc,
                (
                    &SyncRequest::PutLogger(ref logdesc, _),
                    &SyncRequest::PutLogger(ref other_logdesc, _),
                ) => logdesc == other_logdesc,
                (
                    &SyncRequest::PutMachineConfiguration(ref mcb, _),
                    &SyncRequest::PutMachineConfiguration(ref other_mcb, _),
                ) => mcb == other_mcb,
                (
                    &SyncRequest::PutNetworkInterface(ref netif, _),
                    &SyncRequest::PutNetworkInterface(ref other_netif, _),
                ) => netif == other_netif,
                (
                    &SyncRequest::RescanBlockDevice(ref req, _),
                    &SyncRequest::RescanBlockDevice(ref other_req, _),
                ) => req == other_req,
                (&SyncRequest::StartInstance(_), &SyncRequest::StartInstance(_)) => true,
                _ => false,
            }
        }
    }

    #[test]
    fn test_into_parsed_request() {
        let mut fields = Map::<String, Value>::new();
        fields.insert(String::from("drive_id"), Value::String(String::from("foo")));
        fields.insert(String::from("is_read_only"), Value::Bool(true));
        let pdp = PatchDrivePayload {
            fields: Value::Object(fields),
        };
        let (sender, receiver) = oneshot::channel();

        assert!(
            pdp.clone()
                .into_parsed_request(Method::Patch)
                .eq(&Ok(ParsedRequest::Sync(
                    SyncRequest::PatchDrive(pdp.fields.clone(), sender),
                    receiver
                )))
        );

        assert!(pdp.into_parsed_request(Method::Put).is_err());
    }

    fn get_body(
        response: Response<Body>,
    ) -> std::result::Result<serde_json::Value, serde_json::Error> {
        let body = response
            .body()
            .map_err(|_| ())
            .fold(vec![], |mut acc, chunk| {
                acc.extend_from_slice(&chunk);
                Ok(acc)
            }).and_then(|v| String::from_utf8(v).map_err(|_| ()));
        serde_json::from_str::<Value>(body.wait().unwrap().as_ref())
    }

    #[test]
    fn test_generate_response_error() {
        let mut ret =
            Error::DriveOperationFailed(DriveError::InvalidBlockDeviceID).generate_response();
        assert_eq!(ret.status(), StatusCode::BadRequest);
        assert!(get_body(ret).is_ok());

        ret = Error::GuestCIDAlreadyInUse.generate_response();
        assert_eq!(ret.status(), StatusCode::BadRequest);
        assert!(get_body(ret).is_ok());

        ret = Error::InstanceStartFailed(
            ErrorType::InternalError,
            "Dummy error message.".to_string(),
        ).generate_response();
        assert_eq!(ret.status(), StatusCode::InternalServerError);
        assert!(get_body(ret).is_ok());

        ret = Error::InstanceStartFailed(ErrorType::UserError, "Dummy error message.".to_string())
            .generate_response();
        assert_eq!(ret.status(), StatusCode::BadRequest);
        assert!(get_body(ret).is_ok());

        ret = Error::InvalidPayload.generate_response();
        assert_eq!(ret.status(), StatusCode::BadRequest);
        assert!(get_body(ret).is_ok());

        ret = Error::OperationFailed.generate_response();
        assert_eq!(ret.status(), StatusCode::BadRequest);
        assert!(get_body(ret).is_ok());

        ret = Error::OperationNotAllowedPreBoot.generate_response();
        assert_eq!(ret.status(), StatusCode::Forbidden);
        assert!(get_body(ret).is_ok());

        ret = Error::UpdateNotAllowedPostBoot.generate_response();
        assert_eq!(ret.status(), StatusCode::Forbidden);
        assert!(get_body(ret).is_ok());

        ret = Error::UpdateNotImplemented.generate_response();
        assert_eq!(ret.status(), StatusCode::InternalServerError);
        assert!(get_body(ret).is_ok());
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

use jsapi::{JSContext, JSObject, JSPropertyDescriptor, JSBool};

pub type JSJitPropertyOp = *const u8;

pub struct JSJitInfo {
    pub op: JSJitPropertyOp,
    pub protoID: u32,
    pub depth: u32,
    pub isInfallible: bool,
    pub isConstant: bool
}

extern {
pub fn JS_ObjectToOuterObject(cx: *mut JSContext,
                              obj: *mut JSObject) -> *mut JSObject;
pub fn JS_WrapPropertyDescriptor(cx: *mut JSContext,
                                 desc: *mut JSPropertyDescriptor) -> JSBool;
}

//pub type JSJitInfo = JSJitInfo_struct;

pub mod bindgen {
    use jsapi::{JSContext, JSObject, JSClass, JSRuntime};
    use libc::uintptr_t;

    extern {
        pub fn JS_NewObjectWithUniqueType(cx: *mut JSContext, clasp: *const JSClass,
                                          proto: *const JSObject, parent: *const JSObject) -> *mut JSObject;
        pub fn JS_GetAddressableObject(rt: *mut JSRuntime, candidateObj: uintptr_t) -> *mut JSObject;
    }
}
<commit_msg>uint8ClampedArray method declarations added.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

use jsapi::{JSContext, JSObject, JSPropertyDescriptor, JSBool};

pub type JSJitPropertyOp = *const u8;

pub struct JSJitInfo {
    pub op: JSJitPropertyOp,
    pub protoID: u32,
    pub depth: u32,
    pub isInfallible: bool,
    pub isConstant: bool
}

extern {
pub fn JS_ObjectToOuterObject(cx: *mut JSContext,
                              obj: *mut JSObject) -> *mut JSObject;
pub fn JS_WrapPropertyDescriptor(cx: *mut JSContext,
                                 desc: *mut JSPropertyDescriptor) -> JSBool;
}

//pub type JSJitInfo = JSJitInfo_struct;

pub mod bindgen {
    use jsapi::{JSContext, JSObject, JSClass, JSRuntime};
    use libc::{uintptr_t, uint8_t, uint32_t};

    extern {
        pub fn JS_NewObjectWithUniqueType(cx: *mut JSContext, clasp: *const JSClass,
                                          proto: *const JSObject, parent: *const JSObject) -> *mut JSObject;
        pub fn JS_GetAddressableObject(rt: *mut JSRuntime, candidateObj: uintptr_t) -> *mut JSObject;
        pub fn JS_NewUint8ClampedArray(cx: *mut JSContext, nelements: uint32_t) -> *mut JSObject;
        pub fn JS_GetUint8ClampedArrayData(obj: *mut JSObject, cx: *mut JSContext) -> *mut uint8_t;
    }
}
<|endoftext|>


<commit_before>use query::DocResult;
use std::str;

pub enum Segment {
    ObjectKey(String),
    Array(u64),
}

#[derive(Debug, Clone)]
pub struct KeyBuilder {
    keypath: Vec<String>,
    arraypath: Vec<u64>,
}

impl KeyBuilder {
    pub fn new() -> KeyBuilder {
        KeyBuilder{
             // Magic reserve number is completely arbitrary
            keypath: Vec::with_capacity(10),
            arraypath: Vec::with_capacity(10),
        }
    }

    pub fn get_keypathword_only(&self, word: &str) -> String {
        let mut string = String::with_capacity(100);
        string.push('W');
        for segment in &self.keypath {
            string.push_str(&segment);
        }
        string.push('!');
        string.push_str(word);
        string.push('#');
        string
    }

    pub fn keypathword_count_key(&self, word: &str) -> String {
        let mut string = String::with_capacity(100);
        string.push('F');
        for segment in &self.keypath {
            string.push_str(&segment);
        }
        string.push('!');
        string.push_str(word);
        string
    }

    pub fn keypath_count_key(&self) -> String {
        let mut string = String::with_capacity(100);
        string.push('K');
        for segment in &self.keypath {
            string.push_str(&segment);
        }
        string
    }

    /// Builds a stemmed word key for the input word and seq, using the key_path and arraypath
    /// built up internally.
    pub fn stemmed_word_key(&self, word: &str, seq: u64) -> String {
        let mut string = self.get_keypathword_only(&word);
        string.push_str(seq.to_string().as_str());

        KeyBuilder::add_arraypath(&mut string, &self.arraypath);
        string
    }

    /// Builds a field length key for the seq, using the key_path and arraypath
    /// built up internally.
    pub fn field_length_key(&self, seq: u64) -> String {
        let mut string = String::with_capacity(100);
        string.push('L');
        for segment in &self.keypath {
            string.push_str(&segment);
        }
        string.push('#');
        string.push_str(seq.to_string().as_str());

        KeyBuilder::add_arraypath(&mut string, &self.arraypath);
        string
    }

    /// Builds a field length key for the DocResult, using the key_path
    /// built up internally and the arraypath from the DocResult.
    pub fn field_length_key_from_doc_result(&self, dr: &DocResult) -> String {
        let mut string = String::with_capacity(100);
        string.push('L');
        for segment in &self.keypath {
            string.push_str(&segment);
        }
        string.push('#');
        string.push_str(dr.seq.to_string().as_str());

        KeyBuilder::add_arraypath(&mut string, &dr.arraypath);
        string
    }

    /// Adds DocResult seq and array path an already created keypathword.
    pub fn add_doc_result_to_keypathword(keypathword: &mut String, dr: &DocResult) {
        keypathword.push_str(dr.seq.to_string().as_str());
        KeyBuilder::add_arraypath(keypathword, &dr.arraypath);
    }

    pub fn truncate_to_keypathword(stemmed_word_key: &mut String) {
        let n = stemmed_word_key.rfind("#").unwrap();
        stemmed_word_key.truncate(n + 1);
    }

    /// Builds a value key for seq (value keys are the original json terminal value with
    /// keyed on keypath and arraypath built up internally).
    pub fn value_key(&self, seq: u64) -> String {
        let mut string = String::with_capacity(100);
        string.push('V');
        string.push_str(&seq.to_string());
        string.push('#');
        let mut i = 0;
        for segment in &self.keypath {
            string.push_str(&segment);
            if segment == "$" {
                string.push_str(&self.arraypath[i].to_string());
                i += 1;
            }
        }
        string
    }

    /// Returns a value key without the doc seq prepended.
    pub fn value_key_path_only(&self) -> String {
        let mut string = String::with_capacity(100);
        let mut i = 0;
        for segment in &self.keypath {
            string.push_str(&segment);
            if segment == "$" {
                string.push_str(&self.arraypath[i].to_string());
                i += 1;
            }
        }
        string
    }

    pub fn value_key_from_doc_result(&self, dr: &DocResult) -> String {
        let mut string = String::with_capacity(100);
        string.push('V');
        string.push_str(&dr.seq.to_string());
        string.push('#');
        let mut i = 0;
        for segment in &self.keypath {
            string.push_str(&segment);
            if segment == "$" {
                string.push_str(&dr.arraypath[i].to_string());
                i += 1;
            }
        }
        string
    }

    fn add_arraypath(string: &mut String, arraypath: &Vec<u64>) {
        if arraypath.is_empty() {
            string.push(',');
        } else {
            for i in arraypath {
                string.push(',');
                string.push_str(i.to_string().as_str());
            }
        }
    }

    // returns the unescaped segment as Segment and the escaped segment as a slice
    pub fn parse_first_key_value_segment(keypath: &str) -> Option<(Segment, String)> {

        let mut unescaped = String::with_capacity(50);
        let mut len_bytes = 1;
        let mut chars = keypath.chars();

        // first char must be a . or a $ or we've exceeded the keypath
        match chars.next() {
            Some('.') => {
                loop {
                    match chars.next() {
                        Some('\\') => {
                            if let Some(c) = chars.next() {
                                len_bytes += c.len_utf8();
                                unescaped.push(c);
                            } else {
                                panic!("Escape char found as last char in keypath");
                            }
                        },
                        Some('.') | Some('$') => {
                            break;
                        },
                        Some(c) => {
                            len_bytes += c.len_utf8();
                            unescaped.push(c);
                        }
                        None => {
                            break;
                        }
                    }
                }
                Some((Segment::ObjectKey(unescaped), keypath[..len_bytes].to_string()))
            },
            Some('$') => {
                let mut i = String::new();
                for c in chars {
                    if c >= '0' && c <= '9' {
                        i.push(c);
                    } else {
                        break;
                    }
                }
                Some((Segment::Array(i.parse().unwrap()), keypath[..1+i.len()].to_string()))
            },
            Some(_) => None, // we must be past the keypath portion of string. done.
            None => None,
        }
    }

    pub fn push_object_key(&mut self, key: &str) {
        let mut escaped_key = String::with_capacity((key.len() * 2) + 1); // max expansion
        escaped_key.push('.');
        for cc in key.chars() {
            // Escape chars that conflict with delimiters
            if "\\$.!#".contains(cc) {
                escaped_key.push('\\');
            }
            escaped_key.push(cc);
        }
        self.keypath.push(escaped_key);
    }

    pub fn push_array(&mut self) {
        self.keypath.push("$".to_string());
        self.arraypath.push(0);
    }

    pub fn push_array_index(&mut self, index: u64) {
        self.keypath.push("$".to_string());
        self.arraypath.push(index);
    }

    pub fn pop_object_key(&mut self) {
        debug_assert!(self.keypath.last().unwrap().starts_with("."));
        self.keypath.pop();
    }

    pub fn peek_object_key(&self) -> String {
        debug_assert!(self.keypath.last().unwrap().starts_with("."));
        let x = KeyBuilder::parse_first_key_value_segment(&self.keypath.last().unwrap());
        if let Some((Segment::ObjectKey(key), _unescaped)) = x {
            key
        } else {
            panic!("peek_object_key is messed up yo!");
        }
    }

    pub fn peek_array_offset(&self) -> u64 {
        debug_assert!(self.keypath.last().unwrap().starts_with("$"));
        self.arraypath.last().unwrap().clone()
    }

    pub fn pop_array(&mut self) {
        debug_assert!(self.keypath.last().unwrap() == "$");
        self.arraypath.pop();
        self.keypath.pop();
    }

    pub fn inc_top_array_offset(&mut self) {
        if self.keypath.len() > 0 && self.keypath.last().unwrap() == "$" {
            *self.arraypath.last_mut().unwrap() += 1;
        }
    }

    pub fn arraypath_len(&self) -> usize {
        self.arraypath.len()
    }

    pub fn keypath_segments_len(&self) -> usize {
        self.keypath.len()
    }

    /* splits key into key path, seq and array path
        ex "W.foo$.bar$.baz!word#123,0,0" -> ("W.foo$.bar$.bar!word", "123", "0,0") */
    fn split_keypath_seq_arraypath_from_key(str: &str) -> (&str, &str, &str) {
        let n = str.rfind("#").unwrap();
        assert!(n != 0);
        assert!(n != str.len() - 1);
        let seq_arraypath_str = &str[(n + 1)..];
        let m = seq_arraypath_str.find(",").unwrap();

        (&str[..n], &seq_arraypath_str[..m], &seq_arraypath_str[m + 1..])
    }

    /* parses a seq and array path portion (ex "123,0,0,10) of a key into a doc result */
    pub fn parse_doc_result_from_key(str: &str) -> DocResult {
        let mut dr = DocResult::new();
        let (_path_str, seq_str, arraypath_str) =
                KeyBuilder::split_keypath_seq_arraypath_from_key(&str);
        dr.seq = seq_str.parse().unwrap();
        if !arraypath_str.is_empty() {
            for numstr in arraypath_str.split(",") {
                dr.arraypath.push(numstr.parse().unwrap());
            }
        }
        dr
    }

    pub fn compare_keys(akey: &str, bkey: &str) -> i32 {
        use std::cmp::Ordering;
        debug_assert!(akey.starts_with('W'));
        debug_assert!(bkey.starts_with('W'));
        let (apath_str, aseq_str, aarraypath_str) =
                KeyBuilder::split_keypath_seq_arraypath_from_key(&akey);
        let (bpath_str, bseq_str, barraypath_str) =
                KeyBuilder::split_keypath_seq_arraypath_from_key(&bkey);

        match apath_str[1..].cmp(&bpath_str[1..]) {
            Ordering::Less => -1,
            Ordering::Greater => 1,
            Ordering::Equal => {
                let aseq: u64 = aseq_str.parse().unwrap();
                let bseq: u64 = bseq_str.parse().unwrap();;
                if aseq < bseq {
                    -1
                } else if aseq > bseq {
                    1
                } else {
                    if aarraypath_str.is_empty() || barraypath_str.is_empty() {
                        match aarraypath_str.len().cmp(&barraypath_str.len()) {
                            Ordering::Less => -1,
                            Ordering::Greater => 1,
                            Ordering::Equal => 0,
                        }
                    } else {
                        let mut a_nums = aarraypath_str.split(",");
                        let mut b_nums = barraypath_str.split(",");
                        loop {
                            if let Some(ref a_num_str) = a_nums.next() {
                                if let Some(ref b_num_str) = b_nums.next() {
                                    let a_num: u64 = a_num_str.parse().unwrap();
                                    let b_num: u64 = b_num_str.parse().unwrap();
                                    match a_num.cmp(&b_num) {
                                        Ordering::Less => return -1,
                                        Ordering::Greater => return 1,
                                        Ordering::Equal => (),
                                    }
                                } else {
                                    //b is shorter than a, so greater
                                    return 1;
                                }
                            } else {
                                if b_nums.next().is_some() {
                                    //a is shorter than b so less
                                    return -1;
                                } else {
                                    // same length and must have hit all equal before this, so equal
                                    return 0;
                                }
                            }
                        }
                    }
                }
            },
        }
    }

}

#[cfg(test)]
mod tests {
    use super::{KeyBuilder};
    use query::DocResult;

    #[test]
    fn test_segments_push() {
        let mut kb = KeyBuilder::new();
        assert_eq!(kb.keypath_segments_len(), 0, "No segments so far");

        kb.push_object_key("first");
        assert_eq!(kb.keypath_segments_len(), 1, "One segment");

        kb.push_object_key("second");
        assert_eq!(kb.keypath_segments_len(), 2, "Two segments");

        kb.push_array();
        assert_eq!(kb.keypath_segments_len(), 3, "Three segments ");
    }

    #[test]
    fn test_segments_pop() {
        let mut kb = KeyBuilder::new();
        kb.push_object_key("first");
        kb.push_object_key("second");
        kb.push_array();

        assert_eq!(kb.keypath_segments_len(), 3, "three segments");
        assert_eq!(kb.stemmed_word_key("astemmedword", 123), "W.first.second$!astemmedword#123,0",
                   "Key for six segments is correct");

        kb.pop_array();
        assert_eq!(kb.keypath_segments_len(), 2, "Two segments");

        kb.pop_object_key();
        assert_eq!(kb.keypath_segments_len(), 1, "One segment");

        kb.pop_object_key();
        assert_eq!(kb.keypath_segments_len(), 0, "No segments so far");
    }

    #[test]
    fn test_doc_result_parse() {
        let key = "W.foo$.bar$!word#123,1,0".to_string();
        let (keypathstr, seqstr, arraypathstr) = KeyBuilder::split_keypath_seq_arraypath_from_key(&key);
        assert_eq!(keypathstr, "W.foo$.bar$!word");
        assert_eq!(seqstr, "123");
        assert_eq!(arraypathstr, "1,0");

        // make sure escaped commas and # in key path don't cause problems
        let key1 = "W.foo\\#$.bar\\,$!word#123,2,0".to_string();
        let (keypathstr1, seqstr1, arraypathstr1) = KeyBuilder::split_keypath_seq_arraypath_from_key(&key1);
        assert_eq!(keypathstr1, "W.foo\\#$.bar\\,$!word");
        assert_eq!(seqstr1, "123");
        assert_eq!(arraypathstr1, "2,0");

        let mut dr = DocResult::new();
        dr.seq = 123;
        dr.arraypath = vec![1,0];

        assert!(dr == KeyBuilder::parse_doc_result_from_key(&key));
    }
}
<commit_msg>Normalize object keys in key_builder<commit_after>extern crate unicode_normalization;

use query::DocResult;
use std::str;

use self::unicode_normalization::UnicodeNormalization;

pub enum Segment {
    ObjectKey(String),
    Array(u64),
}

#[derive(Debug, Clone)]
pub struct KeyBuilder {
    keypath: Vec<String>,
    arraypath: Vec<u64>,
}

impl KeyBuilder {
    pub fn new() -> KeyBuilder {
        KeyBuilder{
             // Magic reserve number is completely arbitrary
            keypath: Vec::with_capacity(10),
            arraypath: Vec::with_capacity(10),
        }
    }

    pub fn get_keypathword_only(&self, word: &str) -> String {
        let mut string = String::with_capacity(100);
        string.push('W');
        for segment in &self.keypath {
            string.push_str(&segment);
        }
        string.push('!');
        string.push_str(word);
        string.push('#');
        string
    }

    pub fn keypathword_count_key(&self, word: &str) -> String {
        let mut string = String::with_capacity(100);
        string.push('F');
        for segment in &self.keypath {
            string.push_str(&segment);
        }
        string.push('!');
        string.push_str(word);
        string
    }

    pub fn keypath_count_key(&self) -> String {
        let mut string = String::with_capacity(100);
        string.push('K');
        for segment in &self.keypath {
            string.push_str(&segment);
        }
        string
    }

    /// Builds a stemmed word key for the input word and seq, using the key_path and arraypath
    /// built up internally.
    pub fn stemmed_word_key(&self, word: &str, seq: u64) -> String {
        let mut string = self.get_keypathword_only(&word);
        string.push_str(seq.to_string().as_str());

        KeyBuilder::add_arraypath(&mut string, &self.arraypath);
        string
    }

    /// Builds a field length key for the seq, using the key_path and arraypath
    /// built up internally.
    pub fn field_length_key(&self, seq: u64) -> String {
        let mut string = String::with_capacity(100);
        string.push('L');
        for segment in &self.keypath {
            string.push_str(&segment);
        }
        string.push('#');
        string.push_str(seq.to_string().as_str());

        KeyBuilder::add_arraypath(&mut string, &self.arraypath);
        string
    }

    /// Builds a field length key for the DocResult, using the key_path
    /// built up internally and the arraypath from the DocResult.
    pub fn field_length_key_from_doc_result(&self, dr: &DocResult) -> String {
        let mut string = String::with_capacity(100);
        string.push('L');
        for segment in &self.keypath {
            string.push_str(&segment);
        }
        string.push('#');
        string.push_str(dr.seq.to_string().as_str());

        KeyBuilder::add_arraypath(&mut string, &dr.arraypath);
        string
    }

    /// Adds DocResult seq and array path an already created keypathword.
    pub fn add_doc_result_to_keypathword(keypathword: &mut String, dr: &DocResult) {
        keypathword.push_str(dr.seq.to_string().as_str());
        KeyBuilder::add_arraypath(keypathword, &dr.arraypath);
    }

    pub fn truncate_to_keypathword(stemmed_word_key: &mut String) {
        let n = stemmed_word_key.rfind("#").unwrap();
        stemmed_word_key.truncate(n + 1);
    }

    /// Builds a value key for seq (value keys are the original json terminal value with
    /// keyed on keypath and arraypath built up internally).
    pub fn value_key(&self, seq: u64) -> String {
        let mut string = String::with_capacity(100);
        string.push('V');
        string.push_str(&seq.to_string());
        string.push('#');
        let mut i = 0;
        for segment in &self.keypath {
            string.push_str(&segment);
            if segment == "$" {
                string.push_str(&self.arraypath[i].to_string());
                i += 1;
            }
        }
        string
    }

    /// Returns a value key without the doc seq prepended.
    pub fn value_key_path_only(&self) -> String {
        let mut string = String::with_capacity(100);
        let mut i = 0;
        for segment in &self.keypath {
            string.push_str(&segment);
            if segment == "$" {
                string.push_str(&self.arraypath[i].to_string());
                i += 1;
            }
        }
        string
    }

    pub fn value_key_from_doc_result(&self, dr: &DocResult) -> String {
        let mut string = String::with_capacity(100);
        string.push('V');
        string.push_str(&dr.seq.to_string());
        string.push('#');
        let mut i = 0;
        for segment in &self.keypath {
            string.push_str(&segment);
            if segment == "$" {
                string.push_str(&dr.arraypath[i].to_string());
                i += 1;
            }
        }
        string
    }

    fn add_arraypath(string: &mut String, arraypath: &Vec<u64>) {
        if arraypath.is_empty() {
            string.push(',');
        } else {
            for i in arraypath {
                string.push(',');
                string.push_str(i.to_string().as_str());
            }
        }
    }

    // returns the unescaped segment as Segment and the escaped segment as a slice
    pub fn parse_first_key_value_segment(keypath: &str) -> Option<(Segment, String)> {

        let mut unescaped = String::with_capacity(50);
        let mut len_bytes = 1;
        let mut chars = keypath.chars();

        // first char must be a . or a $ or we've exceeded the keypath
        match chars.next() {
            Some('.') => {
                loop {
                    match chars.next() {
                        Some('\\') => {
                            if let Some(c) = chars.next() {
                                len_bytes += c.len_utf8();
                                unescaped.push(c);
                            } else {
                                panic!("Escape char found as last char in keypath");
                            }
                        },
                        Some('.') | Some('$') => {
                            break;
                        },
                        Some(c) => {
                            len_bytes += c.len_utf8();
                            unescaped.push(c);
                        }
                        None => {
                            break;
                        }
                    }
                }
                Some((Segment::ObjectKey(unescaped), keypath[..len_bytes].to_string()))
            },
            Some('$') => {
                let mut i = String::new();
                for c in chars {
                    if c >= '0' && c <= '9' {
                        i.push(c);
                    } else {
                        break;
                    }
                }
                Some((Segment::Array(i.parse().unwrap()), keypath[..1+i.len()].to_string()))
            },
            Some(_) => None, // we must be past the keypath portion of string. done.
            None => None,
        }
    }

    pub fn push_object_key(&mut self, key: &str) {
        let mut escaped_key = String::with_capacity((key.len() * 2) + 1); // max expansion
        escaped_key.push('.');

        // normalize the key otherwise we might not match unnormalized but equivelent keys
        for cc in key.nfkc() {
            // Escape chars that conflict with delimiters
            if "\\$.!#".contains(cc) {
                escaped_key.push('\\');
            }
            escaped_key.push(cc);
        }
        self.keypath.push(escaped_key);
    }

    pub fn push_array(&mut self) {
        self.keypath.push("$".to_string());
        self.arraypath.push(0);
    }

    pub fn push_array_index(&mut self, index: u64) {
        self.keypath.push("$".to_string());
        self.arraypath.push(index);
    }

    pub fn pop_object_key(&mut self) {
        debug_assert!(self.keypath.last().unwrap().starts_with("."));
        self.keypath.pop();
    }

    pub fn peek_object_key(&self) -> String {
        debug_assert!(self.keypath.last().unwrap().starts_with("."));
        let x = KeyBuilder::parse_first_key_value_segment(&self.keypath.last().unwrap());
        if let Some((Segment::ObjectKey(key), _unescaped)) = x {
            key
        } else {
            panic!("peek_object_key is messed up yo!");
        }
    }

    pub fn peek_array_offset(&self) -> u64 {
        debug_assert!(self.keypath.last().unwrap().starts_with("$"));
        self.arraypath.last().unwrap().clone()
    }

    pub fn pop_array(&mut self) {
        debug_assert!(self.keypath.last().unwrap() == "$");
        self.arraypath.pop();
        self.keypath.pop();
    }

    pub fn inc_top_array_offset(&mut self) {
        if self.keypath.len() > 0 && self.keypath.last().unwrap() == "$" {
            *self.arraypath.last_mut().unwrap() += 1;
        }
    }

    pub fn arraypath_len(&self) -> usize {
        self.arraypath.len()
    }

    pub fn keypath_segments_len(&self) -> usize {
        self.keypath.len()
    }

    /* splits key into key path, seq and array path
        ex "W.foo$.bar$.baz!word#123,0,0" -> ("W.foo$.bar$.bar!word", "123", "0,0") */
    fn split_keypath_seq_arraypath_from_key(str: &str) -> (&str, &str, &str) {
        let n = str.rfind("#").unwrap();
        assert!(n != 0);
        assert!(n != str.len() - 1);
        let seq_arraypath_str = &str[(n + 1)..];
        let m = seq_arraypath_str.find(",").unwrap();

        (&str[..n], &seq_arraypath_str[..m], &seq_arraypath_str[m + 1..])
    }

    /* parses a seq and array path portion (ex "123,0,0,10) of a key into a doc result */
    pub fn parse_doc_result_from_key(str: &str) -> DocResult {
        let mut dr = DocResult::new();
        let (_path_str, seq_str, arraypath_str) =
                KeyBuilder::split_keypath_seq_arraypath_from_key(&str);
        dr.seq = seq_str.parse().unwrap();
        if !arraypath_str.is_empty() {
            for numstr in arraypath_str.split(",") {
                dr.arraypath.push(numstr.parse().unwrap());
            }
        }
        dr
    }

    pub fn compare_keys(akey: &str, bkey: &str) -> i32 {
        use std::cmp::Ordering;
        debug_assert!(akey.starts_with('W'));
        debug_assert!(bkey.starts_with('W'));
        let (apath_str, aseq_str, aarraypath_str) =
                KeyBuilder::split_keypath_seq_arraypath_from_key(&akey);
        let (bpath_str, bseq_str, barraypath_str) =
                KeyBuilder::split_keypath_seq_arraypath_from_key(&bkey);

        match apath_str[1..].cmp(&bpath_str[1..]) {
            Ordering::Less => -1,
            Ordering::Greater => 1,
            Ordering::Equal => {
                let aseq: u64 = aseq_str.parse().unwrap();
                let bseq: u64 = bseq_str.parse().unwrap();;
                if aseq < bseq {
                    -1
                } else if aseq > bseq {
                    1
                } else {
                    if aarraypath_str.is_empty() || barraypath_str.is_empty() {
                        match aarraypath_str.len().cmp(&barraypath_str.len()) {
                            Ordering::Less => -1,
                            Ordering::Greater => 1,
                            Ordering::Equal => 0,
                        }
                    } else {
                        let mut a_nums = aarraypath_str.split(",");
                        let mut b_nums = barraypath_str.split(",");
                        loop {
                            if let Some(ref a_num_str) = a_nums.next() {
                                if let Some(ref b_num_str) = b_nums.next() {
                                    let a_num: u64 = a_num_str.parse().unwrap();
                                    let b_num: u64 = b_num_str.parse().unwrap();
                                    match a_num.cmp(&b_num) {
                                        Ordering::Less => return -1,
                                        Ordering::Greater => return 1,
                                        Ordering::Equal => (),
                                    }
                                } else {
                                    //b is shorter than a, so greater
                                    return 1;
                                }
                            } else {
                                if b_nums.next().is_some() {
                                    //a is shorter than b so less
                                    return -1;
                                } else {
                                    // same length and must have hit all equal before this, so equal
                                    return 0;
                                }
                            }
                        }
                    }
                }
            },
        }
    }

}

#[cfg(test)]
mod tests {
    use super::{KeyBuilder};
    use query::DocResult;

    #[test]
    fn test_segments_push() {
        let mut kb = KeyBuilder::new();
        assert_eq!(kb.keypath_segments_len(), 0, "No segments so far");

        kb.push_object_key("first");
        assert_eq!(kb.keypath_segments_len(), 1, "One segment");

        kb.push_object_key("second");
        assert_eq!(kb.keypath_segments_len(), 2, "Two segments");

        kb.push_array();
        assert_eq!(kb.keypath_segments_len(), 3, "Three segments ");
    }

    #[test]
    fn test_segments_pop() {
        let mut kb = KeyBuilder::new();
        kb.push_object_key("first");
        kb.push_object_key("second");
        kb.push_array();

        assert_eq!(kb.keypath_segments_len(), 3, "three segments");
        assert_eq!(kb.stemmed_word_key("astemmedword", 123), "W.first.second$!astemmedword#123,0",
                   "Key for six segments is correct");

        kb.pop_array();
        assert_eq!(kb.keypath_segments_len(), 2, "Two segments");

        kb.pop_object_key();
        assert_eq!(kb.keypath_segments_len(), 1, "One segment");

        kb.pop_object_key();
        assert_eq!(kb.keypath_segments_len(), 0, "No segments so far");
    }

    #[test]
    fn test_segments_canonical() {
        let mut kb = KeyBuilder::new();
        kb.push_object_key("\u{0041}\u{030A}");
        assert_eq!(kb.stemmed_word_key("word", 1), "W.Å!word#1,");
    }

    #[test]
    fn test_doc_result_parse() {
        let key = "W.foo$.bar$!word#123,1,0".to_string();
        let (keypathstr, seqstr, arraypathstr) = KeyBuilder::split_keypath_seq_arraypath_from_key(&key);
        assert_eq!(keypathstr, "W.foo$.bar$!word");
        assert_eq!(seqstr, "123");
        assert_eq!(arraypathstr, "1,0");

        // make sure escaped commas and # in key path don't cause problems
        let key1 = "W.foo\\#$.bar\\,$!word#123,2,0".to_string();
        let (keypathstr1, seqstr1, arraypathstr1) = KeyBuilder::split_keypath_seq_arraypath_from_key(&key1);
        assert_eq!(keypathstr1, "W.foo\\#$.bar\\,$!word");
        assert_eq!(seqstr1, "123");
        assert_eq!(arraypathstr1, "2,0");

        let mut dr = DocResult::new();
        dr.seq = 123;
        dr.arraypath = vec![1,0];

        assert!(dr == KeyBuilder::parse_doc_result_from_key(&key));
    }
}
<|endoftext|>


<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Debugging code to test the state of the dependency graph just
//! after it is loaded from disk. For each node marked with
//! `#[rustc_clean]` or `#[rustc_dirty]`, we will check that a
//! suitable node for that item either appears or does not appear in
//! the dep-graph, as appropriate:
//!
//! - `#[rustc_dirty(label="TypeckItemBody", cfg="rev2")]` if we are
//!   in `#[cfg(rev2)]`, then there MUST NOT be a node
//!   `DepNode::TypeckItemBody(X)` where `X` is the def-id of the
//!   current node.
//! - `#[rustc_clean(label="TypeckItemBody", cfg="rev2")]` same as above,
//!   except that the node MUST exist.
//!
//! Errors are reported if we are in the suitable configuration but
//! the required condition is not met.

use super::directory::RetracedDefIdDirectory;
use super::load::DirtyNodes;
use rustc::dep_graph::{DepGraphQuery, DepNode};
use rustc::hir;
use rustc::hir::def_id::DefId;
use rustc::hir::intravisit::Visitor;
use rustc_data_structures::fnv::FnvHashSet;
use syntax::ast::{self, Attribute, MetaItem};
use syntax::attr::AttrMetaMethods;
use syntax::parse::token::InternedString;
use rustc::ty::TyCtxt;

const DIRTY: &'static str = "rustc_dirty";
const CLEAN: &'static str = "rustc_clean";
const LABEL: &'static str = "label";
const CFG: &'static str = "cfg";

pub fn check_dirty_clean_annotations<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                               dirty_inputs: &DirtyNodes,
                                               retraced: &RetracedDefIdDirectory) {
    // can't add `#[rustc_dirty]` etc without opting in to this feature
    if !tcx.sess.features.borrow().rustc_attrs {
        return;
    }

    let _ignore = tcx.dep_graph.in_ignore();
    let dirty_inputs: FnvHashSet<DepNode<DefId>> =
        dirty_inputs.iter()
                   .filter_map(|d| retraced.map(d))
                   .collect();
    let query = tcx.dep_graph.query();
    debug!("query-nodes: {:?}", query.nodes());
    let krate = tcx.map.krate();
    krate.visit_all_items(&mut DirtyCleanVisitor {
        tcx: tcx,
        query: &query,
        dirty_inputs: dirty_inputs,
    });
}

pub struct DirtyCleanVisitor<'a, 'tcx:'a> {
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    query: &'a DepGraphQuery<DefId>,
    dirty_inputs: FnvHashSet<DepNode<DefId>>,
}

impl<'a, 'tcx> DirtyCleanVisitor<'a, 'tcx> {
    fn expect_associated_value(&self, item: &MetaItem) -> InternedString {
        if let Some(value) = item.value_str() {
            value
        } else {
            self.tcx.sess.span_fatal(
                item.span,
                &format!("associated value expected for `{}`", item.name()));
        }
    }

    /// Given a `#[rustc_dirty]` or `#[rustc_clean]` attribute, scan
    /// for a `cfg="foo"` attribute and check whether we have a cfg
    /// flag called `foo`.
    fn check_config(&self, attr: &ast::Attribute) -> bool {
        debug!("check_config(attr={:?})", attr);
        let config = &self.tcx.map.krate().config;
        debug!("check_config: config={:?}", config);
        for item in attr.meta_item_list().unwrap_or(&[]) {
            if item.check_name(CFG) {
                let value = self.expect_associated_value(item);
                debug!("check_config: searching for cfg {:?}", value);
                for cfg in &config[..] {
                    if cfg.check_name(&value[..]) {
                        debug!("check_config: matched {:?}", cfg);
                        return true;
                    }
                }
                return false;
            }
        }

        self.tcx.sess.span_fatal(
            attr.span,
            &format!("no cfg attribute"));
    }

    fn dep_node(&self, attr: &Attribute, def_id: DefId) -> DepNode<DefId> {
        for item in attr.meta_item_list().unwrap_or(&[]) {
            if item.check_name(LABEL) {
                let value = self.expect_associated_value(item);
                match DepNode::from_label_string(&value[..], def_id) {
                    Ok(def_id) => return def_id,
                    Err(()) => {
                        self.tcx.sess.span_fatal(
                            item.span,
                            &format!("dep-node label `{}` not recognized", value));
                    }
                }
            }
        }

        self.tcx.sess.span_fatal(attr.span, "no `label` found");
    }

    fn dep_node_str(&self, dep_node: &DepNode<DefId>) -> DepNode<String> {
        dep_node.map_def(|&def_id| Some(self.tcx.item_path_str(def_id))).unwrap()
    }

    fn assert_dirty(&self, item: &hir::Item, dep_node: DepNode<DefId>) {
        debug!("assert_dirty({:?})", dep_node);

        match dep_node {
            DepNode::Hir(_) => {
                // HIR nodes are inputs, so if we are asserting that the HIR node is
                // dirty, we check the dirty input set.
                if !self.dirty_inputs.contains(&dep_node) {
                    let dep_node_str = self.dep_node_str(&dep_node);
                    self.tcx.sess.span_err(
                        item.span,
                        &format!("`{:?}` not found in dirty set, but should be dirty", dep_node_str));
                }
            }
            _ => {
                // Other kinds of nodes would be targets, so check if
                // the dep-graph contains the node.
                if self.query.contains_node(&dep_node) {
                    let dep_node_str = self.dep_node_str(&dep_node);
                    self.tcx.sess.span_err(
                        item.span,
                        &format!("`{:?}` found in dep graph, but should be dirty", dep_node_str));
                }
            }
        }
    }

    fn assert_clean(&self, item: &hir::Item, dep_node: DepNode<DefId>) {
        debug!("assert_clean({:?})", dep_node);

        match dep_node {
            DepNode::Hir(_) => {
                // For HIR nodes, check the inputs.
                if self.dirty_inputs.contains(&dep_node) {
                    let dep_node_str = self.dep_node_str(&dep_node);
                    self.tcx.sess.span_err(
                        item.span,
                        &format!("`{:?}` found in dirty-node set, but should be clean", dep_node_str));
                }
            }
            _ => {
                // Otherwise, check if the dep-node exists.
                if !self.query.contains_node(&dep_node) {
                    let dep_node_str = self.dep_node_str(&dep_node);
                    self.tcx.sess.span_err(
                        item.span,
                        &format!("`{:?}` not found in dep graph, but should be clean", dep_node_str));
                }
            }
        }
    }
}

impl<'a, 'tcx> Visitor<'tcx> for DirtyCleanVisitor<'a, 'tcx> {
    fn visit_item(&mut self, item: &'tcx hir::Item) {
        let def_id = self.tcx.map.local_def_id(item.id);
        for attr in self.tcx.get_attrs(def_id).iter() {
            if attr.check_name(DIRTY) {
                if self.check_config(attr) {
                    self.assert_dirty(item, self.dep_node(attr, def_id));
                }
            } else if attr.check_name(CLEAN) {
                if self.check_config(attr) {
                    self.assert_clean(item, self.dep_node(attr, def_id));
                }
            }
        }
    }
}

<commit_msg>pacify the merciless tidy<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Debugging code to test the state of the dependency graph just
//! after it is loaded from disk. For each node marked with
//! `#[rustc_clean]` or `#[rustc_dirty]`, we will check that a
//! suitable node for that item either appears or does not appear in
//! the dep-graph, as appropriate:
//!
//! - `#[rustc_dirty(label="TypeckItemBody", cfg="rev2")]` if we are
//!   in `#[cfg(rev2)]`, then there MUST NOT be a node
//!   `DepNode::TypeckItemBody(X)` where `X` is the def-id of the
//!   current node.
//! - `#[rustc_clean(label="TypeckItemBody", cfg="rev2")]` same as above,
//!   except that the node MUST exist.
//!
//! Errors are reported if we are in the suitable configuration but
//! the required condition is not met.

use super::directory::RetracedDefIdDirectory;
use super::load::DirtyNodes;
use rustc::dep_graph::{DepGraphQuery, DepNode};
use rustc::hir;
use rustc::hir::def_id::DefId;
use rustc::hir::intravisit::Visitor;
use rustc_data_structures::fnv::FnvHashSet;
use syntax::ast::{self, Attribute, MetaItem};
use syntax::attr::AttrMetaMethods;
use syntax::parse::token::InternedString;
use rustc::ty::TyCtxt;

const DIRTY: &'static str = "rustc_dirty";
const CLEAN: &'static str = "rustc_clean";
const LABEL: &'static str = "label";
const CFG: &'static str = "cfg";

pub fn check_dirty_clean_annotations<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                               dirty_inputs: &DirtyNodes,
                                               retraced: &RetracedDefIdDirectory) {
    // can't add `#[rustc_dirty]` etc without opting in to this feature
    if !tcx.sess.features.borrow().rustc_attrs {
        return;
    }

    let _ignore = tcx.dep_graph.in_ignore();
    let dirty_inputs: FnvHashSet<DepNode<DefId>> =
        dirty_inputs.iter()
                   .filter_map(|d| retraced.map(d))
                   .collect();
    let query = tcx.dep_graph.query();
    debug!("query-nodes: {:?}", query.nodes());
    let krate = tcx.map.krate();
    krate.visit_all_items(&mut DirtyCleanVisitor {
        tcx: tcx,
        query: &query,
        dirty_inputs: dirty_inputs,
    });
}

pub struct DirtyCleanVisitor<'a, 'tcx:'a> {
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    query: &'a DepGraphQuery<DefId>,
    dirty_inputs: FnvHashSet<DepNode<DefId>>,
}

impl<'a, 'tcx> DirtyCleanVisitor<'a, 'tcx> {
    fn expect_associated_value(&self, item: &MetaItem) -> InternedString {
        if let Some(value) = item.value_str() {
            value
        } else {
            self.tcx.sess.span_fatal(
                item.span,
                &format!("associated value expected for `{}`", item.name()));
        }
    }

    /// Given a `#[rustc_dirty]` or `#[rustc_clean]` attribute, scan
    /// for a `cfg="foo"` attribute and check whether we have a cfg
    /// flag called `foo`.
    fn check_config(&self, attr: &ast::Attribute) -> bool {
        debug!("check_config(attr={:?})", attr);
        let config = &self.tcx.map.krate().config;
        debug!("check_config: config={:?}", config);
        for item in attr.meta_item_list().unwrap_or(&[]) {
            if item.check_name(CFG) {
                let value = self.expect_associated_value(item);
                debug!("check_config: searching for cfg {:?}", value);
                for cfg in &config[..] {
                    if cfg.check_name(&value[..]) {
                        debug!("check_config: matched {:?}", cfg);
                        return true;
                    }
                }
                return false;
            }
        }

        self.tcx.sess.span_fatal(
            attr.span,
            &format!("no cfg attribute"));
    }

    fn dep_node(&self, attr: &Attribute, def_id: DefId) -> DepNode<DefId> {
        for item in attr.meta_item_list().unwrap_or(&[]) {
            if item.check_name(LABEL) {
                let value = self.expect_associated_value(item);
                match DepNode::from_label_string(&value[..], def_id) {
                    Ok(def_id) => return def_id,
                    Err(()) => {
                        self.tcx.sess.span_fatal(
                            item.span,
                            &format!("dep-node label `{}` not recognized", value));
                    }
                }
            }
        }

        self.tcx.sess.span_fatal(attr.span, "no `label` found");
    }

    fn dep_node_str(&self, dep_node: &DepNode<DefId>) -> DepNode<String> {
        dep_node.map_def(|&def_id| Some(self.tcx.item_path_str(def_id))).unwrap()
    }

    fn assert_dirty(&self, item: &hir::Item, dep_node: DepNode<DefId>) {
        debug!("assert_dirty({:?})", dep_node);

        match dep_node {
            DepNode::Hir(_) => {
                // HIR nodes are inputs, so if we are asserting that the HIR node is
                // dirty, we check the dirty input set.
                if !self.dirty_inputs.contains(&dep_node) {
                    let dep_node_str = self.dep_node_str(&dep_node);
                    self.tcx.sess.span_err(
                        item.span,
                        &format!("`{:?}` not found in dirty set, but should be dirty",
                                 dep_node_str));
                }
            }
            _ => {
                // Other kinds of nodes would be targets, so check if
                // the dep-graph contains the node.
                if self.query.contains_node(&dep_node) {
                    let dep_node_str = self.dep_node_str(&dep_node);
                    self.tcx.sess.span_err(
                        item.span,
                        &format!("`{:?}` found in dep graph, but should be dirty", dep_node_str));
                }
            }
        }
    }

    fn assert_clean(&self, item: &hir::Item, dep_node: DepNode<DefId>) {
        debug!("assert_clean({:?})", dep_node);

        match dep_node {
            DepNode::Hir(_) => {
                // For HIR nodes, check the inputs.
                if self.dirty_inputs.contains(&dep_node) {
                    let dep_node_str = self.dep_node_str(&dep_node);
                    self.tcx.sess.span_err(
                        item.span,
                        &format!("`{:?}` found in dirty-node set, but should be clean",
                                 dep_node_str));
                }
            }
            _ => {
                // Otherwise, check if the dep-node exists.
                if !self.query.contains_node(&dep_node) {
                    let dep_node_str = self.dep_node_str(&dep_node);
                    self.tcx.sess.span_err(
                        item.span,
                        &format!("`{:?}` not found in dep graph, but should be clean",
                                 dep_node_str));
                }
            }
        }
    }
}

impl<'a, 'tcx> Visitor<'tcx> for DirtyCleanVisitor<'a, 'tcx> {
    fn visit_item(&mut self, item: &'tcx hir::Item) {
        let def_id = self.tcx.map.local_def_id(item.id);
        for attr in self.tcx.get_attrs(def_id).iter() {
            if attr.check_name(DIRTY) {
                if self.check_config(attr) {
                    self.assert_dirty(item, self.dep_node(attr, def_id));
                }
            } else if attr.check_name(CLEAN) {
                if self.check_config(attr) {
                    self.assert_clean(item, self.dep_node(attr, def_id));
                }
            }
        }
    }
}

<|endoftext|>


<commit_before>use fake::faker::boolean::en::*;
use fake::faker::company::en::*;
use fake::faker::name::en::*;
// use fake::faker::internet::en::*;
// use fake::UuidConfig;
use fake::Dummy;
use fake::{Fake, Faker};

#[derive(Debug, Dummy)]
enum OrderStatus {
    Completed,
    Cancelled,
}

#[derive(Debug, Dummy)]
pub struct Order {
    #[dummy(faker = "1000..")]
    order_id: usize,

    #[dummy(faker = "Name()")]
    customer: String,

    #[dummy(faker = "(Faker, 3..5)")]
    items: Vec<Item>,

    #[dummy(faker = "Boolean(70)")]
    paid: bool,

    status: OrderStatus,

    // #[dummy(faker = "Uuid(UuidConfig::V4())")]
    // uuid: uuid::Uuid,
}

#[derive(Debug, Dummy)]
pub struct Item {
    #[dummy(faker = "1..100")]
    product_id: usize,

    qty: u8,

    #[dummy(faker = "CompanyName()")]
    company: String,
}

#[derive(Debug, Dummy)]
enum Message {
    Quit,
    Move {
        #[dummy(faker = "1..100")]
        x: i32,
        #[dummy(faker = "1..100")]
        y: i32,
    },
    Write(#[dummy(faker = "Buzzword()")] String),
    ChangeColor(
        #[dummy(faker = "1..100")] i32,
        #[dummy(faker = "1..100")] i32,
        #[dummy(faker = "1..100")] i32,
    ),
    Order(Order),
}

#[derive(Debug, Dummy)]
struct UnitStruct;

#[derive(Debug, Dummy)]
struct EmptyStruct {}

// #[derive(Debug, Dummy)]
// enum EmptyEnum {}

#[derive(Debug, Dummy)]
struct NewType(#[dummy(faker = "1..100")] usize, String);

fn main() {
    let order: Order = Faker.fake();
    println!("{:#?}", order);

    let msg: Message = Faker.fake();
    println!("{:#?}", msg);

    let v: UnitStruct = Faker.fake();
    println!("{:#?}", v);

    let v: EmptyStruct = Faker.fake();
    println!("{:#?}", v);

    // let v: EmptyEnum = Faker.fake();
    // println!("{:#?}", v);

    let v: NewType = Faker.fake();
    println!("{:#?}", v);

    let v: uuid::Uuid = Faker.fake();
    println!("{:#?}", v);
}
<commit_msg>uncomment<commit_after>use fake::faker::boolean::en::*;
use fake::faker::company::en::*;
use fake::faker::name::en::*;
use fake::faker::internet::en::*;
use fake::UuidConfig;
use fake::Dummy;
use fake::{Fake, Faker};

#[derive(Debug, Dummy)]
enum OrderStatus {
    Completed,
    Cancelled,
}

#[derive(Debug, Dummy)]
pub struct Order {
    #[dummy(faker = "1000..")]
    order_id: usize,

    #[dummy(faker = "Name()")]
    customer: String,

    #[dummy(faker = "(Faker, 3..5)")]
    items: Vec<Item>,

    #[dummy(faker = "Boolean(70)")]
    paid: bool,

    status: OrderStatus,

    #[dummy(faker = "Uuid(UuidConfig::V4)")]
    uuid: uuid::Uuid,
}

#[derive(Debug, Dummy)]
pub struct Item {
    #[dummy(faker = "1..100")]
    product_id: usize,

    qty: u8,

    #[dummy(faker = "CompanyName()")]
    company: String,
}

#[derive(Debug, Dummy)]
enum Message {
    Quit,
    Move {
        #[dummy(faker = "1..100")]
        x: i32,
        #[dummy(faker = "1..100")]
        y: i32,
    },
    Write(#[dummy(faker = "Buzzword()")] String),
    ChangeColor(
        #[dummy(faker = "1..100")] i32,
        #[dummy(faker = "1..100")] i32,
        #[dummy(faker = "1..100")] i32,
    ),
    Order(Order),
}

#[derive(Debug, Dummy)]
struct UnitStruct;

#[derive(Debug, Dummy)]
struct EmptyStruct {}

// #[derive(Debug, Dummy)]
// enum EmptyEnum {}

#[derive(Debug, Dummy)]
struct NewType(#[dummy(faker = "1..100")] usize, String);

fn main() {
    let order: Order = Faker.fake();
    println!("{:#?}", order);

    let msg: Message = Faker.fake();
    println!("{:#?}", msg);

    let v: UnitStruct = Faker.fake();
    println!("{:#?}", v);

    let v: EmptyStruct = Faker.fake();
    println!("{:#?}", v);

    // let v: EmptyEnum = Faker.fake();
    // println!("{:#?}", v);

    let v: NewType = Faker.fake();
    println!("{:#?}", v);

    let v: uuid::Uuid = Faker.fake();
    println!("{:#?}", v);
}
<|endoftext|>


<commit_before>use lucet_idl::AtomType;
use proptest::prelude::*;

pub trait AtomTypeExt
where
    Self: Sized,
{
    fn strat() -> BoxedStrategy<Self>;
    fn render_idl(&self) -> String;
}

impl AtomTypeExt for AtomType {
    fn strat() -> BoxedStrategy<AtomType> {
        prop_oneof![
            Just(AtomType::Bool),
            Just(AtomType::U8),
            Just(AtomType::U16),
            Just(AtomType::U32),
            Just(AtomType::U64),
            Just(AtomType::I8),
            Just(AtomType::I16),
            Just(AtomType::I32),
            Just(AtomType::I64),
            Just(AtomType::F32),
            Just(AtomType::F64),
        ]
        .boxed()
    }
    fn render_idl(&self) -> String {
        use AtomType::*;
        match self {
            Bool => "bool",
            U8 => "u8",
            U16 => "u16",
            U32 => "u32",
            U64 => "u64",
            I8 => "i8",
            I16 => "i16",
            I32 => "i32",
            I64 => "i64",
            F32 => "f32",
            F64 => "f64",
        }
        .to_owned()
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DatatypeRef(usize);

impl DatatypeRef {
    pub fn strat() -> impl Strategy<Value = Self> {
        any::<usize>().prop_map(DatatypeRef)
    }

    pub fn normalize(self, highest_definition: usize) -> Self {
        assert!(highest_definition != 0);
        DatatypeRef(self.0 % highest_definition)
    }

    pub fn render_idl(&self) -> String {
        format!("dt_{}", self.0)
    }
}

#[derive(Debug, Clone)]
pub enum DatatypeName {
    Atom(AtomType),
    Defined(DatatypeRef),
}

impl DatatypeName {
    pub fn normalize(self, highest_definition: usize) -> Self {
        match self {
            DatatypeName::Defined(def) => {
                if highest_definition == 0 {
                    DatatypeName::Atom(AtomType::I64)
                } else {
                    DatatypeName::Defined(def.normalize(highest_definition))
                }
            }
            DatatypeName::Atom(a) => DatatypeName::Atom(a.clone()),
        }
    }

    pub fn strat() -> impl Strategy<Value = Self> {
        prop_oneof![
            DatatypeRef::strat().prop_map(DatatypeName::Defined),
            AtomType::strat().prop_map(DatatypeName::Atom)
        ]
    }

    pub fn render_idl(&self) -> String {
        match self {
            DatatypeName::Atom(a) => a.render_idl(),
            DatatypeName::Defined(d) => d.render_idl(),
        }
    }
}

#[derive(Debug, Clone)]
pub struct EnumSyntax {
    pub variants: usize,
}

impl EnumSyntax {
    pub fn strat() -> impl Strategy<Value = Self> {
        (1..20usize).prop_map(|variants| EnumSyntax { variants })
    }

    pub fn render_idl(&self, name: usize) -> String {
        let mut s = String::new();
        for v in 0..self.variants {
            s += &format!("v_{}, ", v);
        }
        format!("enum dt_{} {{ {} }} ", name, s)
    }
}

#[derive(Debug, Clone)]
pub struct StructSyntax {
    pub members: Vec<DatatypeName>,
}

impl StructSyntax {
    pub fn strat() -> impl Strategy<Value = Self> {
        prop::collection::vec(DatatypeName::strat(), 1..10)
            .prop_map(|members| StructSyntax { members })
    }

    pub fn normalize(self, highest_definition: usize) -> Self {
        let members = self
            .members
            .into_iter()
            .map(|m| m.normalize(highest_definition))
            .collect();
        Self { members }
    }

    pub fn render_idl(&self, name: usize) -> String {
        let mut s = String::new();
        for (ix, m) in self.members.iter().enumerate() {
            s += &format!("m_{}: {}, ", ix, m.render_idl());
        }
        format!("struct dt_{} {{ {} }} ", name, s)
    }
}

#[derive(Debug, Clone)]
pub struct AliasSyntax {
    pub target: DatatypeName,
}

impl AliasSyntax {
    pub fn strat() -> impl Strategy<Value = Self> {
        DatatypeName::strat().prop_map(|target| AliasSyntax { target })
    }
    pub fn normalize(self, highest_definition: usize) -> Self {
        Self {
            target: self.target.normalize(highest_definition),
        }
    }
    pub fn render_idl(&self, name: usize) -> String {
        format!("type dt_{} = {};", name, self.target.render_idl())
    }
}

#[derive(Debug, Clone)]
pub enum DatatypeSyntax {
    Enum(EnumSyntax),
    Struct(StructSyntax),
    Alias(AliasSyntax),
}

impl DatatypeSyntax {
    pub fn strat() -> impl Strategy<Value = Self> {
        prop_oneof![
            EnumSyntax::strat().prop_map(DatatypeSyntax::Enum),
            StructSyntax::strat().prop_map(DatatypeSyntax::Struct),
            AliasSyntax::strat().prop_map(DatatypeSyntax::Alias),
        ]
    }

    pub fn normalize(self, highest_definition: usize) -> Self {
        match self {
            DatatypeSyntax::Enum(e) => DatatypeSyntax::Enum(e.clone()),
            DatatypeSyntax::Struct(s) => DatatypeSyntax::Struct(s.normalize(highest_definition)),
            DatatypeSyntax::Alias(a) => DatatypeSyntax::Alias(a.normalize(highest_definition)),
        }
    }

    pub fn render_idl(&self, name: usize) -> String {
        match self {
            DatatypeSyntax::Enum(e) => e.render_idl(name),
            DatatypeSyntax::Struct(s) => s.render_idl(name),
            DatatypeSyntax::Alias(a) => a.render_idl(name),
        }
    }
}

#[derive(Debug, Clone)]
pub struct FunctionSyntax {
    args: Vec<DatatypeRef>,
    ret: Option<DatatypeRef>,
}

impl FunctionSyntax {
    pub fn strat(max_args: usize) -> impl Strategy<Value = Self> {
        (
            prop::collection::vec(DatatypeRef::strat(), 0..max_args),
            prop::option::of(DatatypeRef::strat()),
        )
            .prop_map(|(args, ret)| FunctionSyntax { args, ret })
    }

    pub fn normalize(self, highest_definition: usize) -> Self {
        let args = self
            .args
            .into_iter()
            .map(|a| a.normalize(highest_definition))
            .collect();
        let ret = self.ret.clone().map(|a| a.normalize(highest_definition));
        Self { args, ret }
    }

    pub fn render_idl(&self, name: usize) -> String {
        let mut args = String::new();
        for (ix, a) in self.args.iter().enumerate() {
            args += &format!("a_{}: {}, ", ix, a.render_idl());
        }
        let mut ret = String::new();
        if let Some(ref r) = self.ret {
            ret += &format!("-> {}", r.render_idl());
        }
        format!("fn f_{}({}){};", name, args, ret)
    }
}

#[derive(Debug, Clone)]
pub struct Spec {
    pub datatype_decls: Vec<DatatypeSyntax>,
    pub function_decls: Vec<FunctionSyntax>,
}

impl Spec {
    pub fn strat(max_size: usize) -> impl Strategy<Value = Self> {
        (
            prop::collection::vec(DatatypeSyntax::strat(), 1..max_size),
            prop::collection::vec(FunctionSyntax::strat(max_size), 1..max_size),
        )
            .prop_map(|(ds, fs)| Self::from_decls(ds, fs))
    }

    pub fn from_decls(
        datatype_decls: Vec<DatatypeSyntax>,
        function_decls: Vec<FunctionSyntax>,
    ) -> Self {
        let datatype_decls: Vec<DatatypeSyntax> = datatype_decls
            .into_iter()
            .enumerate()
            .map(|(ix, decl)| decl.normalize(ix))
            .collect();
        let num_datatypes = datatype_decls.len();
        let function_decls = function_decls
            .into_iter()
            .map(|decl| decl.normalize(num_datatypes))
            .collect();
        Spec {
            datatype_decls,
            function_decls,
        }
    }

    pub fn render_idl(&self) -> String {
        let mut s = String::new();
        for (ix, d) in self.datatype_decls.iter().enumerate() {
            s += &format!("    {}\n", d.render_idl(ix));
        }
        for (ix, d) in self.function_decls.iter().enumerate() {
            s += &format!("    {}\n", d.render_idl(ix));
        }
        format!("mod spec {{\n{}\n}}", s)
    }
}
<commit_msg>lucet-idl-test: comments<commit_after>use lucet_idl::AtomType;
use proptest::prelude::*;

pub trait AtomTypeExt
where
    Self: Sized,
{
    fn strat() -> BoxedStrategy<Self>;
    fn render_idl(&self) -> String;
}

impl AtomTypeExt for AtomType {
    fn strat() -> BoxedStrategy<AtomType> {
        prop_oneof![
            Just(AtomType::Bool),
            Just(AtomType::U8),
            Just(AtomType::U16),
            Just(AtomType::U32),
            Just(AtomType::U64),
            Just(AtomType::I8),
            Just(AtomType::I16),
            Just(AtomType::I32),
            Just(AtomType::I64),
            Just(AtomType::F32),
            Just(AtomType::F64),
        ]
        .boxed()
    }
    fn render_idl(&self) -> String {
        use AtomType::*;
        match self {
            Bool => "bool",
            U8 => "u8",
            U16 => "u16",
            U32 => "u32",
            U64 => "u64",
            I8 => "i8",
            I16 => "i16",
            I32 => "i32",
            I64 => "i64",
            F32 => "f32",
            F64 => "f64",
        }
        .to_owned()
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DatatypeRef(usize);

impl DatatypeRef {
    pub fn strat() -> impl Strategy<Value = Self> {
        any::<usize>().prop_map(DatatypeRef)
    }

    pub fn normalize(self, highest_definition: usize) -> Self {
        assert!(highest_definition != 0);
        DatatypeRef(self.0 % highest_definition)
    }

    pub fn render_idl(&self) -> String {
        format!("dt_{}", self.0)
    }
}

#[derive(Debug, Clone)]
pub enum DatatypeName {
    Atom(AtomType),
    Defined(DatatypeRef),
}

impl DatatypeName {
    pub fn normalize(self, highest_definition: usize) -> Self {
        match self {
            DatatypeName::Defined(def) => {
                if highest_definition == 0 {
                    // No defined type to normalize to - instead use an atom type.
                    DatatypeName::Atom(AtomType::I64)
                } else {
                    DatatypeName::Defined(def.normalize(highest_definition))
                }
            }
            DatatypeName::Atom(a) => DatatypeName::Atom(a.clone()),
        }
    }

    pub fn strat() -> impl Strategy<Value = Self> {
        prop_oneof![
            DatatypeRef::strat().prop_map(DatatypeName::Defined),
            AtomType::strat().prop_map(DatatypeName::Atom)
        ]
    }

    pub fn render_idl(&self) -> String {
        match self {
            DatatypeName::Atom(a) => a.render_idl(),
            DatatypeName::Defined(d) => d.render_idl(),
        }
    }
}

#[derive(Debug, Clone)]
pub struct EnumSyntax {
    pub variants: usize,
}

impl EnumSyntax {
    pub fn strat() -> impl Strategy<Value = Self> {
        // up to 20 variants for now. probably want to allow more in the future?
        (1..20usize).prop_map(|variants| EnumSyntax { variants })
    }

    pub fn render_idl(&self, name: usize) -> String {
        let mut s = String::new();
        for v in 0..self.variants {
            s += &format!("v_{}, ", v);
        }
        format!("enum dt_{} {{ {} }} ", name, s)
    }
}

#[derive(Debug, Clone)]
pub struct StructSyntax {
    pub members: Vec<DatatypeName>,
}

impl StructSyntax {
    pub fn strat() -> impl Strategy<Value = Self> {
        prop::collection::vec(DatatypeName::strat(), 1..10)
            .prop_map(|members| StructSyntax { members })
    }

    pub fn normalize(self, highest_definition: usize) -> Self {
        let members = self
            .members
            .into_iter()
            .map(|m| m.normalize(highest_definition))
            .collect();
        Self { members }
    }

    pub fn render_idl(&self, name: usize) -> String {
        let mut s = String::new();
        for (ix, m) in self.members.iter().enumerate() {
            s += &format!("m_{}: {}, ", ix, m.render_idl());
        }
        format!("struct dt_{} {{ {} }} ", name, s)
    }
}

#[derive(Debug, Clone)]
pub struct AliasSyntax {
    pub target: DatatypeName,
}

impl AliasSyntax {
    pub fn strat() -> impl Strategy<Value = Self> {
        DatatypeName::strat().prop_map(|target| AliasSyntax { target })
    }
    pub fn normalize(self, highest_definition: usize) -> Self {
        Self {
            target: self.target.normalize(highest_definition),
        }
    }
    pub fn render_idl(&self, name: usize) -> String {
        format!("type dt_{} = {};", name, self.target.render_idl())
    }
}

#[derive(Debug, Clone)]
pub enum DatatypeSyntax {
    Enum(EnumSyntax),
    Struct(StructSyntax),
    Alias(AliasSyntax),
}

impl DatatypeSyntax {
    pub fn strat() -> impl Strategy<Value = Self> {
        prop_oneof![
            EnumSyntax::strat().prop_map(DatatypeSyntax::Enum),
            StructSyntax::strat().prop_map(DatatypeSyntax::Struct),
            AliasSyntax::strat().prop_map(DatatypeSyntax::Alias),
        ]
    }

    pub fn normalize(self, highest_definition: usize) -> Self {
        match self {
            DatatypeSyntax::Enum(e) => DatatypeSyntax::Enum(e.clone()),
            DatatypeSyntax::Struct(s) => DatatypeSyntax::Struct(s.normalize(highest_definition)),
            DatatypeSyntax::Alias(a) => DatatypeSyntax::Alias(a.normalize(highest_definition)),
        }
    }

    pub fn render_idl(&self, name: usize) -> String {
        match self {
            DatatypeSyntax::Enum(e) => e.render_idl(name),
            DatatypeSyntax::Struct(s) => s.render_idl(name),
            DatatypeSyntax::Alias(a) => a.render_idl(name),
        }
    }
}

#[derive(Debug, Clone)]
pub struct FunctionSyntax {
    args: Vec<DatatypeRef>,
    ret: Option<DatatypeRef>,
}

impl FunctionSyntax {
    pub fn strat(max_args: usize) -> impl Strategy<Value = Self> {
        (
            prop::collection::vec(DatatypeRef::strat(), 0..max_args),
            prop::option::of(DatatypeRef::strat()),
        )
            .prop_map(|(args, ret)| FunctionSyntax { args, ret })
    }

    pub fn normalize(self, highest_definition: usize) -> Self {
        let args = self
            .args
            .into_iter()
            .map(|a| a.normalize(highest_definition))
            .collect();
        let ret = self.ret.clone().map(|a| a.normalize(highest_definition));
        Self { args, ret }
    }

    pub fn render_idl(&self, name: usize) -> String {
        let mut args = String::new();
        for (ix, a) in self.args.iter().enumerate() {
            args += &format!("a_{}: {}, ", ix, a.render_idl());
        }
        let mut ret = String::new();
        if let Some(ref r) = self.ret {
            ret += &format!("-> {}", r.render_idl());
        }
        format!("fn f_{}({}){};", name, args, ret)
    }
}

#[derive(Debug, Clone)]
pub struct Spec {
    pub datatype_decls: Vec<DatatypeSyntax>,
    pub function_decls: Vec<FunctionSyntax>,
}

impl Spec {
    pub fn strat(max_size: usize) -> impl Strategy<Value = Self> {
        (
            prop::collection::vec(DatatypeSyntax::strat(), 1..max_size),
            prop::collection::vec(FunctionSyntax::strat(max_size), 1..max_size),
        )
            .prop_map(|(ds, fs)| Self::from_decls(ds, fs))
    }

    pub fn from_decls(
        datatype_decls: Vec<DatatypeSyntax>,
        function_decls: Vec<FunctionSyntax>,
    ) -> Self {
        let datatype_decls: Vec<DatatypeSyntax> = datatype_decls
            .into_iter()
            .enumerate()
            .map(|(ix, decl)| decl.normalize(ix))
            .collect();
        let num_datatypes = datatype_decls.len();
        let function_decls = function_decls
            .into_iter()
            .map(|decl| decl.normalize(num_datatypes))
            .collect();
        Spec {
            datatype_decls,
            function_decls,
        }
    }

    pub fn render_idl(&self) -> String {
        let mut s = String::new();
        for (ix, d) in self.datatype_decls.iter().enumerate() {
            s += &format!("    {}\n", d.render_idl(ix));
        }
        for (ix, d) in self.function_decls.iter().enumerate() {
            s += &format!("    {}\n", d.render_idl(ix));
        }
        format!("mod spec {{\n{}\n}}", s)
    }
}
<|endoftext|>