datagen-rs 0.1.0

A random data generator written in rust
Documentation
use crate::schema::reference::Reference;
use crate::schema::transform::Transform;
#[cfg(feature = "schema")]
use schemars::JsonSchema;
#[cfg(feature = "serialize")]
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;

#[derive(Debug, Clone)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[cfg_attr(
    feature = "serialize",
    serde(deny_unknown_fields, tag = "type", rename_all = "camelCase")
)]
pub enum StringGenerator {
    Uuid,
    Email,
    FirstName,
    LastName,
    FullName,
    Username,
    City,
    Country,
    CountryCode,
    Street,
    State,
    ZipCode,
    Latitude,
    Longitude,
    Phone,
    #[cfg_attr(feature = "serialize", serde(rename_all = "camelCase"))]
    Format {
        format: String,
        args: BTreeMap<String, FormatArg>,
        serialize_non_strings: Option<bool>,
    },
}

#[derive(Debug, Clone)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serialize", serde(untagged, deny_unknown_fields))]
pub enum FormatArg {
    String(String),
    StringSchema(StringSchema),
    Integer(i32),
    Number(f64),
    Reference(Reference),
}

#[derive(Debug, Clone)]
#[cfg_attr(feature = "schema", derive(JsonSchema))]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
#[cfg_attr(
    feature = "serialize",
    serde(untagged, deny_unknown_fields, rename = "string")
)]
pub enum StringSchema {
    Generated {
        generator: StringGenerator,
        transform: Option<Vec<Transform>>,
    },
    Constant {
        value: String,
        transform: Option<Vec<Transform>>,
    },
}

#[cfg(feature = "generate")]
pub mod generate {
    use crate::generate::current_schema::CurrentSchemaRef;
    use crate::generate::generated_schema::generate::{IntoGenerated, IntoGeneratedArc};
    use crate::generate::generated_schema::{GeneratedSchema, IntoRandom};
    use crate::schema::string::{FormatArg, StringGenerator, StringSchema};
    use crate::schema::transform::Transform;
    use crate::util::types::Result;
    use fake::faker::address::en::{
        CityName, CountryCode, CountryName, Latitude, Longitude, StateName, StreetName, ZipCode,
    };
    use fake::faker::internet::en::{FreeEmail, Username};
    use fake::faker::name::en::{FirstName, LastName, Name};
    use fake::faker::phone_number::en::PhoneNumber;
    use fake::uuid::UUIDv4;
    use fake::Fake;
    use handlebars::Handlebars;
    use std::collections::HashMap;
    use std::sync::Arc;

    impl IntoGeneratedArc for StringSchema {
        fn into_generated_arc(self, schema: CurrentSchemaRef) -> Result<Arc<GeneratedSchema>> {
            match self {
                StringSchema::Constant { value, .. } => schema.resolve_ref(value)?.into_random(),
                StringSchema::Generated { generator, .. } => generator.into_random(schema),
            }
        }

        fn get_transform(&self) -> Option<Vec<Transform>> {
            match self {
                StringSchema::Constant { transform, .. } => transform.clone(),
                StringSchema::Generated { transform, .. } => transform.clone(),
            }
        }
    }

    impl IntoGenerated for StringGenerator {
        fn into_generated(self, schema: CurrentSchemaRef) -> Result<GeneratedSchema> {
            Ok(match self {
                StringGenerator::Uuid => GeneratedSchema::String(UUIDv4.fake()),
                StringGenerator::Email => GeneratedSchema::String(FreeEmail().fake()),
                StringGenerator::FirstName => GeneratedSchema::String(FirstName().fake()),
                StringGenerator::LastName => GeneratedSchema::String(LastName().fake()),
                StringGenerator::FullName => GeneratedSchema::String(Name().fake()),
                StringGenerator::Username => GeneratedSchema::String(Username().fake()),
                StringGenerator::City => GeneratedSchema::String(CityName().fake()),
                StringGenerator::Country => GeneratedSchema::String(CountryName().fake()),
                StringGenerator::CountryCode => GeneratedSchema::String(CountryCode().fake()),
                StringGenerator::Street => GeneratedSchema::String(StreetName().fake()),
                StringGenerator::State => GeneratedSchema::String(StateName().fake()),
                StringGenerator::ZipCode => GeneratedSchema::String(ZipCode().fake()),
                StringGenerator::Latitude => {
                    GeneratedSchema::Number(Latitude().fake::<f64>().into())
                }
                StringGenerator::Longitude => {
                    GeneratedSchema::Number(Longitude().fake::<f64>().into())
                }
                StringGenerator::Phone => GeneratedSchema::String(PhoneNumber().fake()),
                StringGenerator::Format {
                    format,
                    args,
                    serialize_non_strings,
                } => {
                    let mut hbs = Handlebars::new();
                    hbs.register_template_string("template", format)?;

                    let data = args
                        .into_iter()
                        .map(|(name, arg)| -> Result<(String, Arc<GeneratedSchema>)> {
                            Ok((
                                name,
                                match arg {
                                    FormatArg::Number(num) => {
                                        GeneratedSchema::String(num.to_string()).into()
                                    }
                                    FormatArg::Integer(num) => {
                                        GeneratedSchema::String(num.to_string()).into()
                                    }
                                    FormatArg::String(str) => {
                                        schema.resolve_ref(str)?.into_random()?
                                    }
                                    FormatArg::StringSchema(str) => {
                                        let res = str.into_generated_arc(schema.clone())?;
                                        match res.as_ref() {
                                            GeneratedSchema::Number(num) => {
                                                GeneratedSchema::String(num.to_string()).into()
                                            }
                                            GeneratedSchema::Integer(num) => {
                                                GeneratedSchema::String(num.to_string()).into()
                                            }
                                            _ => res,
                                        }
                                    }
                                    FormatArg::Reference(reference) => {
                                        reference.into_generated_arc(schema.clone())?
                                    }
                                },
                            ))
                        })
                        .collect::<Result<HashMap<_, _>>>()?
                        .into_iter()
                        .map(|(name, arg)| -> Result<(String, String)> {
                            if let GeneratedSchema::String(str) = arg.as_ref() {
                                Ok((name, str.clone()))
                            } else if serialize_non_strings
                                .or(schema.options().serialize_non_strings)
                                .unwrap_or(false)
                            {
                                Ok((name, serde_json::to_string(&arg)?))
                            } else {
                                Err(format!(
                                    "Unable to format non-string value: {}",
                                    serde_json::to_string(&arg)?
                                )
                                .into())
                            }
                        })
                        .collect::<Result<HashMap<_, _>>>()?;

                    GeneratedSchema::String(hbs.render("template", &data)?)
                }
            })
        }

        fn get_transform(&self) -> Option<Vec<Transform>> {
            None
        }

        fn should_finalize(&self) -> bool {
            false
        }
    }
}