diff --git a/Cargo.lock b/Cargo.lock index 714af1cc..15c92b56 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3372,7 +3372,7 @@ dependencies = [ [[package]] name = "synth" -version = "0.4.6" +version = "0.4.7" dependencies = [ "anyhow", "async-std", @@ -3413,7 +3413,7 @@ dependencies = [ [[package]] name = "synth-core" -version = "0.4.5" +version = "0.4.6" dependencies = [ "anyhow", "bimap", diff --git a/core/Cargo.toml b/core/Cargo.toml index 089d0202..ba111066 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "synth-core" -version = "0.4.5" +version = "0.4.6" authors = [ "Damien Broka ", "Christos Hadjiaslanis " diff --git a/core/src/graph/mod.rs b/core/src/graph/mod.rs index 0f57dcc2..3b688279 100644 --- a/core/src/graph/mod.rs +++ b/core/src/graph/mod.rs @@ -64,7 +64,7 @@ pub mod null; pub use null::NullNode; pub mod string; -pub use string::{RandFaker, RandomDateTime, RandomString, StringNode, UuidGen}; +pub use string::{RandFaker, RandomDateTime, RandomString, StringNode, Truncated, UuidGen}; pub mod number; pub use number::{Incrementing, NumberNode, RandomF64, RandomI64, RandomU64, UniformRangeStep}; @@ -355,8 +355,14 @@ pub mod tests { "frequency": 0.2 }, "username": { - "type": "string", - "pattern": "[a-z0-9]{5,15}" + "type": "string", + "truncated": { + "content": { + "type": "string", + "pattern": "[a-zA-Z0-9]{0, 255}" + }, + "length": 5 + } }, "bank_country": { "type": "string", @@ -530,6 +536,7 @@ pub mod tests { println!("bank_country={}", user.bank_country); assert!(&user.bank_country == "GB" || &user.bank_country == "ES"); assert!(user.id >= 100); + assert!(user.username.len() <= 5); all_users.insert(user.username.clone()); currencies.insert(user.username, user.currency); /* diff --git a/core/src/graph/string/mod.rs b/core/src/graph/string/mod.rs index cf146111..ba770c6d 100644 --- a/core/src/graph/string/mod.rs +++ b/core/src/graph/string/mod.rs @@ -7,11 +7,13 @@ pub use date_time::RandomDateTime; pub mod faker; pub mod serialized; +pub mod truncated; pub mod uuid; pub use self::uuid::UuidGen; pub use faker::RandFaker; pub use serialized::Serialized; +pub use truncated::Truncated; derive_generator! { yield String, @@ -22,6 +24,7 @@ derive_generator! { Serialized(TryOnce) Categorical(OnceInfallible>>) Uuid(OnceInfallible) + Truncated(Truncated) } } @@ -55,10 +58,17 @@ impl From for RandomString { } } +impl From for RandomString { + fn from(trunc: Truncated) -> Self { + Self::Truncated(trunc) + } +} + derive_generator! { yield Token, return Result, - pub enum StringNode { + pub enum + StringNode { String(Valuize, String>), DateTime(Valuize, ChronoValue>) } diff --git a/core/src/graph/string/truncated.rs b/core/src/graph/string/truncated.rs new file mode 100644 index 00000000..a426a8c1 --- /dev/null +++ b/core/src/graph/string/truncated.rs @@ -0,0 +1,46 @@ +use crate::graph::prelude::*; +use crate::graph::{RandomString, StringNode}; +use anyhow::Result; + +pub struct Truncated { + len: usize, + inner: Box, +} + +impl Truncated { + pub(crate) fn new(len: usize, graph: Graph) -> Result { + match graph { + Graph::String(StringNode::String(random_string)) => { + let unwrapped = random_string.into_inner().into_inner(); + Ok(Self { + inner: Box::new(unwrapped), + len, + }) + } + _ => Err(anyhow!( + "Truncated generators can only have content of type 'string'." + )), + } + } +} + +impl Generator for Truncated { + type Yield = String; + type Return = Result; + + fn next(&mut self, rng: &mut R) -> GeneratorState { + match self.inner.next(rng) { + GeneratorState::Yielded(mut s) => { + s.truncate(self.len); + GeneratorState::Yielded(s) + } + GeneratorState::Complete(r) => match r { + Ok(mut s) => { + s.truncate(self.len); + GeneratorState::Complete(Ok(s)) + } + Err(e) => GeneratorState::Complete(Err(e)), + }, + } + } +} diff --git a/core/src/schema/content/string.rs b/core/src/schema/content/string.rs index 2724caaf..ad43f9f1 100644 --- a/core/src/schema/content/string.rs +++ b/core/src/schema/content/string.rs @@ -11,6 +11,7 @@ pub enum StringContent { Categorical(Categorical), Serialized(SerializedContent), Uuid(Uuid), + Truncated(TruncatedContent), } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] @@ -25,6 +26,7 @@ impl StringContent { Self::Categorical(_) => "categorical", Self::Serialized(_) => "serialized", Self::Uuid(_) => "uuid", + Self::Truncated(_) => "truncated", } } } @@ -189,6 +191,13 @@ pub enum SerializedContent { JSON(JsonContent), } +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] +#[serde(rename_all = "lowercase")] +pub struct TruncatedContent { + content: Box, + length: usize, +} + #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] pub struct JsonContent { content: Box, @@ -550,6 +559,10 @@ impl Compile for StringContent { RandomString::from(Serialized::new_json(inner)).into() } }, + StringContent::Truncated(trunc) => { + let inner = trunc.content.compile(compiler)?; + RandomString::from(Truncated::new(trunc.length, inner)?).into() + } StringContent::Uuid(_uuid) => RandomString::from(UuidGen {}).into(), }; Ok(Graph::String(string_node)) diff --git a/core/src/schema/inference/mod.rs b/core/src/schema/inference/mod.rs index a0d0bff0..3426e06e 100644 --- a/core/src/schema/inference/mod.rs +++ b/core/src/schema/inference/mod.rs @@ -112,6 +112,7 @@ impl MergeStrategy for OptionalMergeStrategy { StringContent::Faker(_) => Ok(()), StringContent::Serialized(_) => Ok(()), // we can probably do better here StringContent::Uuid(_) => Ok(()), + StringContent::Truncated(_) => Ok(()), } } } diff --git a/default.nix b/default.nix index f6b586a6..203a384b 100644 --- a/default.nix +++ b/default.nix @@ -16,7 +16,7 @@ , release ? true }: let - version = "0.4.6"; + version = "0.4.7"; darwinBuildInputs = stdenv.lib.optionals stdenv.hostPlatform.isDarwin (with darwin.apple_sdk.frameworks; [ libiconv diff --git a/docs/docs/content/string.md b/docs/docs/content/string.md index 66b1079d..f6138da8 100644 --- a/docs/docs/content/string.md +++ b/docs/docs/content/string.md @@ -153,6 +153,31 @@ Accepted values for the `"date_time"` key are objects with the following keys: } ``` +## truncated + +The `truncated` generator truncates the output of it's inner generator to a fixed length. + +If the output of its inner generator is less than or equal to the length, it is left untouched. + +`truncated` has 2 fields, +- `length`: The number of characters to truncate to. +- `content`: The content to be truncated. This can be any Synth generator that yields a String. + +#### Example + +```json synth +{ + "type": "string", + "truncated": { + "content": { + "type": "string", + "pattern": "[a-zA-Z0-9]{0, 255}" + }, + "length": 5 + } +} +``` + ## categorical diff --git a/gen/src/generator/mod.rs b/gen/src/generator/mod.rs index 3eacc8fa..e477abaa 100644 --- a/gen/src/generator/mod.rs +++ b/gen/src/generator/mod.rs @@ -315,6 +315,12 @@ where } } +impl MapComplete { + pub fn into_inner(self) -> G { + self.inner + } +} + /// This `struct` is constructed by the /// [`map_yielded`](crate::GeneratorExt::map_yielded) method on /// [`Generator`](crate::Generator). diff --git a/gen/src/value.rs b/gen/src/value.rs index 105e85e1..20a6cc52 100644 --- a/gen/src/value.rs +++ b/gen/src/value.rs @@ -448,6 +448,16 @@ where inner: G, } +impl Tokenizer +where + G: Generator, + G::Yield: IntoToken, +{ + pub fn into_inner(self) -> G { + self.inner + } +} + impl Generator for Tokenizer where G: Generator, diff --git a/synth/Cargo.toml b/synth/Cargo.toml index fa06713f..c5365b56 100644 --- a/synth/Cargo.toml +++ b/synth/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "synth" -version = "0.4.6" +version = "0.4.7" authors = [ "Damien Broka ", "Christos Hadjiaslanis "