diff --git a/.editorconfig b/.editorconfig index 8deec94..9e0ba60 100644 --- a/.editorconfig +++ b/.editorconfig @@ -6,7 +6,7 @@ indent_size = 4 insert_final_newline = true trim_trailing_whitespace = true -[*.{json,yaml,yml}] +[*.{html,js,json,yaml,yml}] indent_size = 2 [Justfile] diff --git a/.vscode/settings.json b/.vscode/settings.json index 2c63c08..e2bbf0b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,2 +1,13 @@ { + "editor.quickSuggestions": { + "other": "on", + "strings": "on" + }, + "files.associations": { + "**/templates/**/*.html": "jinja-html", + "*.css": "tailwindcss" + }, + "tailwindCSS.includeLanguages": { + "jinja-html": "html" + } } diff --git a/Cargo.lock b/Cargo.lock index e50501e..98402a1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -86,6 +86,46 @@ version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +[[package]] +name = "askama" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b79091df18a97caea757e28cd2d5fda49c6cd4bd01ddffd7ff01ace0c0ad2c28" +dependencies = [ + "askama_derive", + "askama_escape", + "comrak", +] + +[[package]] +name = "askama_derive" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a0fc7dcf8bd4ead96b1d36b41df47c14beedf7b0301fc543d8f2384e66a2ec0" +dependencies = [ + "askama_parser", + "mime", + "mime_guess", + "proc-macro2", + "quote", + "syn 2.0.41", +] + +[[package]] +name = "askama_escape" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" + +[[package]] +name = "askama_parser" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c268a96e01a4c47c8c5c2472aaa570707e006a875ea63e819f75474ceedaf7b4" +dependencies = [ + "nom", +] + [[package]] name = "atty" version = "0.2.14" @@ -140,6 +180,12 @@ dependencies = [ "serde", ] +[[package]] +name = "bumpalo" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" + [[package]] name = "bytes" version = "1.5.0" @@ -221,6 +267,21 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "comrak" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "482aa5695bca086022be453c700a40c02893f1ba7098a2c88351de55341ae894" +dependencies = [ + "entities", + "memchr", + "once_cell", + "regex", + "slug", + "typed-arena", + "unicode_categories", +] + [[package]] name = "condtype" version = "1.3.0" @@ -290,6 +351,12 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "deunicode" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a1abaf4d861455be59f64fd2b55606cb151fce304ede7165f410243ce96bde6" + [[package]] name = "directories" version = "5.0.1" @@ -346,6 +413,12 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +[[package]] +name = "entities" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5320ae4c3782150d900b79807611a59a99fc9a1d61d686faafc24b93fc8d7ca" + [[package]] name = "erased-serde" version = "0.3.31" @@ -679,6 +752,28 @@ dependencies = [ "libmimalloc-sys", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.7.1" @@ -694,6 +789,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "num_threads" version = "0.1.6" @@ -871,6 +976,18 @@ dependencies = [ "thiserror", ] +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + [[package]] name = "regex-automata" version = "0.4.3" @@ -1001,6 +1118,16 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2aeaf503862c419d66959f5d7ca015337d864e9c49485d771b732e2a20453597" +[[package]] +name = "slug" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bd94acec9c8da640005f8e135a39fc0372e74535e6b368b7a04b875f784c8c4" +dependencies = [ + "deunicode", + "wasm-bindgen", +] + [[package]] name = "smallvec" version = "1.11.2" @@ -1087,6 +1214,19 @@ dependencies = [ "syn 2.0.41", ] +[[package]] +name = "stef-doc" +version = "0.1.0" +dependencies = [ + "anyhow", + "askama", + "glob", + "heck", + "insta", + "stef-meta", + "stef-parser", +] + [[package]] name = "stef-go" version = "0.1.0" @@ -1117,10 +1257,18 @@ dependencies = [ "serde", "serde_json", "stef-compiler", + "stef-meta", "stef-parser", "time", ] +[[package]] +name = "stef-meta" +version = "0.1.0" +dependencies = [ + "stef-parser", +] + [[package]] name = "stef-parser" version = "0.1.0" @@ -1383,6 +1531,21 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +[[package]] +name = "typed-arena" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" + +[[package]] +name = "unicase" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" +dependencies = [ + "version_check", +] + [[package]] name = "unicode-bidi" version = "0.3.14" @@ -1416,6 +1579,12 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" + [[package]] name = "url" version = "2.5.0" @@ -1492,6 +1661,60 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.41", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.41", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" + [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index c99ce58..e1df026 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ missing_docs = "warn" [workspace.lints.clippy] all = "deny" pedantic = "warn" +clone_on_ref_ptr = "warn" [workspace.dependencies] clap = { version = "4.4.11", features = ["derive", "wrap_help"] } diff --git a/book/highlight/package.json b/book/highlight/package.json index 315aa08..cc8f966 100644 --- a/book/highlight/package.json +++ b/book/highlight/package.json @@ -12,6 +12,6 @@ }, "devDependencies": { "@biomejs/biome": "^1.4.1", - "esbuild": "^0.19.9" + "esbuild": "^0.19.10" } } diff --git a/bun.lockb b/bun.lockb index f05e99a..15f1f18 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/crates/stef-build/src/definition.rs b/crates/stef-build/src/definition.rs index d412da8..9e39a89 100644 --- a/crates/stef-build/src/definition.rs +++ b/crates/stef-build/src/definition.rs @@ -186,7 +186,11 @@ fn compile_const( } } -fn compile_import(Import { segments, element }: &Import<'_>) -> TokenStream { +fn compile_import( + Import { + segments, element, .. + }: &Import<'_>, +) -> TokenStream { let segments = segments.iter().enumerate().map(|(i, segment)| { let segment = Ident::new(segment.get(), Span::call_site()); if i > 0 { diff --git a/crates/stef-build/tests/snapshots/compiler__compile@mixed.stef.snap b/crates/stef-build/tests/snapshots/compiler__compile@mixed.stef.snap new file mode 100644 index 0000000..5cc3a8b --- /dev/null +++ b/crates/stef-build/tests/snapshots/compiler__compile@mixed.stef.snap @@ -0,0 +1,665 @@ +--- +source: crates/stef-build/tests/compiler.rs +description: "/// Basic user information.\n///\n/// Uses various other `structs` and `enums` to compose the information\n/// in a **type safe** way.\nstruct User {\n name: FullName @1,\n /// Physical address, might not be specified by the user.\n address: option
@2,\n age: u8 @3,\n birthday: birthday::DayOfBirth @4,\n}\n\n/// Full name of a user.\nstruct FullName {\n first: string @1,\n middle: option @2,\n last: string @3,\n}\n\n/// Simple alias for convenience.\n///\n/// - Might be easier to remember.\n/// - Often referenced as this.\ntype Name = FullName;\n\nstruct Address {\n /// Street name.\n street: string @1,\n /// Number of the house in the street.\n house_no: HouseNumber @2,\n city: string @3,\n}\n\n/// The number on the house.\n///\n/// More details can be found at [Wikipedia](https://en.wikipedia.org/wiki/House_numbering).\nenum HouseNumber {\n /// Digit only number.\n Digit(u16 @1) @1,\n /// Mixed _number_ with characters like `1a`.\n Text(string @1) @2,\n}\n\n/// Probably the max age of a human, currently.\nconst MAX_AGE: u8 = 120;\n\n\n/// Details for defining birthdays.\nmod birthday {\n /// As the name suggests, specifies details about birthdays.\n enum DayOfBirth {\n Specific {\n year: u16 @1,\n month: Month @2,\n day: u8 @3,\n } @1,\n /// The user didn't want to say.\n Secret {\n /// Optional info from the user about why they didn't want to\n /// reveal their birthday.\n reason: option @1,\n } @2,\n /// We never asked and nobody knows.\n Unknown @3,\n }\n\n /// Let's assume we only have details of people born **after** this year.\n const MIN_YEAR: u16 = 1900;\n\n /// Absolute maximum for a day, but might be even less depending\n /// on the month.\n const MAX_DAY: u8 = 31;\n\n /// The month of the year.\n enum Month {\n January @1,\n February @2,\n March @3,\n April @4,\n May @5,\n June @6,\n July @7,\n August @8,\n September @9,\n October @10,\n November @11,\n December @12,\n }\n}" +input_file: crates/stef-parser/tests/inputs/mixed.stef +--- +#[allow(unused_imports)] +use ::stef::buf::{Decode, Encode, Size}; +/// Basic user information. +/// +/// Uses various other `structs` and `enums` to compose the information +/// in a **type safe** way. +#[derive(Clone, Debug, PartialEq)] +#[allow(clippy::module_name_repetitions, clippy::option_option)] +pub struct User { + pub name: FullName, + /// Physical address, might not be specified by the user. + pub address: Option
, + pub age: u8, + pub birthday: birthday::DayOfBirth, +} +#[automatically_derived] +impl ::stef::Encode for User { + #[allow( + clippy::borrow_deref_ref, + clippy::explicit_auto_deref, + clippy::needless_borrow, + clippy::too_many_lines, + )] + fn encode(&self, w: &mut impl ::stef::BufMut) { + ::stef::buf::encode_field( + w, + 1, + |w| { + (self.name).encode(w); + }, + ); + ::stef::buf::encode_field_option( + w, + 2, + &self.address, + |w, v| { + (v).encode(w); + }, + ); + ::stef::buf::encode_field( + w, + 3, + |w| { + ::stef::buf::encode_u8(w, self.age); + }, + ); + ::stef::buf::encode_field( + w, + 4, + |w| { + (self.birthday).encode(w); + }, + ); + ::stef::buf::encode_u32(w, ::stef::buf::END_MARKER); + } +} +#[automatically_derived] +impl ::stef::Decode for User { + #[allow(clippy::type_complexity, clippy::too_many_lines)] + fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { + let mut name: Option = None; + let mut address: Option
= None; + let mut age: Option = None; + let mut birthday: Option = None; + loop { + match ::stef::buf::decode_id(r)? { + ::stef::buf::END_MARKER => break, + 1 => name = Some(FullName::decode(r)?), + 2 => address = Some(Address::decode(r)?), + 3 => age = Some(::stef::buf::decode_u8(r)?), + 4 => birthday = Some(birthday::DayOfBirth::decode(r)?), + _ => continue, + } + } + Ok(Self { + name: name + .ok_or(::stef::buf::Error::MissingField { + id: 1, + name: Some("name"), + })?, + address, + age: age + .ok_or(::stef::buf::Error::MissingField { + id: 3, + name: Some("age"), + })?, + birthday: birthday + .ok_or(::stef::buf::Error::MissingField { + id: 4, + name: Some("birthday"), + })?, + }) + } +} +#[automatically_derived] +impl ::stef::buf::Size for User { + #[allow( + clippy::borrow_deref_ref, + clippy::explicit_auto_deref, + clippy::needless_borrow, + clippy::too_many_lines, + )] + fn size(&self) -> usize { + ::stef::buf::size_field(1, || { (self.name).size() }) + + ::stef::buf::size_field_option( + 2, + self.address.as_ref(), + |v| { (v).size() }, + ) + ::stef::buf::size_field(3, || { ::stef::buf::size_u8(self.age) }) + + ::stef::buf::size_field(4, || { (self.birthday).size() }) + + ::stef::buf::size_u32(::stef::buf::END_MARKER) + } +} +/// Full name of a user. +#[derive(Clone, Debug, PartialEq)] +#[allow(clippy::module_name_repetitions, clippy::option_option)] +pub struct FullName { + pub first: String, + pub middle: Option, + pub last: String, +} +#[automatically_derived] +impl ::stef::Encode for FullName { + #[allow( + clippy::borrow_deref_ref, + clippy::explicit_auto_deref, + clippy::needless_borrow, + clippy::too_many_lines, + )] + fn encode(&self, w: &mut impl ::stef::BufMut) { + ::stef::buf::encode_field( + w, + 1, + |w| { + ::stef::buf::encode_string(w, &self.first); + }, + ); + ::stef::buf::encode_field_option( + w, + 2, + &self.middle, + |w, v| { + ::stef::buf::encode_string(w, &v); + }, + ); + ::stef::buf::encode_field( + w, + 3, + |w| { + ::stef::buf::encode_string(w, &self.last); + }, + ); + ::stef::buf::encode_u32(w, ::stef::buf::END_MARKER); + } +} +#[automatically_derived] +impl ::stef::Decode for FullName { + #[allow(clippy::type_complexity, clippy::too_many_lines)] + fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { + let mut first: Option = None; + let mut middle: Option = None; + let mut last: Option = None; + loop { + match ::stef::buf::decode_id(r)? { + ::stef::buf::END_MARKER => break, + 1 => first = Some(::stef::buf::decode_string(r)?), + 2 => middle = Some(::stef::buf::decode_string(r)?), + 3 => last = Some(::stef::buf::decode_string(r)?), + _ => continue, + } + } + Ok(Self { + first: first + .ok_or(::stef::buf::Error::MissingField { + id: 1, + name: Some("first"), + })?, + middle, + last: last + .ok_or(::stef::buf::Error::MissingField { + id: 3, + name: Some("last"), + })?, + }) + } +} +#[automatically_derived] +impl ::stef::buf::Size for FullName { + #[allow( + clippy::borrow_deref_ref, + clippy::explicit_auto_deref, + clippy::needless_borrow, + clippy::too_many_lines, + )] + fn size(&self) -> usize { + ::stef::buf::size_field(1, || { ::stef::buf::size_string(&self.first) }) + + ::stef::buf::size_field_option( + 2, + self.middle.as_ref(), + |v| { ::stef::buf::size_string(&v) }, + ) + ::stef::buf::size_field(3, || { ::stef::buf::size_string(&self.last) }) + + ::stef::buf::size_u32(::stef::buf::END_MARKER) + } +} +/// Simple alias for convenience. +/// +/// - Might be easier to remember. +/// - Often referenced as this. +#[allow(dead_code, clippy::module_name_repetitions, clippy::option_option)] +pub type Name = FullName; +#[derive(Clone, Debug, PartialEq)] +#[allow(clippy::module_name_repetitions, clippy::option_option)] +pub struct Address { + /// Street name. + pub street: String, + /// Number of the house in the street. + pub house_no: HouseNumber, + pub city: String, +} +#[automatically_derived] +impl ::stef::Encode for Address { + #[allow( + clippy::borrow_deref_ref, + clippy::explicit_auto_deref, + clippy::needless_borrow, + clippy::too_many_lines, + )] + fn encode(&self, w: &mut impl ::stef::BufMut) { + ::stef::buf::encode_field( + w, + 1, + |w| { + ::stef::buf::encode_string(w, &self.street); + }, + ); + ::stef::buf::encode_field( + w, + 2, + |w| { + (self.house_no).encode(w); + }, + ); + ::stef::buf::encode_field( + w, + 3, + |w| { + ::stef::buf::encode_string(w, &self.city); + }, + ); + ::stef::buf::encode_u32(w, ::stef::buf::END_MARKER); + } +} +#[automatically_derived] +impl ::stef::Decode for Address { + #[allow(clippy::type_complexity, clippy::too_many_lines)] + fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { + let mut street: Option = None; + let mut house_no: Option = None; + let mut city: Option = None; + loop { + match ::stef::buf::decode_id(r)? { + ::stef::buf::END_MARKER => break, + 1 => street = Some(::stef::buf::decode_string(r)?), + 2 => house_no = Some(HouseNumber::decode(r)?), + 3 => city = Some(::stef::buf::decode_string(r)?), + _ => continue, + } + } + Ok(Self { + street: street + .ok_or(::stef::buf::Error::MissingField { + id: 1, + name: Some("street"), + })?, + house_no: house_no + .ok_or(::stef::buf::Error::MissingField { + id: 2, + name: Some("house_no"), + })?, + city: city + .ok_or(::stef::buf::Error::MissingField { + id: 3, + name: Some("city"), + })?, + }) + } +} +#[automatically_derived] +impl ::stef::buf::Size for Address { + #[allow( + clippy::borrow_deref_ref, + clippy::explicit_auto_deref, + clippy::needless_borrow, + clippy::too_many_lines, + )] + fn size(&self) -> usize { + ::stef::buf::size_field(1, || { ::stef::buf::size_string(&self.street) }) + + ::stef::buf::size_field(2, || { (self.house_no).size() }) + + ::stef::buf::size_field(3, || { ::stef::buf::size_string(&self.city) }) + + ::stef::buf::size_u32(::stef::buf::END_MARKER) + } +} +/// The number on the house. +/// +/// More details can be found at [Wikipedia](https://en.wikipedia.org/wiki/House_numbering). +#[derive(Clone, Debug, PartialEq)] +#[allow(clippy::module_name_repetitions, clippy::option_option)] +pub enum HouseNumber { + /// Digit only number. + Digit(u16), + /// Mixed _number_ with characters like `1a`. + Text(String), +} +#[automatically_derived] +impl ::stef::Encode for HouseNumber { + #[allow( + clippy::borrow_deref_ref, + clippy::semicolon_if_nothing_returned, + clippy::too_many_lines, + )] + fn encode(&self, w: &mut impl ::stef::BufMut) { + match self { + Self::Digit(n0) => { + ::stef::buf::encode_id(w, 1); + ::stef::buf::encode_field(w, 1, |w| { ::stef::buf::encode_u16(w, *n0) }); + ::stef::buf::encode_u32(w, ::stef::buf::END_MARKER); + } + Self::Text(n0) => { + ::stef::buf::encode_id(w, 2); + ::stef::buf::encode_field( + w, + 1, + |w| { ::stef::buf::encode_string(w, &*n0) }, + ); + ::stef::buf::encode_u32(w, ::stef::buf::END_MARKER); + } + } + } +} +#[automatically_derived] +impl ::stef::Decode for HouseNumber { + #[allow(clippy::too_many_lines)] + fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { + match ::stef::buf::decode_id(r)? { + 1 => { + let mut n0: Option = None; + loop { + match ::stef::buf::decode_id(r)? { + ::stef::buf::END_MARKER => break, + 1 => n0 = Some(::stef::buf::decode_u16(r)?), + _ => continue, + } + } + Ok( + Self::Digit( + n0 + .ok_or(::stef::buf::Error::MissingField { + id: 1, + name: None, + })?, + ), + ) + } + 2 => { + let mut n0: Option = None; + loop { + match ::stef::buf::decode_id(r)? { + ::stef::buf::END_MARKER => break, + 1 => n0 = Some(::stef::buf::decode_string(r)?), + _ => continue, + } + } + Ok( + Self::Text( + n0 + .ok_or(::stef::buf::Error::MissingField { + id: 1, + name: None, + })?, + ), + ) + } + id => Err(::stef::buf::Error::UnknownVariant(id)), + } + } +} +#[automatically_derived] +impl ::stef::buf::Size for HouseNumber { + #[allow( + clippy::borrow_deref_ref, + clippy::semicolon_if_nothing_returned, + clippy::too_many_lines, + )] + fn size(&self) -> usize { + match self { + Self::Digit(n0) => { + ::stef::buf::size_id(1) + + ::stef::buf::size_field(1, || { ::stef::buf::size_u16(*n0) }) + + ::stef::buf::size_u32(::stef::buf::END_MARKER) + } + Self::Text(n0) => { + ::stef::buf::size_id(2) + + ::stef::buf::size_field(1, || { ::stef::buf::size_string(&*n0) }) + + ::stef::buf::size_u32(::stef::buf::END_MARKER) + } + } + } +} +/// Probably the max age of a human, currently. +#[allow(dead_code)] +pub const MAX_AGE: u8 = 120; +/// Details for defining birthdays. +pub mod birthday { + #[allow(unused_imports)] + use ::stef::buf::{Decode, Encode, Size}; + /// As the name suggests, specifies details about birthdays. + #[derive(Clone, Debug, PartialEq)] + #[allow(clippy::module_name_repetitions, clippy::option_option)] + pub enum DayOfBirth { + Specific { year: u16, month: Month, day: u8 }, + /// The user didn't want to say. + Secret { + /// Optional info from the user about why they didn't want to + /// reveal their birthday. + reason: Option, + }, + /// We never asked and nobody knows. + Unknown, + } + #[automatically_derived] + impl ::stef::Encode for DayOfBirth { + #[allow( + clippy::borrow_deref_ref, + clippy::semicolon_if_nothing_returned, + clippy::too_many_lines, + )] + fn encode(&self, w: &mut impl ::stef::BufMut) { + match self { + Self::Specific { year, month, day } => { + ::stef::buf::encode_id(w, 1); + ::stef::buf::encode_field( + w, + 1, + |w| { ::stef::buf::encode_u16(w, *year) }, + ); + ::stef::buf::encode_field(w, 2, |w| { (*month).encode(w) }); + ::stef::buf::encode_field( + w, + 3, + |w| { ::stef::buf::encode_u8(w, *day) }, + ); + ::stef::buf::encode_u32(w, ::stef::buf::END_MARKER); + } + Self::Secret { reason } => { + ::stef::buf::encode_id(w, 2); + ::stef::buf::encode_field_option(w, 1, &reason); + ::stef::buf::encode_u32(w, ::stef::buf::END_MARKER); + } + Self::Unknown => { + ::stef::buf::encode_id(w, 3); + } + } + } + } + #[automatically_derived] + impl ::stef::Decode for DayOfBirth { + #[allow(clippy::too_many_lines)] + fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { + match ::stef::buf::decode_id(r)? { + 1 => { + let mut year: Option = None; + let mut month: Option = None; + let mut day: Option = None; + loop { + match ::stef::buf::decode_id(r)? { + ::stef::buf::END_MARKER => break, + 1 => year = Some(::stef::buf::decode_u16(r)?), + 2 => month = Some(Month::decode(r)?), + 3 => day = Some(::stef::buf::decode_u8(r)?), + _ => continue, + } + } + Ok(Self::Specific { + year: year + .ok_or(::stef::buf::Error::MissingField { + id: 1, + name: Some("year"), + })?, + month: month + .ok_or(::stef::buf::Error::MissingField { + id: 2, + name: Some("month"), + })?, + day: day + .ok_or(::stef::buf::Error::MissingField { + id: 3, + name: Some("day"), + })?, + }) + } + 2 => { + let mut reason: Option = None; + loop { + match ::stef::buf::decode_id(r)? { + ::stef::buf::END_MARKER => break, + 1 => reason = Some(::stef::buf::decode_string(r)?), + _ => continue, + } + } + Ok(Self::Secret { reason }) + } + 3 => Ok(Self::Unknown), + id => Err(::stef::buf::Error::UnknownVariant(id)), + } + } + } + #[automatically_derived] + impl ::stef::buf::Size for DayOfBirth { + #[allow( + clippy::borrow_deref_ref, + clippy::semicolon_if_nothing_returned, + clippy::too_many_lines, + )] + fn size(&self) -> usize { + match self { + Self::Specific { year, month, day } => { + ::stef::buf::size_id(1) + + ::stef::buf::size_field(1, || { ::stef::buf::size_u16(*year) }) + + ::stef::buf::size_field(2, || { (*month).size() }) + + ::stef::buf::size_field(3, || { ::stef::buf::size_u8(*day) }) + + ::stef::buf::size_u32(::stef::buf::END_MARKER) + } + Self::Secret { reason } => { + ::stef::buf::size_id(2) + + ::stef::buf::size_field_option(1, reason.as_ref()) + + ::stef::buf::size_u32(::stef::buf::END_MARKER) + } + Self::Unknown => ::stef::buf::size_id(3), + } + } + } + /// Let's assume we only have details of people born **after** this year. + #[allow(dead_code)] + pub const MIN_YEAR: u16 = 1900; + /// Absolute maximum for a day, but might be even less depending + /// on the month. + #[allow(dead_code)] + pub const MAX_DAY: u8 = 31; + /// The month of the year. + #[derive(Clone, Debug, PartialEq)] + #[allow(clippy::module_name_repetitions, clippy::option_option)] + pub enum Month { + January, + February, + March, + April, + May, + June, + July, + August, + September, + October, + November, + December, + } + #[automatically_derived] + impl ::stef::Encode for Month { + #[allow( + clippy::borrow_deref_ref, + clippy::semicolon_if_nothing_returned, + clippy::too_many_lines, + )] + fn encode(&self, w: &mut impl ::stef::BufMut) { + match self { + Self::January => { + ::stef::buf::encode_id(w, 1); + } + Self::February => { + ::stef::buf::encode_id(w, 2); + } + Self::March => { + ::stef::buf::encode_id(w, 3); + } + Self::April => { + ::stef::buf::encode_id(w, 4); + } + Self::May => { + ::stef::buf::encode_id(w, 5); + } + Self::June => { + ::stef::buf::encode_id(w, 6); + } + Self::July => { + ::stef::buf::encode_id(w, 7); + } + Self::August => { + ::stef::buf::encode_id(w, 8); + } + Self::September => { + ::stef::buf::encode_id(w, 9); + } + Self::October => { + ::stef::buf::encode_id(w, 10); + } + Self::November => { + ::stef::buf::encode_id(w, 11); + } + Self::December => { + ::stef::buf::encode_id(w, 12); + } + } + } + } + #[automatically_derived] + impl ::stef::Decode for Month { + #[allow(clippy::too_many_lines)] + fn decode(r: &mut impl ::stef::Buf) -> ::stef::buf::Result { + match ::stef::buf::decode_id(r)? { + 1 => Ok(Self::January), + 2 => Ok(Self::February), + 3 => Ok(Self::March), + 4 => Ok(Self::April), + 5 => Ok(Self::May), + 6 => Ok(Self::June), + 7 => Ok(Self::July), + 8 => Ok(Self::August), + 9 => Ok(Self::September), + 10 => Ok(Self::October), + 11 => Ok(Self::November), + 12 => Ok(Self::December), + id => Err(::stef::buf::Error::UnknownVariant(id)), + } + } + } + #[automatically_derived] + impl ::stef::buf::Size for Month { + #[allow( + clippy::borrow_deref_ref, + clippy::semicolon_if_nothing_returned, + clippy::too_many_lines, + )] + fn size(&self) -> usize { + match self { + Self::January => ::stef::buf::size_id(1), + Self::February => ::stef::buf::size_id(2), + Self::March => ::stef::buf::size_id(3), + Self::April => ::stef::buf::size_id(4), + Self::May => ::stef::buf::size_id(5), + Self::June => ::stef::buf::size_id(6), + Self::July => ::stef::buf::size_id(7), + Self::August => ::stef::buf::size_id(8), + Self::September => ::stef::buf::size_id(9), + Self::October => ::stef::buf::size_id(10), + Self::November => ::stef::buf::size_id(11), + Self::December => ::stef::buf::size_id(12), + } + } + } +} + diff --git a/crates/stef-doc/.gitignore b/crates/stef-doc/.gitignore new file mode 100644 index 0000000..8c4b504 --- /dev/null +++ b/crates/stef-doc/.gitignore @@ -0,0 +1,2 @@ +/assets/style.css +/node_modules diff --git a/crates/stef-doc/Cargo.toml b/crates/stef-doc/Cargo.toml new file mode 100644 index 0000000..a8fd25f --- /dev/null +++ b/crates/stef-doc/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "stef-doc" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +readme.workspace = true +homepage.workspace = true +repository.workspace = true +license.workspace = true + +[dependencies] +anyhow = "1.0.75" +askama = { version = "0.12.1", default-features = false, features = ["markdown"] } +heck = "0.4.1" +stef-meta = { path = "../stef-meta" } +stef-parser = { path = "../stef-parser" } + +[dev-dependencies] +glob.workspace = true +insta.workspace = true + +[lints] +workspace = true diff --git a/crates/stef-doc/examples/render.rs b/crates/stef-doc/examples/render.rs new file mode 100644 index 0000000..90310e3 --- /dev/null +++ b/crates/stef-doc/examples/render.rs @@ -0,0 +1,48 @@ +//! Helper that renders all the test snapshot inputs as HTML files out into the OS's temp folder. +//! +//! All required assets are copied to the right location as well, so each input file is turned into +//! an atomic output folder, as if it would be run through the CLI in the future (similar to +//! `rustdoc` being invoked by `cargo doc`). + +use std::{env, fs, path::Path}; + +use stef_doc::{Opts, Output}; +use stef_parser::Schema; + +fn write_output(output: &Output<'_>, parent: &Path) { + let path = parent.join(output.name); + let file = parent.join(&output.file); + + fs::create_dir_all(file.parent().unwrap()).unwrap(); + fs::write(file, &output.content).unwrap(); + + for module in &output.modules { + write_output(module, &path); + } +} + +fn main() { + let dir = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/inputs"); + let out = env::temp_dir().join(concat!("stef/", env!("CARGO_PKG_NAME"))); + + for path in glob::glob(&format!("{dir}/*.stef")).unwrap() { + let path = path.unwrap(); + let name = path.strip_prefix(dir).unwrap(); + + let input = fs::read_to_string(&path).unwrap(); + let value = Schema::parse(input.as_str(), Some(name)).unwrap(); + let value = stef_doc::render_schema(&Opts {}, &value).unwrap(); + + let out = out.join(name).with_extension(""); + + fs::remove_dir_all(&out).ok(); + fs::create_dir_all(out.join("assets")).unwrap(); + fs::copy( + concat!(env!("CARGO_MANIFEST_DIR"), "/assets/style.css"), + out.join("assets/style.css"), + ) + .unwrap(); + + write_output(&value, &out); + } +} diff --git a/crates/stef-doc/input.css b/crates/stef-doc/input.css new file mode 100644 index 0000000..c936f9a --- /dev/null +++ b/crates/stef-doc/input.css @@ -0,0 +1,91 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + body { + @apply bg-main-200 text-main-800 dark:bg-main-800 dark:text-main-200; + } + + main { + @apply container max-w-6xl px-12 py-4; + } + + a { + @apply hover:underline; + } + + table { + @apply table-auto border-collapse; + } + + h1 { + @apply text-3xl font-bold; + } + + h2 { + @apply text-2xl font-bold; + } + + h3 { + @apply text-xl font-bold; + } + + h4 { + @apply text-lg font-bold; + } +} + +@layer components { + .markdown { + @apply markdown-neutral dark:markdown-invert max-w-none; + } + + .name-module { + @apply text-black dark:text-white; + } + + .name-struct { + @apply text-sky-600 dark:text-sky-400; + } + + .name-enum { + @apply text-sky-600 dark:text-sky-400; + } + + .name-variant { + @apply text-teal-600 dark:text-teal-400; + } + + .name-alias { + @apply text-lime-600 dark:text-lime-400; + } + + .name-const { + @apply text-orange-600 dark:text-orange-400; + } + + .field-definition { + @apply mt-2 p-2 bg-main-300/50 dark:bg-main-700/50 rounded-sm; + } + + .field-name { + @apply text-red-600 dark:text-red-400; + } + + .field-type { + @apply text-purple-600 dark:text-purple-400; + } + + .field-id { + @apply text-amber-600 dark:text-amber-400; + } + + .section { + @apply rounded-sm bg-main-100/50 dark:bg-main-900/50 p-2 mt-2; + } + + .item-definition { + @apply p-2 bg-main-300/50 dark:bg-main-700/50 rounded-sm overflow-x-auto; + } +} diff --git a/crates/stef-doc/package.json b/crates/stef-doc/package.json new file mode 100644 index 0000000..4f7dbd5 --- /dev/null +++ b/crates/stef-doc/package.json @@ -0,0 +1,13 @@ +{ + "name": "stef-doc", + "private": true, + "scripts": { + "tailwind": "tailwindcss -i ./input.css -o ./assets/style.css", + "watch": "bun run tailwind --watch", + "build": "bun run tailwind --minify" + }, + "dependencies": { + "@tailwindcss/typography": "^0.5.10", + "tailwindcss": "^3.4.0" + } +} diff --git a/crates/stef-doc/src/lib.rs b/crates/stef-doc/src/lib.rs new file mode 100644 index 0000000..4ba4d74 --- /dev/null +++ b/crates/stef-doc/src/lib.rs @@ -0,0 +1,140 @@ +//! Documentation generator for Stef schema files, akin to Rust's `rustdoc`. + +use std::rc::Rc; + +use anyhow::Result; +use askama::Template; +use stef_parser::{Const, Definition, Enum, Module, Schema, Struct, TypeAlias}; + +mod templates; + +/// Additional options to modify the behavior of the documentation generation. +pub struct Opts {} + +/// The in-memory output of rendering a single [`Schema`] into HTML documentation. +#[derive(Debug)] +pub struct Output<'a> { + /// Name of the element, without any module path attached to it. For example, the plain struct + /// or module name. + pub name: &'a str, + /// Module path from the root of the schema to the current element/file. + pub path: Rc<[Rc]>, + /// Name of the output file. This might contain a parent directly name as well, in case it is a + /// module as each module is grouped into its own subfolder together with all its definitions. + pub file: String, + /// Fully generated content of the file that should be written to disk. + pub content: String, + /// Sub-modules of the root of the schema or currently represented module within. + pub modules: Vec>, +} + +/// Convert the given schema into a tree of HTML documentation files. +/// +/// The files are all kept in memory and writing to disc is up to the caller. +/// +/// # Errors +/// +/// Will return `Err` if any of the HTML templates fail to render. +pub fn render_schema<'a>( + _opts: &'a Opts, + Schema { + path, definitions, .. + }: &'a Schema<'_>, +) -> Result> { + let name = path + .as_ref() + .and_then(|p| p.file_stem()) + .and_then(|p| p.to_str()) + .unwrap_or("root"); + let path = Rc::from([Rc::from(name)]); + + Ok(Output { + content: templates::Index { + name, + path: &path, + definitions, + } + .render()?, + modules: definitions + .iter() + .filter_map(|def| render_definition(def, &path)) + .collect::>()?, + name, + path, + file: format!("{name}/index.html"), + }) +} + +fn render_definition<'a>( + item: &'a Definition<'_>, + path: &Rc<[Rc]>, +) -> Option>> { + Some(match item { + Definition::Module(m) => render_module(m, path), + Definition::Struct(s) => render_struct(s, path), + Definition::Enum(e) => render_enum(e, path), + Definition::TypeAlias(a) => render_alias(a, path), + Definition::Const(c) => render_const(c, path), + Definition::Import(_) => return None, + }) +} + +fn render_module<'a>(item: &'a Module<'_>, path: &Rc<[Rc]>) -> Result> { + let path = { + let mut path = path.to_vec(); + path.push(item.name.get().into()); + Rc::from(path) + }; + + Ok(Output { + content: templates::ModuleDetail { path: &path, item }.render()?, + modules: item + .definitions + .iter() + .filter_map(|def| render_definition(def, &path)) + .collect::>()?, + name: item.name.get(), + path, + file: format!("{}/index.html", item.name), + }) +} + +fn render_struct<'a>(item: &'a Struct<'_>, path: &Rc<[Rc]>) -> Result> { + Ok(Output { + name: item.name.get(), + path: Rc::clone(path), + file: format!("struct.{}.html", item.name), + content: templates::StructDetail { path, item }.render()?, + modules: Vec::new(), + }) +} + +fn render_enum<'a>(item: &'a Enum<'_>, path: &Rc<[Rc]>) -> Result> { + Ok(Output { + name: item.name.get(), + path: Rc::clone(path), + file: format!("enum.{}.html", item.name), + content: templates::EnumDetail { path, item }.render()?, + modules: Vec::new(), + }) +} + +fn render_alias<'a>(item: &'a TypeAlias<'_>, path: &Rc<[Rc]>) -> Result> { + Ok(Output { + name: item.name.get(), + path: Rc::clone(path), + file: format!("alias.{}.html", item.name), + content: templates::AliasDetail { path, item }.render()?, + modules: Vec::new(), + }) +} + +fn render_const<'a>(item: &'a Const<'_>, path: &Rc<[Rc]>) -> Result> { + Ok(Output { + name: item.name.get(), + path: Rc::clone(path), + file: format!("constant.{}.html", item.name), + content: templates::ConstDetail { path, item }.render()?, + modules: Vec::new(), + }) +} diff --git a/crates/stef-doc/src/templates.rs b/crates/stef-doc/src/templates.rs new file mode 100644 index 0000000..8d8e622 --- /dev/null +++ b/crates/stef-doc/src/templates.rs @@ -0,0 +1,94 @@ +use std::{ + fmt::{self, Display}, + rc::Rc, +}; + +use askama::Template; +use stef_meta::WireSize; +use stef_parser::{Comment, Const, Definition, Enum, Fields, Module, Struct, TypeAlias}; + +#[derive(Template)] +#[template(path = "index.html")] +pub struct Index<'a> { + pub name: &'a str, + pub path: &'a [Rc], + pub definitions: &'a [Definition<'a>], +} + +#[derive(Template)] +#[template(path = "detail/module.html")] +pub struct ModuleDetail<'a> { + pub path: &'a [Rc], + pub item: &'a Module<'a>, +} + +#[derive(Template)] +#[template(path = "detail/struct.html")] +pub struct StructDetail<'a> { + pub path: &'a [Rc], + pub item: &'a Struct<'a>, +} + +#[derive(Template)] +#[template(path = "detail/enum.html")] +pub struct EnumDetail<'a> { + pub path: &'a [Rc], + pub item: &'a Enum<'a>, +} + +#[derive(Template)] +#[template(path = "detail/alias.html")] +pub struct AliasDetail<'a> { + pub path: &'a [Rc], + pub item: &'a TypeAlias<'a>, +} + +#[derive(Template)] +#[template(path = "detail/const.html")] +pub struct ConstDetail<'a> { + pub path: &'a [Rc], + pub item: &'a Const<'a>, +} + +fn render_wire_size(size: &WireSize) -> String { + let mut buf = String::new(); + size.print(&mut buf, 0); + buf +} + +#[allow(clippy::trivially_copy_pass_by_ref)] +fn path_up(len: usize, i: &usize) -> PathUp { + debug_assert!(len > 0); + debug_assert!(len > *i); + PathUp(len - *i - 1) +} + +struct PathUp(usize); + +impl Display for PathUp { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for _ in 0..self.0 { + f.write_str("../")?; + } + Ok(()) + } +} + +fn merge_comments(item: &Comment<'_>) -> String { + item.0.iter().fold(String::new(),|mut acc, line| { + acc.push_str(line.value); + acc.push('\n'); + acc + }) +} + +struct MergeComments<'a>(&'a Comment<'a>); + +impl Display for MergeComments<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for line in &self.0 .0 { + writeln!(f, "{}", line.value)?; + } + Ok(()) + } +} diff --git a/crates/stef-doc/tailwind.config.js b/crates/stef-doc/tailwind.config.js new file mode 100644 index 0000000..2251659 --- /dev/null +++ b/crates/stef-doc/tailwind.config.js @@ -0,0 +1,27 @@ +const defaultTheme = require("tailwindcss/defaultTheme") +const colors = require("tailwindcss/colors") + +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: { + relative: true, + files: ["./templates/**/*.html"], + }, + theme: { + extend: { + colors: { + main: colors.neutral, + }, + fontFamily: { + 'sans': ['"Noto Sans"', ...defaultTheme.fontFamily.sans], + 'serif': ['"Noto Serif"', ...defaultTheme.fontFamily.serif], + 'mono': ['"Fira Code"', ...defaultTheme.fontFamily.mono], + }, + }, + }, + plugins: [ + require("@tailwindcss/typography")({ + className: "markdown", + }), + ], +} diff --git a/crates/stef-doc/templates/base.html b/crates/stef-doc/templates/base.html new file mode 100644 index 0000000..b1c4968 --- /dev/null +++ b/crates/stef-doc/templates/base.html @@ -0,0 +1,20 @@ + + + + + + + {% let prefix = "../".repeat(path.len()) %} + + + {% block title %}{{ title }}{% endblock %} - Stef + + + +
+ {%- block content %}{% endblock %} +
+ + diff --git a/crates/stef-doc/templates/detail/alias.html b/crates/stef-doc/templates/detail/alias.html new file mode 100644 index 0000000..614f635 --- /dev/null +++ b/crates/stef-doc/templates/detail/alias.html @@ -0,0 +1,19 @@ +{% extends "base.html" %} + +{% block title %}{{ item.name }}{% endblock %} + +{% block content %} +
+

+ Alias + {% for (i, p) in path.iter().enumerate() -%} + {{ p }}:: + {%- endfor -%} + {{ item.name }} +

+
{{ item }}
+
+ {{ self::merge_comments(item.comment)|markdown|trim|safe }} +
+
+{% endblock %} diff --git a/crates/stef-doc/templates/detail/const.html b/crates/stef-doc/templates/detail/const.html new file mode 100644 index 0000000..1cc24b0 --- /dev/null +++ b/crates/stef-doc/templates/detail/const.html @@ -0,0 +1,19 @@ +{% extends "base.html" %} + +{% block title %}{{ item.name }}{% endblock %} + +{% block content %} +
+

+ Constant + {% for (i, p) in path.iter().enumerate() -%} + {{ p }}:: + {%- endfor -%} + {{ item.name }} +

+
{{ item }}
+
+ {{ self::merge_comments(item.comment)|markdown|trim|safe }} +
+
+{% endblock %} diff --git a/crates/stef-doc/templates/detail/enum.html b/crates/stef-doc/templates/detail/enum.html new file mode 100644 index 0000000..f17efd9 --- /dev/null +++ b/crates/stef-doc/templates/detail/enum.html @@ -0,0 +1,94 @@ +{% extends "base.html" %} + +{% block title %}{{ item.name }}{% endblock %} + +{% block content %} +
+

+ Enum + {% for (i, p) in path.iter().enumerate() -%} + {{ p }}:: + {%- endfor -%} + {{ item.name }} +

+
{{ item }}
+
+ {{ self::merge_comments(item.comment)|markdown|trim|safe }} +
+
+
+

Variants

+
    + {%- for variant in item.variants %} +
  • +
    {{ variant.name }}
    +
    + {{ self::merge_comments(variant.comment)|markdown|trim|safe }} +
    +
    + {%- match variant.fields %} + {%- when Fields::Named(fields) %} +

    Fields

    +
      + {%- for field in fields %} +
    • + + {{ field.name }}: + {{ field.ty }} + {{ field.id }} + +
      + {{ self::merge_comments(field.comment)|markdown|trim|safe }} +
      + + {%- match stef_meta::wire_size(field.ty.value) %} + {%- when Some(size) %} +
      +

      Metadata

      +
      +

      The size range is:

      +
      + {{ self::render_wire_size(size)|markdown|trim|indent(16)|safe }} +
      +
      +
      + {%- when None %} + {%- endmatch %} +
    • + {%- endfor %} +
    + {%- when Fields::Unnamed(fields) %} +

    Fields

    +
      + {%- for (i, field) in fields.iter().enumerate() %} +
    • + + {{ i }}: + {{ field.ty }} + {{ field.id }} + +
    • + + {%- match stef_meta::wire_size(field.ty.value) %} + {%- when Some(size) %} +

      Metadata

      +
      +

      The size range is:

      +
      + {{ self::render_wire_size(size)|markdown|trim|indent(14)|safe }} +
      +
      + {%- when None %} + {%- endmatch %} + {%- endfor %} +
    + {%- when Fields::Unit %} + {%- endmatch %} +
    +
  • + {%- endfor %} +
+
+{% endblock %} diff --git a/crates/stef-doc/templates/detail/module.html b/crates/stef-doc/templates/detail/module.html new file mode 100644 index 0000000..325c39c --- /dev/null +++ b/crates/stef-doc/templates/detail/module.html @@ -0,0 +1,123 @@ +{% extends "base.html" %} + +{% block title %}{{ item.name }}{% endblock %} + +{% block content %} +
+

+ Module + {% for (i, p) in path.iter().take(path.len() - 1).enumerate() -%} + {{ p }}:: + {%- endfor -%} + {{ item.name }} +

+
+ {{ self::merge_comments(item.comment)|markdown|trim|safe }} +
+
+
+

Modules

+ + {%- for def in item.definitions %} + {%- match def %} + {%- when Definition::Module(m) %} + + + + + {%- else %} + {%- endmatch %} + {%- endfor %} +
+ {{ m.name }} + + {%- if !m.comment.0.is_empty() %} + {{ m.comment.0[0].value|markdown|trim|safe }} + {%- endif %} +
+
+
+

Structs

+ + {%- for def in item.definitions %} + {%- match def %} + {%- when Definition::Struct(s) %} + + + + + {%- else %} + {%- endmatch %} + {%- endfor %} +
+ {{ s.name }} + + {%- if !s.comment.0.is_empty() %} + {{ s.comment.0[0].value|markdown|trim|safe }} + {%- endif %} +
+
+
+

Enums

+ + {%- for def in item.definitions %} + {%- match def %} + {%- when Definition::Enum(e) %} + + + + + {%- else %} + {%- endmatch %} + {%- endfor %} +
+ {{ e.name }} + + {%- if !e.comment.0.is_empty() %} + {{ e.comment.0[0].value|markdown|trim|safe }} + {%- endif %} +
+
+
+

Aliases

+ + {%- for def in item.definitions %} + {%- match def %} + {%- when Definition::TypeAlias(a) %} + + + + + {%- else %} + {%- endmatch %} + {%- endfor %} +
+ {{ a.name }} + + {%- if !a.comment.0.is_empty() %} + {{ a.comment.0[0].value|markdown|trim|safe }} + {%- endif %} +
+
+
+

Constants

+ + {%- for def in item.definitions %} + {%- match def %} + {%- when Definition::Const(c) %} + + + + + {%- else %} + {%- endmatch %} + {%- endfor %} +
+ {{ c.name }} + + {%- if !c.comment.0.is_empty() %} + {{ c.comment.0[0].value|markdown|trim|safe }} + {%- endif %} +
+
+{%- endblock %} diff --git a/crates/stef-doc/templates/detail/struct.html b/crates/stef-doc/templates/detail/struct.html new file mode 100644 index 0000000..7fc9926 --- /dev/null +++ b/crates/stef-doc/templates/detail/struct.html @@ -0,0 +1,87 @@ +{% extends "base.html" %} + +{% block title %}{{ item.name }}{% endblock %} + +{% block content %} +
+

+ Struct + {% for (i, p) in path.iter().enumerate() -%} + {{ p }}:: + {%- endfor -%} + {{ item.name }} +

+
{{ item }}
+
+ {{ self::merge_comments(item.comment)|markdown|trim|safe }} +
+
+
+

Metadata

+
+ {%- match stef_meta::next_field_id(item.fields) %} + {%- when Some(next_id) %} +

The next free ID is {{ next_id }}

+ {%- when None %} + {%- endmatch %} +
+
+
+

Fields

+ {%- match item.fields %} + {%- when Fields::Named(fields) %} +
    + {%- for field in fields %} +
  • + + {{ field.name }}: + {{ field.ty }} + {{ field.id }} + +
    + {{ self::merge_comments(field.comment)|markdown|trim|safe }} +
    + {%- match stef_meta::wire_size(field.ty.value) %} + {%- when Some(size) %} +

    Metadata

    +
    +

    The size range is:

    +
    + {{ self::render_wire_size(size)|markdown|trim|indent(10)|safe }} +
    +
    + {%- when None %} + {%- endmatch %} +
  • + {%- endfor %} +
+ {%- when Fields::Unnamed(fields) %} +
    + {%- for (i, field) in fields.iter().enumerate() %} +
  • + + {{ i }}: + {{ field.ty }} + {{ field.id }} + +
  • + + {%- match stef_meta::wire_size(field.ty.value) %} + {%- when Some(size) %} +

    Metadata

    +
    +

    The size range is:

    +
    + {{ self::render_wire_size(size)|markdown|trim|indent(8)|safe }} +
    +
    + {%- when None %} + {%- endmatch %} + {%- endfor %} +
+ {%- when Fields::Unit %} +
+{%- endmatch %} +{% endblock %} diff --git a/crates/stef-doc/templates/index.html b/crates/stef-doc/templates/index.html new file mode 100644 index 0000000..784671e --- /dev/null +++ b/crates/stef-doc/templates/index.html @@ -0,0 +1,114 @@ +{% extends "base.html" %} + +{% block title %}{{ name }}{% endblock %} + +{% block content %} +
+

Schema {{ name }}

+
+
+

Modules

+ + {%- for def in definitions %} + {%- match def %} + {%- when Definition::Module(m) %} + + + + + {%- else %} + {%- endmatch %} + {%- endfor %} +
+ {{ m.name }} + + {%- if !m.comment.0.is_empty() %} + {{ m.comment.0[0].value|markdown|trim|safe }} + {%- endif %} +
+
+
+

Structs

+ + {%- for def in definitions %} + {%- match def %} + {%- when Definition::Struct(s) %} + + + + + {%- else %} + {%- endmatch %} + {%- endfor %} +
+ {{ s.name }} + + {%- if !s.comment.0.is_empty() %} + {{ s.comment.0[0].value|markdown|trim|safe }} + {%- endif %} +
+
+
+

Enums

+ + {%- for def in definitions %} + {%- match def %} + {%- when Definition::Enum(e) %} + + + + + {%- else %} + {%- endmatch %} + {%- endfor %} +
+ {{ e.name }} + + {%- if !e.comment.0.is_empty() %} + {{ e.comment.0[0].value|markdown|trim|safe }} + {%- endif %} +
+
+
+

Aliases

+ + {%- for def in definitions %} + {%- match def %} + {%- when Definition::TypeAlias(a) %} + + + + + {%- else %} + {%- endmatch %} + {%- endfor %} +
+ {{ a.name }} + + {%- if !a.comment.0.is_empty() %} + {{ a.comment.0[0].value|markdown|trim|safe }} + {%- endif %} +
+
+
+

Constants

+ + {%- for def in definitions %} + {%- match def %} + {%- when Definition::Const(c) %} + + + + + {%- else %} + {%- endmatch %} + {%- endfor %} +
+ {{ c.name }} + + {%- if !c.comment.0.is_empty() %} + {{ c.comment.0[0].value|markdown|trim|safe }} + {%- endif %} +
+
+{%- endblock %} diff --git a/crates/stef-doc/tests/inputs b/crates/stef-doc/tests/inputs new file mode 120000 index 0000000..7089636 --- /dev/null +++ b/crates/stef-doc/tests/inputs @@ -0,0 +1 @@ +../../stef-parser/tests/inputs \ No newline at end of file diff --git a/crates/stef-doc/tests/render.rs b/crates/stef-doc/tests/render.rs new file mode 100644 index 0000000..9b96e98 --- /dev/null +++ b/crates/stef-doc/tests/render.rs @@ -0,0 +1,44 @@ +use std::{ + fmt::Write, + fs, + path::{Path, PathBuf}, +}; + +use insta::{assert_snapshot, glob, with_settings}; +use stef_doc::{Opts, Output}; +use stef_parser::Schema; + +fn strip_path(path: &Path) -> PathBuf { + path.strip_prefix(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/inputs")) + .unwrap() + .to_owned() +} + +fn merge_output(buf: &mut String, output: &Output<'_>, parent: &Path) { + let path = parent.join(output.name); + let file = parent.join(&output.file); + let _ = write!(buf, "\n\n--- {}\n\n{}", file.display(), output.content); + + for module in &output.modules { + merge_output(buf, module, &path); + } +} + +#[test] +fn render_schema() { + glob!("inputs/*.stef", |path| { + let input = fs::read_to_string(path).unwrap(); + let value = Schema::parse(input.as_str(), Some(&strip_path(path))).unwrap(); + let value = stef_doc::render_schema(&Opts {}, &value).unwrap(); + + let mut merged = String::new(); + merge_output(&mut merged, &value, Path::new("")); + + with_settings!({ + description => input.trim(), + omit_expression => true, + }, { + assert_snapshot!("render", merged.trim_start()); + }); + }); +} diff --git a/crates/stef-doc/tests/snapshots/render__render@alias_basic.stef.snap b/crates/stef-doc/tests/snapshots/render__render@alias_basic.stef.snap new file mode 100644 index 0000000..816fc77 --- /dev/null +++ b/crates/stef-doc/tests/snapshots/render__render@alias_basic.stef.snap @@ -0,0 +1,94 @@ +--- +source: crates/stef-doc/tests/render.rs +description: "/// Sample type alias.\ntype Sample = u32;" +input_file: crates/stef-parser/tests/inputs/alias_basic.stef +--- +--- alias_basic/index.html + + + + + + + + + + + alias_basic - Stef + + + +
+
+

Schema alias_basic

+
+
+

Modules

+ +
+
+
+

Structs

+ +
+
+
+

Enums

+ +
+
+
+

Aliases

+ + + + + +
+ Sample + +

Sample type alias.

+
+
+
+

Constants

+ +
+
+
+ + + +--- alias_basic/alias.Sample.html + + + + + + + + + + + Sample - Stef + + + +
+
+

+ Alias + alias_basic::Sample +

+
/// Sample type alias.
+type Sample = u32;
+
+

Sample type alias.

+
+
+ +
+ + diff --git a/crates/stef-doc/tests/snapshots/render__render@attribute_multi.stef.snap b/crates/stef-doc/tests/snapshots/render__render@attribute_multi.stef.snap new file mode 100644 index 0000000..6f283fb --- /dev/null +++ b/crates/stef-doc/tests/snapshots/render__render@attribute_multi.stef.snap @@ -0,0 +1,102 @@ +--- +source: crates/stef-doc/tests/render.rs +description: "#[validate(min = 1, max = 100)]\nstruct Sample" +input_file: crates/stef-parser/tests/inputs/attribute_multi.stef +--- +--- attribute_multi/index.html + + + + + + + + + + + attribute_multi - Stef + + + +
+
+

Schema attribute_multi

+
+
+

Modules

+ +
+
+
+

Structs

+ + + + + +
+ Sample + +
+
+
+

Enums

+ +
+
+
+

Aliases

+ +
+
+
+

Constants

+ +
+
+
+ + + +--- attribute_multi/struct.Sample.html + + + + + + + + + + + Sample - Stef + + + +
+
+

+ Struct + attribute_multi::Sample +

+
#[validate(min = 1, max = 100)]
+struct Sample
+
+
+ +
+
+
+

Metadata

+
+
+
+
+

Fields

+
+ +
+ + diff --git a/crates/stef-doc/tests/snapshots/render__render@attribute_single.stef.snap b/crates/stef-doc/tests/snapshots/render__render@attribute_single.stef.snap new file mode 100644 index 0000000..78a2073 --- /dev/null +++ b/crates/stef-doc/tests/snapshots/render__render@attribute_single.stef.snap @@ -0,0 +1,102 @@ +--- +source: crates/stef-doc/tests/render.rs +description: "#[deprecated = \"don't use\"]\nstruct Sample" +input_file: crates/stef-parser/tests/inputs/attribute_single.stef +--- +--- attribute_single/index.html + + + + + + + + + + + attribute_single - Stef + + + +
+
+

Schema attribute_single

+
+
+

Modules

+ +
+
+
+

Structs

+ + + + + +
+ Sample + +
+
+
+

Enums

+ +
+
+
+

Aliases

+ +
+
+
+

Constants

+ +
+
+
+ + + +--- attribute_single/struct.Sample.html + + + + + + + + + + + Sample - Stef + + + +
+
+

+ Struct + attribute_single::Sample +

+
#[deprecated = "don't use"]
+struct Sample
+
+
+ +
+
+
+

Metadata

+
+
+
+
+

Fields

+
+ +
+ + diff --git a/crates/stef-doc/tests/snapshots/render__render@attribute_unit.stef.snap b/crates/stef-doc/tests/snapshots/render__render@attribute_unit.stef.snap new file mode 100644 index 0000000..5d1fc13 --- /dev/null +++ b/crates/stef-doc/tests/snapshots/render__render@attribute_unit.stef.snap @@ -0,0 +1,102 @@ +--- +source: crates/stef-doc/tests/render.rs +description: "#[deprecated]\nstruct Sample" +input_file: crates/stef-parser/tests/inputs/attribute_unit.stef +--- +--- attribute_unit/index.html + + + + + + + + + + + attribute_unit - Stef + + + +
+
+

Schema attribute_unit

+
+
+

Modules

+ +
+
+
+

Structs

+ + + + + +
+ Sample + +
+
+
+

Enums

+ +
+
+
+

Aliases

+ +
+
+
+

Constants

+ +
+
+
+ + + +--- attribute_unit/struct.Sample.html + + + + + + + + + + + Sample - Stef + + + +
+
+

+ Struct + attribute_unit::Sample +

+
#[deprecated]
+struct Sample
+
+
+ +
+
+
+

Metadata

+
+
+
+
+

Fields

+
+ +
+ + diff --git a/crates/stef-doc/tests/snapshots/render__render@attributes.stef.snap b/crates/stef-doc/tests/snapshots/render__render@attributes.stef.snap new file mode 100644 index 0000000..3d11556 --- /dev/null +++ b/crates/stef-doc/tests/snapshots/render__render@attributes.stef.snap @@ -0,0 +1,102 @@ +--- +source: crates/stef-doc/tests/render.rs +description: "#[deprecated = \"don't use\", compress]\n#[validate(\n in_range(min = 100, max = 200),\n non_empty,\n)]\nstruct Sample" +input_file: crates/stef-parser/tests/inputs/attributes.stef +--- +--- attributes/index.html + + + + + + + + + + + attributes - Stef + + + +
+
+

Schema attributes

+
+
+

Modules

+ +
+
+
+

Structs

+ + + + + +
+ Sample + +
+
+
+

Enums

+ +
+
+
+

Aliases

+ +
+
+
+

Constants

+ +
+
+
+ + + +--- attributes/struct.Sample.html + + + + + + + + + + + Sample - Stef + + + +
+
+

+ Struct + attributes::Sample +

+
#[deprecated = "don't use", compress, validate(in_range(min = 100, max = 200), non_empty)]
+struct Sample
+
+
+ +
+
+
+

Metadata

+
+
+
+
+

Fields

+
+ +
+ + diff --git a/crates/stef-doc/tests/snapshots/render__render@attributes_min_ws.stef.snap b/crates/stef-doc/tests/snapshots/render__render@attributes_min_ws.stef.snap new file mode 100644 index 0000000..e02ec5d --- /dev/null +++ b/crates/stef-doc/tests/snapshots/render__render@attributes_min_ws.stef.snap @@ -0,0 +1,102 @@ +--- +source: crates/stef-doc/tests/render.rs +description: "#[deprecated=\"don't use\",compress]\n#[validate(in_range(min=100,max=200),non_empty)]\nstruct Sample" +input_file: crates/stef-parser/tests/inputs/attributes_min_ws.stef +--- +--- attributes_min_ws/index.html + + + + + + + + + + + attributes_min_ws - Stef + + + +
+
+

Schema attributes_min_ws

+
+
+

Modules

+ +
+
+
+

Structs

+ + + + + +
+ Sample + +
+
+
+

Enums

+ +
+
+
+

Aliases

+ +
+
+
+

Constants

+ +
+
+
+ + + +--- attributes_min_ws/struct.Sample.html + + + + + + + + + + + Sample - Stef + + + +
+
+

+ Struct + attributes_min_ws::Sample +

+
#[deprecated = "don't use", compress, validate(in_range(min = 100, max = 200), non_empty)]
+struct Sample
+
+
+ +
+
+
+

Metadata

+
+
+
+
+

Fields

+
+ +
+ + diff --git a/crates/stef-doc/tests/snapshots/render__render@const_basic.stef.snap b/crates/stef-doc/tests/snapshots/render__render@const_basic.stef.snap new file mode 100644 index 0000000..c3870be --- /dev/null +++ b/crates/stef-doc/tests/snapshots/render__render@const_basic.stef.snap @@ -0,0 +1,287 @@ +--- +source: crates/stef-doc/tests/render.rs +description: "const BOOL_TRUE: bool = true;\nconst BOOL_FALSE: bool = false;\nconst INT: u32 = 100;\nconst FLOAT: f64 = 5.5;\nconst STRING: string = \"value\";\nconst BYTES: bytes = [1, 2, 3];" +input_file: crates/stef-parser/tests/inputs/const_basic.stef +--- +--- const_basic/index.html + + + + + + + + + + + const_basic - Stef + + + +
+
+

Schema const_basic

+
+
+

Modules

+ +
+
+
+

Structs

+ +
+
+
+

Enums

+ +
+
+
+

Aliases

+ +
+
+
+

Constants

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ BOOL_TRUE + +
+ BOOL_FALSE + +
+ INT + +
+ FLOAT + +
+ STRING + +
+ BYTES + +
+
+
+ + + +--- const_basic/constant.BOOL_TRUE.html + + + + + + + + + + + BOOL_TRUE - Stef + + + +
+
+

+ Constant + const_basic::BOOL_TRUE +

+
const BOOL_TRUE: bool = true;
+
+ +
+
+ +
+ + + +--- const_basic/constant.BOOL_FALSE.html + + + + + + + + + + + BOOL_FALSE - Stef + + + +
+
+

+ Constant + const_basic::BOOL_FALSE +

+
const BOOL_FALSE: bool = false;
+
+ +
+
+ +
+ + + +--- const_basic/constant.INT.html + + + + + + + + + + + INT - Stef + + + +
+
+

+ Constant + const_basic::INT +

+
const INT: u32 = 100;
+
+ +
+
+ +
+ + + +--- const_basic/constant.FLOAT.html + + + + + + + + + + + FLOAT - Stef + + + +
+
+

+ Constant + const_basic::FLOAT +

+
const FLOAT: f64 = 5.5;
+
+ +
+
+ +
+ + + +--- const_basic/constant.STRING.html + + + + + + + + + + + STRING - Stef + + + +
+
+

+ Constant + const_basic::STRING +

+
const STRING: string = "value";
+
+ +
+
+ +
+ + + +--- const_basic/constant.BYTES.html + + + + + + + + + + + BYTES - Stef + + + +
+
+

+ Constant + const_basic::BYTES +

+
const BYTES: bytes = [1, 2, 3];
+
+ +
+
+ +
+ + diff --git a/crates/stef-doc/tests/snapshots/render__render@const_string.stef.snap b/crates/stef-doc/tests/snapshots/render__render@const_string.stef.snap new file mode 100644 index 0000000..25ce6e5 --- /dev/null +++ b/crates/stef-doc/tests/snapshots/render__render@const_string.stef.snap @@ -0,0 +1,209 @@ +--- +source: crates/stef-doc/tests/render.rs +description: "const SIMPLE: string = \"value\";\n\nconst NEWLINE_ESCAPE: string = \"one \\\n two \\\n three\\\n\";\n\nconst ESCAPES: string = \"escape basics \\r\\n \\t \\b \\f \\\\ \\\"\\\n hello\\\" \\n\\\n unicode \\u{2764} \\\n emoji ❤ \\\n\";\n\nconst MULTILINE: string = \"a\n b\n c\n\";" +input_file: crates/stef-parser/tests/inputs/const_string.stef +--- +--- const_string/index.html + + + + + + + + + + + const_string - Stef + + + +
+
+

Schema const_string

+
+
+

Modules

+ +
+
+
+

Structs

+ +
+
+
+

Enums

+ +
+
+
+

Aliases

+ +
+
+
+

Constants

+ + + + + + + + + + + + + + + + + +
+ SIMPLE + +
+ NEWLINE_ESCAPE + +
+ ESCAPES + +
+ MULTILINE + +
+
+
+ + + +--- const_string/constant.SIMPLE.html + + + + + + + + + + + SIMPLE - Stef + + + +
+
+

+ Constant + const_string::SIMPLE +

+
const SIMPLE: string = "value";
+
+ +
+
+ +
+ + + +--- const_string/constant.NEWLINE_ESCAPE.html + + + + + + + + + + + NEWLINE_ESCAPE - Stef + + + +
+
+

+ Constant + const_string::NEWLINE_ESCAPE +

+
const NEWLINE_ESCAPE: string = "one two three";
+
+ +
+
+ +
+ + + +--- const_string/constant.ESCAPES.html + + + + + + + + + + + ESCAPES - Stef + + + +
+
+

+ Constant + const_string::ESCAPES +

+
const ESCAPES: string = "escape basics \r\n \t \u{8} \u{c} \\ \"hello\" \nunicode ❤  emoji ❤ ";
+
+ +
+
+ +
+ + + +--- const_string/constant.MULTILINE.html + + + + + + + + + + + MULTILINE - Stef + + + +
+
+

+ Constant + const_string::MULTILINE +

+
const MULTILINE: string = "a\n    b\n    c\n";
+
+ +
+
+ +
+ + diff --git a/crates/stef-doc/tests/snapshots/render__render@enum_basic.stef.snap b/crates/stef-doc/tests/snapshots/render__render@enum_basic.stef.snap new file mode 100644 index 0000000..36342d2 --- /dev/null +++ b/crates/stef-doc/tests/snapshots/render__render@enum_basic.stef.snap @@ -0,0 +1,209 @@ +--- +source: crates/stef-doc/tests/render.rs +description: "/// Sample enum.\nenum Sample {\n One @1,\n /// Second variant\n Two(u32 @1, u64 @2) @2,\n Three {\n field1: u32 @1,\n /// Second field of third variant\n field2: bool @2,\n } @3,\n}" +input_file: crates/stef-parser/tests/inputs/enum_basic.stef +--- +--- enum_basic/index.html + + + + + + + + + + + enum_basic - Stef + + + +
+
+

Schema enum_basic

+
+
+

Modules

+ +
+
+
+

Structs

+ +
+
+
+

Enums

+ + + + + +
+ Sample + +

Sample enum.

+
+
+
+

Aliases

+ +
+
+
+

Constants

+ +
+
+
+ + + +--- enum_basic/enum.Sample.html + + + + + + + + + + + Sample - Stef + + + +
+
+

+ Enum + enum_basic::Sample +

+
/// Sample enum.
+enum Sample {
+    One @1,
+    /// Second variant
+    Two(u32 @1, u64 @2) @2,
+    Three {
+        field1: u32 @1,
+        /// Second field of third variant
+        field2: bool @2,
+    } @3,
+}
+
+
+

Sample enum.

+
+
+
+

Variants

+
    +
  • +
    One
    +
    + +
    +
    +
    +
  • +
  • +
    Two
    +
    +

    Second variant

    +
    +
    +

    Fields

    +
      +
    • + + 0: + u32 + @1 + +
    • +

      Metadata

      +
      +

      The size range is:

      +
      +

      u32 1..5

      +
      +
      +
    • + + 1: + u64 + @2 + +
    • +

      Metadata

      +
      +

      The size range is:

      +
      +

      u64 1..10

      +
      +
      +
    +
    +
  • +
  • +
    Three
    +
    + +
    +
    +

    Fields

    +
      +
    • + + field1: + u32 + @1 + +
      + +
      +
      +

      Metadata

      +
      +

      The size range is:

      +
      +

      u32 1..5

      +
      +
      +
      +
    • +
    • + + field2: + bool + @2 + +
      +

      Second field of third variant

      +
      +
      +

      Metadata

      +
      +

      The size range is:

      +
      +

      bool 1

      +
      +
      +
      +
    • +
    +
    +
  • +
+
+ +
+ + diff --git a/crates/stef-doc/tests/snapshots/render__render@enum_generics.stef.snap b/crates/stef-doc/tests/snapshots/render__render@enum_generics.stef.snap new file mode 100644 index 0000000..1b66dc3 --- /dev/null +++ b/crates/stef-doc/tests/snapshots/render__render@enum_generics.stef.snap @@ -0,0 +1,171 @@ +--- +source: crates/stef-doc/tests/render.rs +description: "/// Enum with generics.\nenum Sample {\n One @1,\n Two(A @1, B @2) @2,\n Three {\n field1: C @1,\n field2: D @2,\n } @3,\n}" +input_file: crates/stef-parser/tests/inputs/enum_generics.stef +--- +--- enum_generics/index.html + + + + + + + + + + + enum_generics - Stef + + + +
+
+

Schema enum_generics

+
+
+

Modules

+ +
+
+
+

Structs

+ +
+
+
+

Enums

+ + + + + +
+ Sample + +

Enum with generics.

+
+
+
+

Aliases

+ +
+
+
+

Constants

+ +
+
+
+ + + +--- enum_generics/enum.Sample.html + + + + + + + + + + + Sample - Stef + + + +
+
+

+ Enum + enum_generics::Sample +

+
/// Enum with generics.
+enum Sample<A, B, C, D> {
+    One @1,
+    Two(A @1, B @2) @2,
+    Three {
+        field1: C @1,
+        field2: D @2,
+    } @3,
+}
+
+
+

Enum with generics.

+
+
+
+

Variants

+
    +
  • +
    One
    +
    + +
    +
    +
    +
  • +
  • +
    Two
    +
    + +
    +
    +

    Fields

    +
      +
    • + + 0: + A + @1 + +
    • +
    • + + 1: + B + @2 + +
    • +
    +
    +
  • +
  • +
    Three
    +
    + +
    +
    +

    Fields

    +
      +
    • + + field1: + C + @1 + +
      + +
      +
    • +
    • + + field2: + D + @2 + +
      + +
      +
    • +
    +
    +
  • +
+
+ +
+ + diff --git a/crates/stef-doc/tests/snapshots/render__render@enum_many_ws.stef.snap b/crates/stef-doc/tests/snapshots/render__render@enum_many_ws.stef.snap new file mode 100644 index 0000000..eccd595 --- /dev/null +++ b/crates/stef-doc/tests/snapshots/render__render@enum_many_ws.stef.snap @@ -0,0 +1,207 @@ +--- +source: crates/stef-doc/tests/render.rs +description: "/// Sample enum.\n enum Sample {\n\n One @1,\n\n Two ( u32 @1, u64 @2) @2,\n\n Three {\n\n field1: u32 @1,\n\n field2: bool @2,\n\n } @3,\n\n }" +input_file: crates/stef-parser/tests/inputs/enum_many_ws.stef +--- +--- enum_many_ws/index.html + + + + + + + + + + + enum_many_ws - Stef + + + +
+
+

Schema enum_many_ws

+
+
+

Modules

+ +
+
+
+

Structs

+ +
+
+
+

Enums

+ + + + + +
+ Sample + +

Sample enum.

+
+
+
+

Aliases

+ +
+
+
+

Constants

+ +
+
+
+ + + +--- enum_many_ws/enum.Sample.html + + + + + + + + + + + Sample - Stef + + + +
+
+

+ Enum + enum_many_ws::Sample +

+
/// Sample enum.
+enum Sample {
+    One @1,
+    Two(u32 @1, u64 @2) @2,
+    Three {
+        field1: u32 @1,
+        field2: bool @2,
+    } @3,
+}
+
+
+

Sample enum.

+
+
+
+

Variants

+
    +
  • +
    One
    +
    + +
    +
    +
    +
  • +
  • +
    Two
    +
    + +
    +
    +

    Fields

    +
      +
    • + + 0: + u32 + @1 + +
    • +

      Metadata

      +
      +

      The size range is:

      +
      +

      u32 1..5

      +
      +
      +
    • + + 1: + u64 + @2 + +
    • +

      Metadata

      +
      +

      The size range is:

      +
      +

      u64 1..10

      +
      +
      +
    +
    +
  • +
  • +
    Three
    +
    + +
    +
    +

    Fields

    +
      +
    • + + field1: + u32 + @1 + +
      + +
      +
      +

      Metadata

      +
      +

      The size range is:

      +
      +

      u32 1..5

      +
      +
      +
      +
    • +
    • + + field2: + bool + @2 + +
      + +
      +
      +

      Metadata

      +
      +

      The size range is:

      +
      +

      bool 1

      +
      +
      +
      +
    • +
    +
    +
  • +
+
+ +
+ + diff --git a/crates/stef-doc/tests/snapshots/render__render@enum_min_ws.stef.snap b/crates/stef-doc/tests/snapshots/render__render@enum_min_ws.stef.snap new file mode 100644 index 0000000..7ab8b82 --- /dev/null +++ b/crates/stef-doc/tests/snapshots/render__render@enum_min_ws.stef.snap @@ -0,0 +1,223 @@ +--- +source: crates/stef-doc/tests/render.rs +description: "enum Sample{One@1,Two(u32@1,u64@2,T@3)@2,Three{field1:u32@1,field2:bool@2,field3:T@3}@3}" +input_file: crates/stef-parser/tests/inputs/enum_min_ws.stef +--- +--- enum_min_ws/index.html + + + + + + + + + + + enum_min_ws - Stef + + + +
+
+

Schema enum_min_ws

+
+
+

Modules

+ +
+
+
+

Structs

+ +
+
+
+

Enums

+ + + + + +
+ Sample + +
+
+
+

Aliases

+ +
+
+
+

Constants

+ +
+
+
+ + + +--- enum_min_ws/enum.Sample.html + + + + + + + + + + + Sample - Stef + + + +
+
+

+ Enum + enum_min_ws::Sample +

+
enum Sample<T> {
+    One @1,
+    Two(u32 @1, u64 @2, T @3) @2,
+    Three {
+        field1: u32 @1,
+        field2: bool @2,
+        field3: T @3,
+    } @3,
+}
+
+
+ +
+
+
+

Variants

+
    +
  • +
    One
    +
    + +
    +
    +
    +
  • +
  • +
    Two
    +
    + +
    +
    +

    Fields

    +
      +
    • + + 0: + u32 + @1 + +
    • +

      Metadata

      +
      +

      The size range is:

      +
      +

      u32 1..5

      +
      +
      +
    • + + 1: + u64 + @2 + +
    • +

      Metadata

      +
      +

      The size range is:

      +
      +

      u64 1..10

      +
      +
      +
    • + + 2: + T + @3 + +
    • +
    +
    +
  • +
  • +
    Three
    +
    + +
    +
    +

    Fields

    +
      +
    • + + field1: + u32 + @1 + +
      + +
      +
      +

      Metadata

      +
      +

      The size range is:

      +
      +

      u32 1..5

      +
      +
      +
      +
    • +
    • + + field2: + bool + @2 + +
      + +
      +
      +

      Metadata

      +
      +

      The size range is:

      +
      +

      bool 1

      +
      +
      +
      +
    • +
    • + + field3: + T + @3 + +
      + +
      +
    • +
    +
    +
  • +
+
+ +
+ + diff --git a/crates/stef-doc/tests/snapshots/render__render@import_basic.stef.snap b/crates/stef-doc/tests/snapshots/render__render@import_basic.stef.snap new file mode 100644 index 0000000..1342ced --- /dev/null +++ b/crates/stef-doc/tests/snapshots/render__render@import_basic.stef.snap @@ -0,0 +1,53 @@ +--- +source: crates/stef-doc/tests/render.rs +description: "use other::schema::Sample;\nuse second::submodule;" +input_file: crates/stef-parser/tests/inputs/import_basic.stef +--- +--- import_basic/index.html + + + + + + + + + + + import_basic - Stef + + + +
+
+

Schema import_basic

+
+
+

Modules

+ +
+
+
+

Structs

+ +
+
+
+

Enums

+ +
+
+
+

Aliases

+ +
+
+
+

Constants

+ +
+
+
+ + diff --git a/crates/stef-doc/tests/snapshots/render__render@mixed.stef.snap b/crates/stef-doc/tests/snapshots/render__render@mixed.stef.snap new file mode 100644 index 0000000..95db2ef --- /dev/null +++ b/crates/stef-doc/tests/snapshots/render__render@mixed.stef.snap @@ -0,0 +1,1054 @@ +--- +source: crates/stef-doc/tests/render.rs +description: "/// Basic user information.\n///\n/// Uses various other `structs` and `enums` to compose the information\n/// in a **type safe** way.\nstruct User {\n name: FullName @1,\n /// Physical address, might not be specified by the user.\n address: option
@2,\n age: u8 @3,\n birthday: birthday::DayOfBirth @4,\n}\n\n/// Full name of a user.\nstruct FullName {\n first: string @1,\n middle: option @2,\n last: string @3,\n}\n\n/// Simple alias for convenience.\n///\n/// - Might be easier to remember.\n/// - Often referenced as this.\ntype Name = FullName;\n\nstruct Address {\n /// Street name.\n street: string @1,\n /// Number of the house in the street.\n house_no: HouseNumber @2,\n city: string @3,\n}\n\n/// The number on the house.\n///\n/// More details can be found at [Wikipedia](https://en.wikipedia.org/wiki/House_numbering).\nenum HouseNumber {\n /// Digit only number.\n Digit(u16 @1) @1,\n /// Mixed _number_ with characters like `1a`.\n Text(string @1) @2,\n}\n\n/// Probably the max age of a human, currently.\nconst MAX_AGE: u8 = 120;\n\n\n/// Details for defining birthdays.\nmod birthday {\n /// As the name suggests, specifies details about birthdays.\n enum DayOfBirth {\n Specific {\n year: u16 @1,\n month: Month @2,\n day: u8 @3,\n } @1,\n /// The user didn't want to say.\n Secret {\n /// Optional info from the user about why they didn't want to\n /// reveal their birthday.\n reason: option @1,\n } @2,\n /// We never asked and nobody knows.\n Unknown @3,\n }\n\n /// Let's assume we only have details of people born **after** this year.\n const MIN_YEAR: u16 = 1900;\n\n /// Absolute maximum for a day, but might be even less depending\n /// on the month.\n const MAX_DAY: u8 = 31;\n\n /// The month of the year.\n enum Month {\n January @1,\n February @2,\n March @3,\n April @4,\n May @5,\n June @6,\n July @7,\n August @8,\n September @9,\n October @10,\n November @11,\n December @12,\n }\n}" +input_file: crates/stef-parser/tests/inputs/mixed.stef +--- +--- mixed/index.html + + + + + + + + + + + mixed - Stef + + + +
+
+

Schema mixed

+
+
+

Modules

+ + + + + +
+ birthday + +

Details for defining birthdays.

+
+
+
+

Structs

+ + + + + + + + + + + + + +
+ User + +

Basic user information.

+
+ FullName + +

Full name of a user.

+
+ Address + +
+
+
+

Enums

+ + + + + +
+ HouseNumber + +

The number on the house.

+
+
+
+

Aliases

+ + + + + +
+ Name + +

Simple alias for convenience.

+
+
+
+

Constants

+ + + + + +
+ MAX_AGE + +

Probably the max age of a human, currently.

+
+
+
+ + + +--- mixed/struct.User.html + + + + + + + + + + + User - Stef + + + +
+
+

+ Struct + mixed::User +

+
/// Basic user information.
+/// 
+/// Uses various other `structs` and `enums` to compose the information
+/// in a **type safe** way.
+struct User {
+    name: FullName @1,
+    /// Physical address, might not be specified by the user.
+    address: option<Address> @2,
+    age: u8 @3,
+    birthday: birthday::DayOfBirth @4,
+}
+
+
+

Basic user information.

+

Uses various other structs and enums to compose the information +in a type safe way.

+
+
+
+

Metadata

+
+

The next free ID is 5

+
+
+
+

Fields

+
    +
  • + + name: + FullName + @1 + +
    + +
    +
  • +
  • + + address: + option<Address> + @2 + +
    +

    Physical address, might not be specified by the user.

    +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    option 0..

    +
      +
    • value: unknown
    • +
    +
    +
    +
  • +
  • + + age: + u8 + @3 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    u8 1

    +
    +
    +
  • +
  • + + birthday: + birthday::DayOfBirth + @4 + +
    + +
    +
  • +
+ +
+ + + +--- mixed/struct.FullName.html + + + + + + + + + + + FullName - Stef + + + +
+
+

+ Struct + mixed::FullName +

+
/// Full name of a user.
+struct FullName {
+    first: string @1,
+    middle: option<string> @2,
+    last: string @3,
+}
+
+
+

Full name of a user.

+
+
+
+

Metadata

+
+

The next free ID is 4

+
+
+
+

Fields

+
    +
  • + + first: + string + @1 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    string 1..

    +
    +
    +
  • +
  • + + middle: + option<string> + @2 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    option 0..

    +
      +
    • value: string 1..
    • +
    +
    +
    +
  • +
  • + + last: + string + @3 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    string 1..

    +
    +
    +
  • +
+ +
+ + + +--- mixed/alias.Name.html + + + + + + + + + + + Name - Stef + + + +
+
+

+ Alias + mixed::Name +

+
/// Simple alias for convenience.
+/// 
+/// - Might be easier to remember.
+/// - Often referenced as this.
+type Name = FullName;
+
+

Simple alias for convenience.

+
    +
  • Might be easier to remember.
  • +
  • Often referenced as this.
  • +
+
+
+ +
+ + + +--- mixed/struct.Address.html + + + + + + + + + + + Address - Stef + + + +
+
+

+ Struct + mixed::Address +

+
struct Address {
+    /// Street name.
+    street: string @1,
+    /// Number of the house in the street.
+    house_no: HouseNumber @2,
+    city: string @3,
+}
+
+
+ +
+
+
+

Metadata

+
+

The next free ID is 4

+
+
+
+

Fields

+
    +
  • + + street: + string + @1 + +
    +

    Street name.

    +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    string 1..

    +
    +
    +
  • +
  • + + house_no: + HouseNumber + @2 + +
    +

    Number of the house in the street.

    +
    +
  • +
  • + + city: + string + @3 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    string 1..

    +
    +
    +
  • +
+ +
+ + + +--- mixed/enum.HouseNumber.html + + + + + + + + + + + HouseNumber - Stef + + + +
+
+

+ Enum + mixed::HouseNumber +

+
/// The number on the house.
+/// 
+/// More details can be found at [Wikipedia](https://en.wikipedia.org/wiki/House_numbering).
+enum HouseNumber {
+    /// Digit only number.
+    Digit(u16 @1) @1,
+    /// Mixed _number_ with characters like `1a`.
+    Text(string @1) @2,
+}
+
+
+

The number on the house.

+

More details can be found at Wikipedia.

+
+
+
+

Variants

+
    +
  • +
    Digit
    +
    +

    Digit only number.

    +
    +
    +

    Fields

    +
      +
    • + + 0: + u16 + @1 + +
    • +

      Metadata

      +
      +

      The size range is:

      +
      +

      u16 1..3

      +
      +
      +
    +
    +
  • +
  • +
    Text
    +
    +

    Mixed number with characters like 1a.

    +
    +
    +

    Fields

    +
      +
    • + + 0: + string + @1 + +
    • +

      Metadata

      +
      +

      The size range is:

      +
      +

      string 1..

      +
      +
      +
    +
    +
  • +
+
+ +
+ + + +--- mixed/constant.MAX_AGE.html + + + + + + + + + + + MAX_AGE - Stef + + + +
+
+

+ Constant + mixed::MAX_AGE +

+
/// Probably the max age of a human, currently.
+const MAX_AGE: u8 = 120;
+
+

Probably the max age of a human, currently.

+
+
+ +
+ + + +--- mixed/birthday/index.html + + + + + + + + + + + birthday - Stef + + + +
+
+

+ Module + mixed::birthday +

+
+

Details for defining birthdays.

+
+
+
+

Modules

+ +
+
+
+

Structs

+ +
+
+
+

Enums

+ + + + + + + + + +
+ DayOfBirth + +

As the name suggests, specifies details about birthdays.

+
+ Month + +

The month of the year.

+
+
+
+

Aliases

+ +
+
+
+

Constants

+ + + + + + + + + +
+ MIN_YEAR + +

Let's assume we only have details of people born after this year.

+
+ MAX_DAY + +

Absolute maximum for a day, but might be even less depending

+
+
+
+ + + +--- mixed/birthday/enum.DayOfBirth.html + + + + + + + + + + + DayOfBirth - Stef + + + +
+
+

+ Enum + mixed::birthday::DayOfBirth +

+
/// As the name suggests, specifies details about birthdays.
+enum DayOfBirth {
+    Specific {
+        year: u16 @1,
+        month: Month @2,
+        day: u8 @3,
+    } @1,
+    /// The user didn't want to say.
+    Secret {
+        /// Optional info from the user about why they didn't want to
+        /// reveal their birthday.
+        reason: option<string> @1,
+    } @2,
+    /// We never asked and nobody knows.
+    Unknown @3,
+}
+
+
+

As the name suggests, specifies details about birthdays.

+
+
+
+

Variants

+
    +
  • +
    Specific
    +
    + +
    +
    +

    Fields

    +
      +
    • + + year: + u16 + @1 + +
      + +
      +
      +

      Metadata

      +
      +

      The size range is:

      +
      +

      u16 1..3

      +
      +
      +
      +
    • +
    • + + month: + Month + @2 + +
      + +
      +
    • +
    • + + day: + u8 + @3 + +
      + +
      +
      +

      Metadata

      +
      +

      The size range is:

      +
      +

      u8 1

      +
      +
      +
      +
    • +
    +
    +
  • +
  • +
    Secret
    +
    +

    The user didn't want to say.

    +
    +
    +

    Fields

    +
      +
    • + + reason: + option<string> + @1 + +
      +

      Optional info from the user about why they didn't want to +reveal their birthday.

      +
      +
      +

      Metadata

      +
      +

      The size range is:

      +
      +

      option 0..

      +
        +
      • value: string 1..
      • +
      +
      +
      +
      +
    • +
    +
    +
  • +
  • +
    Unknown
    +
    +

    We never asked and nobody knows.

    +
    +
    +
    +
  • +
+
+ +
+ + + +--- mixed/birthday/constant.MIN_YEAR.html + + + + + + + + + + + MIN_YEAR - Stef + + + +
+
+

+ Constant + mixed::birthday::MIN_YEAR +

+
/// Let's assume we only have details of people born **after** this year.
+const MIN_YEAR: u16 = 1900;
+
+

Let's assume we only have details of people born after this year.

+
+
+ +
+ + + +--- mixed/birthday/constant.MAX_DAY.html + + + + + + + + + + + MAX_DAY - Stef + + + +
+
+

+ Constant + mixed::birthday::MAX_DAY +

+
/// Absolute maximum for a day, but might be even less depending
+/// on the month.
+const MAX_DAY: u8 = 31;
+
+

Absolute maximum for a day, but might be even less depending +on the month.

+
+
+ +
+ + + +--- mixed/birthday/enum.Month.html + + + + + + + + + + + Month - Stef + + + +
+
+

+ Enum + mixed::birthday::Month +

+
/// The month of the year.
+enum Month {
+    January @1,
+    February @2,
+    March @3,
+    April @4,
+    May @5,
+    June @6,
+    July @7,
+    August @8,
+    September @9,
+    October @10,
+    November @11,
+    December @12,
+}
+
+
+

The month of the year.

+
+
+
+

Variants

+
    +
  • +
    January
    +
    + +
    +
    +
    +
  • +
  • +
    February
    +
    + +
    +
    +
    +
  • +
  • +
    March
    +
    + +
    +
    +
    +
  • +
  • +
    April
    +
    + +
    +
    +
    +
  • +
  • +
    May
    +
    + +
    +
    +
    +
  • +
  • +
    June
    +
    + +
    +
    +
    +
  • +
  • +
    July
    +
    + +
    +
    +
    +
  • +
  • +
    August
    +
    + +
    +
    +
    +
  • +
  • +
    September
    +
    + +
    +
    +
    +
  • +
  • +
    October
    +
    + +
    +
    +
    +
  • +
  • +
    November
    +
    + +
    +
    +
    +
  • +
  • +
    December
    +
    + +
    +
    +
    +
  • +
+
+ +
+ + diff --git a/crates/stef-doc/tests/snapshots/render__render@module_basic.stef.snap b/crates/stef-doc/tests/snapshots/render__render@module_basic.stef.snap new file mode 100644 index 0000000..755c060 --- /dev/null +++ b/crates/stef-doc/tests/snapshots/render__render@module_basic.stef.snap @@ -0,0 +1,314 @@ +--- +source: crates/stef-doc/tests/render.rs +description: "mod a {\n /// Inner module\n mod b {\n enum Sample {\n One @1,\n }\n }\n\n struct Sample {\n value: u32 @1,\n inner: b::Sample @2,\n }\n}" +input_file: crates/stef-parser/tests/inputs/module_basic.stef +--- +--- module_basic/index.html + + + + + + + + + + + module_basic - Stef + + + +
+
+

Schema module_basic

+
+
+

Modules

+ + + + + +
+ a + +
+
+
+

Structs

+ +
+
+
+

Enums

+ +
+
+
+

Aliases

+ +
+
+
+

Constants

+ +
+
+
+ + + +--- module_basic/a/index.html + + + + + + + + + + + a - Stef + + + +
+
+

+ Module + module_basic::a +

+
+ +
+
+
+

Modules

+ + + + + +
+ b + +

Inner module

+
+
+
+

Structs

+ + + + + +
+ Sample + +
+
+
+

Enums

+ +
+
+
+

Aliases

+ +
+
+
+

Constants

+ +
+
+
+ + + +--- module_basic/a/b/index.html + + + + + + + + + + + b - Stef + + + +
+
+

+ Module + module_basic::a::b +

+
+

Inner module

+
+
+
+

Modules

+ +
+
+
+

Structs

+ +
+
+
+

Enums

+ + + + + +
+ Sample + +
+
+
+

Aliases

+ +
+
+
+

Constants

+ +
+
+
+ + + +--- module_basic/a/b/enum.Sample.html + + + + + + + + + + + Sample - Stef + + + +
+
+

+ Enum + module_basic::a::b::Sample +

+
enum Sample {
+    One @1,
+}
+
+
+ +
+
+
+

Variants

+
    +
  • +
    One
    +
    + +
    +
    +
    +
  • +
+
+ +
+ + + +--- module_basic/a/struct.Sample.html + + + + + + + + + + + Sample - Stef + + + +
+
+

+ Struct + module_basic::a::Sample +

+
struct Sample {
+    value: u32 @1,
+    inner: b::Sample @2,
+}
+
+
+ +
+
+
+

Metadata

+
+

The next free ID is 3

+
+
+
+

Fields

+
    +
  • + + value: + u32 + @1 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    u32 1..5

    +
    +
    +
  • +
  • + + inner: + b::Sample + @2 + +
    + +
    +
  • +
+ +
+ + diff --git a/crates/stef-doc/tests/snapshots/render__render@schema_basic.stef.snap b/crates/stef-doc/tests/snapshots/render__render@schema_basic.stef.snap new file mode 100644 index 0000000..232b4df --- /dev/null +++ b/crates/stef-doc/tests/snapshots/render__render@schema_basic.stef.snap @@ -0,0 +1,298 @@ +--- +source: crates/stef-doc/tests/render.rs +description: "/// Basic struct.\nstruct SampleStruct {\n a: u32 @1,\n b: bool @2,\n}\n\n/// Sample enum.\nenum SampleEnum {\n One @1,\n Two(u32 @1, u64 @2) @2,\n Three {\n field1: u32 @1,\n field2: bool @2,\n } @3,\n}" +input_file: crates/stef-parser/tests/inputs/schema_basic.stef +--- +--- schema_basic/index.html + + + + + + + + + + + schema_basic - Stef + + + +
+
+

Schema schema_basic

+
+
+

Modules

+ +
+
+
+

Structs

+ + + + + +
+ SampleStruct + +

Basic struct.

+
+
+
+

Enums

+ + + + + +
+ SampleEnum + +

Sample enum.

+
+
+
+

Aliases

+ +
+
+
+

Constants

+ +
+
+
+ + + +--- schema_basic/struct.SampleStruct.html + + + + + + + + + + + SampleStruct - Stef + + + +
+
+

+ Struct + schema_basic::SampleStruct +

+
/// Basic struct.
+struct SampleStruct {
+    a: u32 @1,
+    b: bool @2,
+}
+
+
+

Basic struct.

+
+
+
+

Metadata

+
+

The next free ID is 3

+
+
+
+

Fields

+
    +
  • + + a: + u32 + @1 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    u32 1..5

    +
    +
    +
  • +
  • + + b: + bool + @2 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    bool 1

    +
    +
    +
  • +
+ +
+ + + +--- schema_basic/enum.SampleEnum.html + + + + + + + + + + + SampleEnum - Stef + + + +
+
+

+ Enum + schema_basic::SampleEnum +

+
/// Sample enum.
+enum SampleEnum {
+    One @1,
+    Two(u32 @1, u64 @2) @2,
+    Three {
+        field1: u32 @1,
+        field2: bool @2,
+    } @3,
+}
+
+
+

Sample enum.

+
+
+
+

Variants

+
    +
  • +
    One
    +
    + +
    +
    +
    +
  • +
  • +
    Two
    +
    + +
    +
    +

    Fields

    +
      +
    • + + 0: + u32 + @1 + +
    • +

      Metadata

      +
      +

      The size range is:

      +
      +

      u32 1..5

      +
      +
      +
    • + + 1: + u64 + @2 + +
    • +

      Metadata

      +
      +

      The size range is:

      +
      +

      u64 1..10

      +
      +
      +
    +
    +
  • +
  • +
    Three
    +
    + +
    +
    +

    Fields

    +
      +
    • + + field1: + u32 + @1 + +
      + +
      +
      +

      Metadata

      +
      +

      The size range is:

      +
      +

      u32 1..5

      +
      +
      +
      +
    • +
    • + + field2: + bool + @2 + +
      + +
      +
      +

      Metadata

      +
      +

      The size range is:

      +
      +

      bool 1

      +
      +
      +
      +
    • +
    +
    +
  • +
+
+ +
+ + diff --git a/crates/stef-doc/tests/snapshots/render__render@struct_basic.stef.snap b/crates/stef-doc/tests/snapshots/render__render@struct_basic.stef.snap new file mode 100644 index 0000000..6ac5fd2 --- /dev/null +++ b/crates/stef-doc/tests/snapshots/render__render@struct_basic.stef.snap @@ -0,0 +1,145 @@ +--- +source: crates/stef-doc/tests/render.rs +description: "/// Basic struct.\nstruct Sample {\n a: u32 @1,\n /// Second field\n b: bool @2,\n}" +input_file: crates/stef-parser/tests/inputs/struct_basic.stef +--- +--- struct_basic/index.html + + + + + + + + + + + struct_basic - Stef + + + +
+
+

Schema struct_basic

+
+
+

Modules

+ +
+
+
+

Structs

+ + + + + +
+ Sample + +

Basic struct.

+
+
+
+

Enums

+ +
+
+
+

Aliases

+ +
+
+
+

Constants

+ +
+
+
+ + + +--- struct_basic/struct.Sample.html + + + + + + + + + + + Sample - Stef + + + +
+
+

+ Struct + struct_basic::Sample +

+
/// Basic struct.
+struct Sample {
+    a: u32 @1,
+    /// Second field
+    b: bool @2,
+}
+
+
+

Basic struct.

+
+
+
+

Metadata

+
+

The next free ID is 3

+
+
+
+

Fields

+
    +
  • + + a: + u32 + @1 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    u32 1..5

    +
    +
    +
  • +
  • + + b: + bool + @2 + +
    +

    Second field

    +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    bool 1

    +
    +
    +
  • +
+ +
+ + diff --git a/crates/stef-doc/tests/snapshots/render__render@struct_generics.stef.snap b/crates/stef-doc/tests/snapshots/render__render@struct_generics.stef.snap new file mode 100644 index 0000000..4b3a1b7 --- /dev/null +++ b/crates/stef-doc/tests/snapshots/render__render@struct_generics.stef.snap @@ -0,0 +1,128 @@ +--- +source: crates/stef-doc/tests/render.rs +description: "/// Generic key-value pair.\nstruct KeyValue {\n key: K @1,\n value: V @2,\n}" +input_file: crates/stef-parser/tests/inputs/struct_generics.stef +--- +--- struct_generics/index.html + + + + + + + + + + + struct_generics - Stef + + + +
+
+

Schema struct_generics

+
+
+

Modules

+ +
+
+
+

Structs

+ + + + + +
+ KeyValue + +

Generic key-value pair.

+
+
+
+

Enums

+ +
+
+
+

Aliases

+ +
+
+
+

Constants

+ +
+
+
+ + + +--- struct_generics/struct.KeyValue.html + + + + + + + + + + + KeyValue - Stef + + + +
+
+

+ Struct + struct_generics::KeyValue +

+
/// Generic key-value pair.
+struct KeyValue<K, V> {
+    key: K @1,
+    value: V @2,
+}
+
+
+

Generic key-value pair.

+
+
+
+

Metadata

+
+

The next free ID is 3

+
+
+
+

Fields

+
    +
  • + + key: + K + @1 + +
    + +
    +
  • +
  • + + value: + V + @2 + +
    + +
    +
  • +
+ +
+ + diff --git a/crates/stef-doc/tests/snapshots/render__render@struct_many_ws.stef.snap b/crates/stef-doc/tests/snapshots/render__render@struct_many_ws.stef.snap new file mode 100644 index 0000000..644180a --- /dev/null +++ b/crates/stef-doc/tests/snapshots/render__render@struct_many_ws.stef.snap @@ -0,0 +1,155 @@ +--- +source: crates/stef-doc/tests/render.rs +description: "/// Some comment\n struct Sample<\n T\n > {\n\n a: u32 @1,\n b: bool @2,\n c: T @3,\n }" +input_file: crates/stef-parser/tests/inputs/struct_many_ws.stef +--- +--- struct_many_ws/index.html + + + + + + + + + + + struct_many_ws - Stef + + + +
+
+

Schema struct_many_ws

+
+
+

Modules

+ +
+
+
+

Structs

+ + + + + +
+ Sample + +

Some comment

+
+
+
+

Enums

+ +
+
+
+

Aliases

+ +
+
+
+

Constants

+ +
+
+
+ + + +--- struct_many_ws/struct.Sample.html + + + + + + + + + + + Sample - Stef + + + +
+
+

+ Struct + struct_many_ws::Sample +

+
/// Some comment
+struct Sample<T> {
+    a: u32 @1,
+    b: bool @2,
+    c: T @3,
+}
+
+
+

Some comment

+
+
+
+

Metadata

+
+

The next free ID is 4

+
+
+
+

Fields

+
    +
  • + + a: + u32 + @1 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    u32 1..5

    +
    +
    +
  • +
  • + + b: + bool + @2 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    bool 1

    +
    +
    +
  • +
  • + + c: + T + @3 + +
    + +
    +
  • +
+ +
+ + diff --git a/crates/stef-doc/tests/snapshots/render__render@struct_min_ws.stef.snap b/crates/stef-doc/tests/snapshots/render__render@struct_min_ws.stef.snap new file mode 100644 index 0000000..ee832ac --- /dev/null +++ b/crates/stef-doc/tests/snapshots/render__render@struct_min_ws.stef.snap @@ -0,0 +1,153 @@ +--- +source: crates/stef-doc/tests/render.rs +description: "struct Sample{a:u32@1,b:bool@2,c:T@3}" +input_file: crates/stef-parser/tests/inputs/struct_min_ws.stef +--- +--- struct_min_ws/index.html + + + + + + + + + + + struct_min_ws - Stef + + + +
+
+

Schema struct_min_ws

+
+
+

Modules

+ +
+
+
+

Structs

+ + + + + +
+ Sample + +
+
+
+

Enums

+ +
+
+
+

Aliases

+ +
+
+
+

Constants

+ +
+
+
+ + + +--- struct_min_ws/struct.Sample.html + + + + + + + + + + + Sample - Stef + + + +
+
+

+ Struct + struct_min_ws::Sample +

+
struct Sample<T> {
+    a: u32 @1,
+    b: bool @2,
+    c: T @3,
+}
+
+
+ +
+
+
+

Metadata

+
+

The next free ID is 4

+
+
+
+

Fields

+
    +
  • + + a: + u32 + @1 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    u32 1..5

    +
    +
    +
  • +
  • + + b: + bool + @2 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    bool 1

    +
    +
    +
  • +
  • + + c: + T + @3 + +
    + +
    +
  • +
+ +
+ + diff --git a/crates/stef-doc/tests/snapshots/render__render@struct_tuple.stef.snap b/crates/stef-doc/tests/snapshots/render__render@struct_tuple.stef.snap new file mode 100644 index 0000000..23954d1 --- /dev/null +++ b/crates/stef-doc/tests/snapshots/render__render@struct_tuple.stef.snap @@ -0,0 +1,135 @@ +--- +source: crates/stef-doc/tests/render.rs +description: "/// Basic struct.\nstruct Sample(u32 @1, bool @2)" +input_file: crates/stef-parser/tests/inputs/struct_tuple.stef +--- +--- struct_tuple/index.html + + + + + + + + + + + struct_tuple - Stef + + + +
+
+

Schema struct_tuple

+
+
+

Modules

+ +
+
+
+

Structs

+ + + + + +
+ Sample + +

Basic struct.

+
+
+
+

Enums

+ +
+
+
+

Aliases

+ +
+
+
+

Constants

+ +
+
+
+ + + +--- struct_tuple/struct.Sample.html + + + + + + + + + + + Sample - Stef + + + +
+
+

+ Struct + struct_tuple::Sample +

+
/// Basic struct.
+struct Sample(u32 @1, bool @2)
+
+
+

Basic struct.

+
+
+
+

Metadata

+
+

The next free ID is 3

+
+
+
+

Fields

+
    +
  • + + 0: + u32 + @1 + +
  • +

    Metadata

    +
    +

    The size range is:

    +
    +

    u32 1..5

    +
    +
    +
  • + + 1: + bool + @2 + +
  • +

    Metadata

    +
    +

    The size range is:

    +
    +

    bool 1

    +
    +
    +
+ +
+ + diff --git a/crates/stef-doc/tests/snapshots/render__render@types_basic.stef.snap b/crates/stef-doc/tests/snapshots/render__render@types_basic.stef.snap new file mode 100644 index 0000000..75789bc --- /dev/null +++ b/crates/stef-doc/tests/snapshots/render__render@types_basic.stef.snap @@ -0,0 +1,512 @@ +--- +source: crates/stef-doc/tests/render.rs +description: "struct Sample {\n f01: bool @1,\n f02: u8 @2,\n f03: u16 @3,\n f04: u32 @4,\n f05: u64 @5,\n f06: u128 @6,\n f07: i8 @7,\n f08: i16 @8,\n f09: i32 @9,\n f10: i64 @10,\n f11: i128 @11,\n f12: f32 @12,\n f13: f64 @13,\n f14: string @14,\n f15: &string @15,\n f16: bytes @16,\n f17: &bytes @17,\n f18: box @18,\n f19: box @19,\n f20: (u32, u32, u32) @20,\n f21: [u32; 12] @21,\n}" +input_file: crates/stef-parser/tests/inputs/types_basic.stef +--- +--- types_basic/index.html + + + + + + + + + + + types_basic - Stef + + + +
+
+

Schema types_basic

+
+
+

Modules

+ +
+
+
+

Structs

+ + + + + +
+ Sample + +
+
+
+

Enums

+ +
+
+
+

Aliases

+ +
+
+
+

Constants

+ +
+
+
+ + + +--- types_basic/struct.Sample.html + + + + + + + + + + + Sample - Stef + + + +
+
+

+ Struct + types_basic::Sample +

+
struct Sample {
+    f01: bool @1,
+    f02: u8 @2,
+    f03: u16 @3,
+    f04: u32 @4,
+    f05: u64 @5,
+    f06: u128 @6,
+    f07: i8 @7,
+    f08: i16 @8,
+    f09: i32 @9,
+    f10: i64 @10,
+    f11: i128 @11,
+    f12: f32 @12,
+    f13: f64 @13,
+    f14: string @14,
+    f15: &string @15,
+    f16: bytes @16,
+    f17: &bytes @17,
+    f18: box<string> @18,
+    f19: box<bytes> @19,
+    f20: (u32, u32, u32) @20,
+    f21: [u32; 12] @21,
+}
+
+
+ +
+
+
+

Metadata

+
+

The next free ID is 22

+
+
+
+

Fields

+
    +
  • + + f01: + bool + @1 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    bool 1

    +
    +
    +
  • +
  • + + f02: + u8 + @2 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    u8 1

    +
    +
    +
  • +
  • + + f03: + u16 + @3 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    u16 1..3

    +
    +
    +
  • +
  • + + f04: + u32 + @4 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    u32 1..5

    +
    +
    +
  • +
  • + + f05: + u64 + @5 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    u64 1..10

    +
    +
    +
  • +
  • + + f06: + u128 + @6 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    u128 1..19

    +
    +
    +
  • +
  • + + f07: + i8 + @7 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    i8 1

    +
    +
    +
  • +
  • + + f08: + i16 + @8 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    i16 1..3

    +
    +
    +
  • +
  • + + f09: + i32 + @9 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    i32 1..5

    +
    +
    +
  • +
  • + + f10: + i64 + @10 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    i64 1..10

    +
    +
    +
  • +
  • + + f11: + i128 + @11 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    i128 1..19

    +
    +
    +
  • +
  • + + f12: + f32 + @12 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    f32 4

    +
    +
    +
  • +
  • + + f13: + f64 + @13 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    f64 8

    +
    +
    +
  • +
  • + + f14: + string + @14 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    string 1..

    +
    +
    +
  • +
  • + + f15: + &string + @15 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    &string 1..

    +
    +
    +
  • +
  • + + f16: + bytes + @16 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    bytes 1..

    +
    +
    +
  • +
  • + + f17: + &bytes + @17 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    &bytes 1..

    +
    +
    +
  • +
  • + + f18: + box<string> + @18 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    box<string> 1..

    +
    +
    +
  • +
  • + + f19: + box<bytes> + @19 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    box<bytes> 1..

    +
    +
    +
  • +
  • + + f20: + (u32, u32, u32) + @20 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    tuple 3..15

    +
      +
    • 0: u32 1..5
    • +
    • 1: u32 1..5
    • +
    • 2: u32 1..5
    • +
    +
    +
    +
  • +
  • + + f21: + [u32; 12] + @21 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    array 13..61

    +
      +
    • length: u64 1
    • +
    • element: u32 1..5
    • +
    +
    +
    +
  • +
+ +
+ + diff --git a/crates/stef-doc/tests/snapshots/render__render@types_generic.stef.snap b/crates/stef-doc/tests/snapshots/render__render@types_generic.stef.snap new file mode 100644 index 0000000..2baf5b6 --- /dev/null +++ b/crates/stef-doc/tests/snapshots/render__render@types_generic.stef.snap @@ -0,0 +1,362 @@ +--- +source: crates/stef-doc/tests/render.rs +description: "struct Sample {\n f1: vec @1,\n f2: hash_map @2,\n f3: hash_set @3,\n f4: option @4,\n f5: non_zero @5,\n}\n\nstruct SampleUnnamed(\n vec @1,\n hash_map @2,\n hash_set @3,\n option @4,\n non_zero @5,\n)" +input_file: crates/stef-parser/tests/inputs/types_generic.stef +--- +--- types_generic/index.html + + + + + + + + + + + types_generic - Stef + + + +
+
+

Schema types_generic

+
+
+

Modules

+ +
+
+
+

Structs

+ + + + + + + + + +
+ Sample + +
+ SampleUnnamed + +
+
+
+

Enums

+ +
+
+
+

Aliases

+ +
+
+
+

Constants

+ +
+
+
+ + + +--- types_generic/struct.Sample.html + + + + + + + + + + + Sample - Stef + + + +
+
+

+ Struct + types_generic::Sample +

+
struct Sample {
+    f1: vec<u32> @1,
+    f2: hash_map<u32, string> @2,
+    f3: hash_set<u32> @3,
+    f4: option<u32> @4,
+    f5: non_zero<u32> @5,
+}
+
+
+ +
+
+
+

Metadata

+
+

The next free ID is 6

+
+
+
+

Fields

+
    +
  • + + f1: + vec<u32> + @1 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    vec 1..

    +
      +
    • length: u64 1..10
    • +
    • element: u32 1..5
    • +
    +
    +
    +
  • +
  • + + f2: + hash_map<u32, string> + @2 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    hash_map 1..

    +
      +
    • length: u64 1..10
    • +
    • key: u32 1..5
    • +
    • value: string 1..
    • +
    +
    +
    +
  • +
  • + + f3: + hash_set<u32> + @3 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    hash_set 1..

    +
      +
    • length: u64 1..10
    • +
    • element: u32 1..5
    • +
    +
    +
    +
  • +
  • + + f4: + option<u32> + @4 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    option 0..6

    +
      +
    • value: u32 1..5
    • +
    +
    +
    +
  • +
  • + + f5: + non_zero<u32> + @5 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    non_zero 0..6

    +
      +
    • value: u32 1..5
    • +
    +
    +
    +
  • +
+ +
+ + + +--- types_generic/struct.SampleUnnamed.html + + + + + + + + + + + SampleUnnamed - Stef + + + +
+
+

+ Struct + types_generic::SampleUnnamed +

+
struct SampleUnnamed(vec<u32> @1, hash_map<u32, string> @2, hash_set<u32> @3, option<u32> @4, non_zero<u32> @5)
+
+
+ +
+
+
+

Metadata

+
+

The next free ID is 6

+
+
+
+

Fields

+
    +
  • + + 0: + vec<u32> + @1 + +
  • +

    Metadata

    +
    +

    The size range is:

    +
    +

    vec 1..

    +
      +
    • length: u64 1..10
    • +
    • element: u32 1..5
    • +
    +
    +
    +
  • + + 1: + hash_map<u32, string> + @2 + +
  • +

    Metadata

    +
    +

    The size range is:

    +
    +

    hash_map 1..

    +
      +
    • length: u64 1..10
    • +
    • key: u32 1..5
    • +
    • value: string 1..
    • +
    +
    +
    +
  • + + 2: + hash_set<u32> + @3 + +
  • +

    Metadata

    +
    +

    The size range is:

    +
    +

    hash_set 1..

    +
      +
    • length: u64 1..10
    • +
    • element: u32 1..5
    • +
    +
    +
    +
  • + + 3: + option<u32> + @4 + +
  • +

    Metadata

    +
    +

    The size range is:

    +
    +

    option 0..6

    +
      +
    • value: u32 1..5
    • +
    +
    +
    +
  • + + 4: + non_zero<u32> + @5 + +
  • +

    Metadata

    +
    +

    The size range is:

    +
    +

    non_zero 0..6

    +
      +
    • value: u32 1..5
    • +
    +
    +
    +
+ +
+ + diff --git a/crates/stef-doc/tests/snapshots/render__render@types_nested.stef.snap b/crates/stef-doc/tests/snapshots/render__render@types_nested.stef.snap new file mode 100644 index 0000000..c3df923 --- /dev/null +++ b/crates/stef-doc/tests/snapshots/render__render@types_nested.stef.snap @@ -0,0 +1,141 @@ +--- +source: crates/stef-doc/tests/render.rs +description: "struct Sample {\n value: vec>>>> @1,\n}" +input_file: crates/stef-parser/tests/inputs/types_nested.stef +--- +--- types_nested/index.html + + + + + + + + + + + types_nested - Stef + + + +
+
+

Schema types_nested

+
+
+

Modules

+ +
+
+
+

Structs

+ + + + + +
+ Sample + +
+
+
+

Enums

+ +
+
+
+

Aliases

+ +
+
+
+

Constants

+ +
+
+
+ + + +--- types_nested/struct.Sample.html + + + + + + + + + + + Sample - Stef + + + +
+
+

+ Struct + types_nested::Sample +

+
struct Sample {
+    value: vec<option<non_zero<hash_map<i64, box<string>>>>> @1,
+}
+
+
+ +
+
+
+

Metadata

+
+

The next free ID is 2

+
+
+
+

Fields

+
    +
  • + + value: + vec<option<non_zero<hash_map<i64, box<string>>>>> + @1 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    vec 1..

    +
      +
    • length: u64 1..10
    • +
    • element: option 0.. +
        +
      • value: non_zero 0.. +
          +
        • value: hash_map 1.. +
            +
          • length: u64 1..10
          • +
          • key: i64 1..10
          • +
          • value: box<string> 1..
          • +
          +
        • +
        +
      • +
      +
    • +
    +
    +
    +
  • +
+ +
+ + diff --git a/crates/stef-doc/tests/snapshots/render__render@types_non_zero.stef.snap b/crates/stef-doc/tests/snapshots/render__render@types_non_zero.stef.snap new file mode 100644 index 0000000..7fc31bb --- /dev/null +++ b/crates/stef-doc/tests/snapshots/render__render@types_non_zero.stef.snap @@ -0,0 +1,450 @@ +--- +source: crates/stef-doc/tests/render.rs +description: "struct Sample {\n f01: non_zero @1,\n f02: non_zero @2,\n f03: non_zero @3,\n f04: non_zero @4,\n f05: non_zero @5,\n f06: non_zero @6,\n f07: non_zero @7,\n f08: non_zero @8,\n f09: non_zero @9,\n f10: non_zero @10,\n f11: non_zero @11,\n f12: non_zero @12,\n f13: non_zero> @13,\n f14: non_zero> @14,\n f15: non_zero> @15,\n}" +input_file: crates/stef-parser/tests/inputs/types_non_zero.stef +--- +--- types_non_zero/index.html + + + + + + + + + + + types_non_zero - Stef + + + +
+
+

Schema types_non_zero

+
+
+

Modules

+ +
+
+
+

Structs

+ + + + + +
+ Sample + +
+
+
+

Enums

+ +
+
+
+

Aliases

+ +
+
+
+

Constants

+ +
+
+
+ + + +--- types_non_zero/struct.Sample.html + + + + + + + + + + + Sample - Stef + + + +
+
+

+ Struct + types_non_zero::Sample +

+
struct Sample {
+    f01: non_zero<u8> @1,
+    f02: non_zero<u16> @2,
+    f03: non_zero<u32> @3,
+    f04: non_zero<u64> @4,
+    f05: non_zero<u128> @5,
+    f06: non_zero<i8> @6,
+    f07: non_zero<i16> @7,
+    f08: non_zero<i32> @8,
+    f09: non_zero<i64> @9,
+    f10: non_zero<i128> @10,
+    f11: non_zero<string> @11,
+    f12: non_zero<bytes> @12,
+    f13: non_zero<vec<string>> @13,
+    f14: non_zero<hash_map<string, bytes>> @14,
+    f15: non_zero<hash_set<string>> @15,
+}
+
+
+ +
+
+
+

Metadata

+
+

The next free ID is 16

+
+
+
+

Fields

+
    +
  • + + f01: + non_zero<u8> + @1 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    non_zero 0..2

    +
      +
    • value: u8 1
    • +
    +
    +
    +
  • +
  • + + f02: + non_zero<u16> + @2 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    non_zero 0..4

    +
      +
    • value: u16 1..3
    • +
    +
    +
    +
  • +
  • + + f03: + non_zero<u32> + @3 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    non_zero 0..6

    +
      +
    • value: u32 1..5
    • +
    +
    +
    +
  • +
  • + + f04: + non_zero<u64> + @4 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    non_zero 0..11

    +
      +
    • value: u64 1..10
    • +
    +
    +
    +
  • +
  • + + f05: + non_zero<u128> + @5 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    non_zero 0..20

    +
      +
    • value: u128 1..19
    • +
    +
    +
    +
  • +
  • + + f06: + non_zero<i8> + @6 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    non_zero 0..2

    +
      +
    • value: i8 1
    • +
    +
    +
    +
  • +
  • + + f07: + non_zero<i16> + @7 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    non_zero 0..4

    +
      +
    • value: i16 1..3
    • +
    +
    +
    +
  • +
  • + + f08: + non_zero<i32> + @8 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    non_zero 0..6

    +
      +
    • value: i32 1..5
    • +
    +
    +
    +
  • +
  • + + f09: + non_zero<i64> + @9 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    non_zero 0..11

    +
      +
    • value: i64 1..10
    • +
    +
    +
    +
  • +
  • + + f10: + non_zero<i128> + @10 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    non_zero 0..20

    +
      +
    • value: i128 1..19
    • +
    +
    +
    +
  • +
  • + + f11: + non_zero<string> + @11 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    non_zero 0..

    +
      +
    • value: string 1..
    • +
    +
    +
    +
  • +
  • + + f12: + non_zero<bytes> + @12 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    non_zero 0..

    +
      +
    • value: bytes 1..
    • +
    +
    +
    +
  • +
  • + + f13: + non_zero<vec<string>> + @13 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    non_zero 0..

    +
      +
    • value: vec 1.. +
        +
      • length: u64 1..10
      • +
      • element: string 1..
      • +
      +
    • +
    +
    +
    +
  • +
  • + + f14: + non_zero<hash_map<string, bytes>> + @14 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    non_zero 0..

    +
      +
    • value: hash_map 1.. +
        +
      • length: u64 1..10
      • +
      • key: string 1..
      • +
      • value: bytes 1..
      • +
      +
    • +
    +
    +
    +
  • +
  • + + f15: + non_zero<hash_set<string>> + @15 + +
    + +
    +

    Metadata

    +
    +

    The size range is:

    +
    +

    non_zero 0..

    +
      +
    • value: hash_set 1.. +
        +
      • length: u64 1..10
      • +
      • element: string 1..
      • +
      +
    • +
    +
    +
    +
  • +
+ +
+ + diff --git a/crates/stef-doc/tests/snapshots/render__render@types_ref.stef.snap b/crates/stef-doc/tests/snapshots/render__render@types_ref.stef.snap new file mode 100644 index 0000000..baff5af --- /dev/null +++ b/crates/stef-doc/tests/snapshots/render__render@types_ref.stef.snap @@ -0,0 +1,254 @@ +--- +source: crates/stef-doc/tests/render.rs +description: "struct Sample {\n basic: Test123 @1,\n with_generics: KeyValue @2,\n}\n\nenum Test123 {\n Value @1,\n}\n\nstruct KeyValue {\n key: K @1,\n value: V @2,\n}" +input_file: crates/stef-parser/tests/inputs/types_ref.stef +--- +--- types_ref/index.html + + + + + + + + + + + types_ref - Stef + + + +
+
+

Schema types_ref

+
+
+

Modules

+ +
+
+
+

Structs

+ + + + + + + + + +
+ Sample + +
+ KeyValue + +
+
+
+

Enums

+ + + + + +
+ Test123 + +
+
+
+

Aliases

+ +
+
+
+

Constants

+ +
+
+
+ + + +--- types_ref/struct.Sample.html + + + + + + + + + + + Sample - Stef + + + +
+
+

+ Struct + types_ref::Sample +

+
struct Sample {
+    basic: Test123 @1,
+    with_generics: KeyValue<u32, bool> @2,
+}
+
+
+ +
+
+
+

Metadata

+
+

The next free ID is 3

+
+
+
+

Fields

+
    +
  • + + basic: + Test123 + @1 + +
    + +
    +
  • +
  • + + with_generics: + KeyValue<u32, bool> + @2 + +
    + +
    +
  • +
+ +
+ + + +--- types_ref/enum.Test123.html + + + + + + + + + + + Test123 - Stef + + + +
+
+

+ Enum + types_ref::Test123 +

+
enum Test123 {
+    Value @1,
+}
+
+
+ +
+
+
+

Variants

+
    +
  • +
    Value
    +
    + +
    +
    +
    +
  • +
+
+ +
+ + + +--- types_ref/struct.KeyValue.html + + + + + + + + + + + KeyValue - Stef + + + +
+
+

+ Struct + types_ref::KeyValue +

+
struct KeyValue<K, V> {
+    key: K @1,
+    value: V @2,
+}
+
+
+ +
+
+
+

Metadata

+
+

The next free ID is 3

+
+
+
+

Fields

+
    +
  • + + key: + K + @1 + +
    + +
    +
  • +
  • + + value: + V + @2 + +
    + +
    +
  • +
+ +
+ + diff --git a/crates/stef-go/src/definition.rs b/crates/stef-go/src/definition.rs index 35d6ad6..9ff17b6 100644 --- a/crates/stef-go/src/definition.rs +++ b/crates/stef-go/src/definition.rs @@ -76,7 +76,7 @@ fn render_definition<'a>(buf: &mut String, definition: &'a Definition<'_>) -> Op .unwrap(); } Definition::Enum(e) => writeln!(buf, "{}", RenderEnum(e)).unwrap(), - Definition::TypeAlias(a) => write!(buf, "{}", RenderAlias(a)).unwrap(), + Definition::TypeAlias(a) => writeln!(buf, "{}", RenderAlias(a)).unwrap(), Definition::Const(c) => write!(buf, "{}", RenderConst(c)).unwrap(), Definition::Import(_) => {} } @@ -405,7 +405,7 @@ struct RenderAlias<'a>(&'a TypeAlias<'a>); impl Display for RenderAlias<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( + writeln!( f, "{}type {} {}", RenderComment { diff --git a/crates/stef-go/tests/snapshots/render__render@mixed.stef.snap b/crates/stef-go/tests/snapshots/render__render@mixed.stef.snap new file mode 100644 index 0000000..762f189 --- /dev/null +++ b/crates/stef-go/tests/snapshots/render__render@mixed.stef.snap @@ -0,0 +1,1353 @@ +--- +source: crates/stef-go/tests/render.rs +description: "/// Basic user information.\n///\n/// Uses various other `structs` and `enums` to compose the information\n/// in a **type safe** way.\nstruct User {\n name: FullName @1,\n /// Physical address, might not be specified by the user.\n address: option
@2,\n age: u8 @3,\n birthday: birthday::DayOfBirth @4,\n}\n\n/// Full name of a user.\nstruct FullName {\n first: string @1,\n middle: option @2,\n last: string @3,\n}\n\n/// Simple alias for convenience.\n///\n/// - Might be easier to remember.\n/// - Often referenced as this.\ntype Name = FullName;\n\nstruct Address {\n /// Street name.\n street: string @1,\n /// Number of the house in the street.\n house_no: HouseNumber @2,\n city: string @3,\n}\n\n/// The number on the house.\n///\n/// More details can be found at [Wikipedia](https://en.wikipedia.org/wiki/House_numbering).\nenum HouseNumber {\n /// Digit only number.\n Digit(u16 @1) @1,\n /// Mixed _number_ with characters like `1a`.\n Text(string @1) @2,\n}\n\n/// Probably the max age of a human, currently.\nconst MAX_AGE: u8 = 120;\n\n\n/// Details for defining birthdays.\nmod birthday {\n /// As the name suggests, specifies details about birthdays.\n enum DayOfBirth {\n Specific {\n year: u16 @1,\n month: Month @2,\n day: u8 @3,\n } @1,\n /// The user didn't want to say.\n Secret {\n /// Optional info from the user about why they didn't want to\n /// reveal their birthday.\n reason: option @1,\n } @2,\n /// We never asked and nobody knows.\n Unknown @3,\n }\n\n /// Let's assume we only have details of people born **after** this year.\n const MIN_YEAR: u16 = 1900;\n\n /// Absolute maximum for a day, but might be even less depending\n /// on the month.\n const MAX_DAY: u8 = 31;\n\n /// The month of the year.\n enum Month {\n January @1,\n February @2,\n March @3,\n April @4,\n May @5,\n June @6,\n July @7,\n August @8,\n September @9,\n October @10,\n November @11,\n December @12,\n }\n}" +input_file: crates/stef-parser/tests/inputs/mixed.stef +--- +--- sample.go + +// Code generated by stef-go (v0.1.0). DO NOT EDIT. + +import ( + "github.com/dnaka91/stef-go" + "github.com/dnaka91/stef-go/buf" +) + +package sample + +// Basic user information. +// +// Uses various other `structs` and `enums` to compose the information +// in a **type safe** way. +type User struct { + Name FullName + // Physical address, might not be specified by the user. + Address *Address + Age uint8 + Birthday birthday.DayOfBirth +} + +func NewUser( + name FullName, + address *Address, + age uint8, + birthday birthday.DayOfBirth, +) User { + return User{ + Name: name, + Address: address, + Age: age, + Birthday: birthday, + } +} + +var _ buf.Encode = (*User)(nil) + +func (v *User) Encode(w []byte) []byte { + w = buf.EncodeField(w, 1, func (w []byte) []byte { + return v.Name.Encode(w) + }) + w = buf.EncodeFieldOption[Address](w, 2, &v.Address, func (w []byte, v Address) []byte { + return v.Encode(w) + }) + w = buf.EncodeField(w, 3, func (w []byte) []byte { + return buf.EncodeU8(w, v.Age) + }) + w = buf.EncodeField(w, 4, func (w []byte) []byte { + return v.Birthday.Encode(w) + }) + w = buf.EncodeU32(w, buf.EndMarker) + return w +} + +var _ buf.Decode = (*User)(nil) + +func (v *User) Decode(r []byte) ([]byte, error) { + foundName := false + foundAddress := false + foundAge := false + foundBirthday := false + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case 1: + r2, value, err := func(r []byte) ([]byte, FullName, error) { + var value FullName + return value.Decode(r) + }(r) + + if err != nil { + return nil, err + } + r = r2 + v.Name = value + foundName = true + case 2: + r2, value, err := buf.DecodeOption[Address](r, func(r []byte) ([]byte, Address, error) { + return func(r []byte) ([]byte, Address, error) { + var value Address + return value.Decode(r) + }(r) + + }) + if err != nil { + return nil, err + } + r = r2 + v.Address = value + foundAddress = true + case 3: + r2, value, err := buf.DecodeU8(r) + if err != nil { + return nil, err + } + r = r2 + v.Age = value + foundAge = true + case 4: + r2, value, err := func(r []byte) ([]byte, birthday.DayOfBirth, error) { + var value birthday.DayOfBirth + return value.Decode(r) + }(r) + + if err != nil { + return nil, err + } + r = r2 + v.Birthday = value + foundBirthday = true + case buf.EndMarker: + break + } + } + + if !foundName { + return nil, buf.MissingFieldError{ + ID: 1 + Field: "name" + } + } + if !foundAddress { + return nil, buf.MissingFieldError{ + ID: 2 + Field: "address" + } + } + if !foundAge { + return nil, buf.MissingFieldError{ + ID: 3 + Field: "age" + } + } + if !foundBirthday { + return nil, buf.MissingFieldError{ + ID: 4 + Field: "birthday" + } + } + + return r, nil +} + +var _ buf.Size = (*User)(nil) + +func (v *User) Size() int { + size := 0 + size += buf.SizeField(1, func() int { + return v.Name.Size() + }) + size += buf.SizeFieldOption[Address](2, &v.Address, func (v Address) []byte { + return v.Size() + }) + size += buf.SizeField(3, func() int { + return buf.SizeU8(v.Age) + }) + size += buf.SizeField(4, func() int { + return v.Birthday.Size() + }) + size += buf.SizeU32(buf.EndMarker) + return size +} + +// Full name of a user. +type FullName struct { + First string + Middle *string + Last string +} + +func NewFullName( + first string, + middle *string, + last string, +) FullName { + return FullName{ + First: first, + Middle: middle, + Last: last, + } +} + +var _ buf.Encode = (*FullName)(nil) + +func (v *FullName) Encode(w []byte) []byte { + w = buf.EncodeField(w, 1, func (w []byte) []byte { + return buf.EncodeString(w, v.First) + }) + w = buf.EncodeFieldOption[string](w, 2, &v.Middle, func (w []byte, v string) []byte { + return buf.EncodeString(w, v) + }) + w = buf.EncodeField(w, 3, func (w []byte) []byte { + return buf.EncodeString(w, v.Last) + }) + w = buf.EncodeU32(w, buf.EndMarker) + return w +} + +var _ buf.Decode = (*FullName)(nil) + +func (v *FullName) Decode(r []byte) ([]byte, error) { + foundFirst := false + foundMiddle := false + foundLast := false + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case 1: + r2, value, err := buf.DecodeString(r) + if err != nil { + return nil, err + } + r = r2 + v.First = value + foundFirst = true + case 2: + r2, value, err := buf.DecodeOption[string](r, func(r []byte) ([]byte, string, error) { + return buf.DecodeString(r) + }) + if err != nil { + return nil, err + } + r = r2 + v.Middle = value + foundMiddle = true + case 3: + r2, value, err := buf.DecodeString(r) + if err != nil { + return nil, err + } + r = r2 + v.Last = value + foundLast = true + case buf.EndMarker: + break + } + } + + if !foundFirst { + return nil, buf.MissingFieldError{ + ID: 1 + Field: "first" + } + } + if !foundMiddle { + return nil, buf.MissingFieldError{ + ID: 2 + Field: "middle" + } + } + if !foundLast { + return nil, buf.MissingFieldError{ + ID: 3 + Field: "last" + } + } + + return r, nil +} + +var _ buf.Size = (*FullName)(nil) + +func (v *FullName) Size() int { + size := 0 + size += buf.SizeField(1, func() int { + return buf.SizeString(v.First) + }) + size += buf.SizeFieldOption[string](2, &v.Middle, func (v string) []byte { + return buf.SizeString(v) + }) + size += buf.SizeField(3, func() int { + return buf.SizeString(v.Last) + }) + size += buf.SizeU32(buf.EndMarker) + return size +} + +// Simple alias for convenience. +// +// - Might be easier to remember. +// - Often referenced as this. +type Name FullName + +type Address struct { + // Street name. + Street string + // Number of the house in the street. + HouseNo HouseNumber + City string +} + +func NewAddress( + street string, + houseNo HouseNumber, + city string, +) Address { + return Address{ + Street: street, + HouseNo: houseNo, + City: city, + } +} + +var _ buf.Encode = (*Address)(nil) + +func (v *Address) Encode(w []byte) []byte { + w = buf.EncodeField(w, 1, func (w []byte) []byte { + return buf.EncodeString(w, v.Street) + }) + w = buf.EncodeField(w, 2, func (w []byte) []byte { + return v.HouseNo.Encode(w) + }) + w = buf.EncodeField(w, 3, func (w []byte) []byte { + return buf.EncodeString(w, v.City) + }) + w = buf.EncodeU32(w, buf.EndMarker) + return w +} + +var _ buf.Decode = (*Address)(nil) + +func (v *Address) Decode(r []byte) ([]byte, error) { + foundStreet := false + foundHouseNo := false + foundCity := false + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case 1: + r2, value, err := buf.DecodeString(r) + if err != nil { + return nil, err + } + r = r2 + v.Street = value + foundStreet = true + case 2: + r2, value, err := func(r []byte) ([]byte, HouseNumber, error) { + var value HouseNumber + return value.Decode(r) + }(r) + + if err != nil { + return nil, err + } + r = r2 + v.HouseNo = value + foundHouseNo = true + case 3: + r2, value, err := buf.DecodeString(r) + if err != nil { + return nil, err + } + r = r2 + v.City = value + foundCity = true + case buf.EndMarker: + break + } + } + + if !foundStreet { + return nil, buf.MissingFieldError{ + ID: 1 + Field: "street" + } + } + if !foundHouseNo { + return nil, buf.MissingFieldError{ + ID: 2 + Field: "house_no" + } + } + if !foundCity { + return nil, buf.MissingFieldError{ + ID: 3 + Field: "city" + } + } + + return r, nil +} + +var _ buf.Size = (*Address)(nil) + +func (v *Address) Size() int { + size := 0 + size += buf.SizeField(1, func() int { + return buf.SizeString(v.Street) + }) + size += buf.SizeField(2, func() int { + return v.HouseNo.Size() + }) + size += buf.SizeField(3, func() int { + return buf.SizeString(v.City) + }) + size += buf.SizeU32(buf.EndMarker) + return size +} + +type HouseNumberVariant interface { + sealed() +} + +// The number on the house. +// +// More details can be found at [Wikipedia](https://en.wikipedia.org/wiki/House_numbering). +type HouseNumber HouseNumberVariant + +// Digit only number. +type HouseNumber_Digit struct { + F0 uint16 +} + +func (v HouseNumber_Digit) sealed() {} + +func NewHouseNumber_Digit( + f0 uint16, +) HouseNumber_Digit { + return HouseNumber_Digit{ + F0: f0, + } +} + +var _ buf.Encode = (*HouseNumber_Digit)(nil) + +func (v *HouseNumber_Digit) Encode(w []byte) []byte { + w = buf.EncodeField(w, 1, func (w []byte) []byte { + return buf.EncodeU16(w, v.F0) + }) + w = buf.EncodeU32(w, buf.EndMarker) + return nil +} + +var _ buf.Decode = (*HouseNumber_Digit)(nil) + +func (v *HouseNumber_Digit) Decode(r []byte) ([]byte, error) { + foundF0 := false + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case 1: + r2, value, err := buf.DecodeU16(r) + if err != nil { + return nil, err + } + r = r2 + v.F0 = value + foundF0 = true + case buf.EndMarker: + break + } + } + + if !foundF0 { + return nil, buf.MissingFieldError{ + ID: 1 + Field: "" + } + } + + return r, nil +} + +var _ buf.Size = (*HouseNumber_Digit)(nil) + +func (v *HouseNumber_Digit) Size() int { + size := 0 + size += buf.SizeField(1, func() int { + return buf.SizeU16(v.F0) + }) + size += buf.SizeU32(buf.EndMarker) + return size +} + +// Mixed _number_ with characters like `1a`. +type HouseNumber_Text struct { + F0 string +} + +func (v HouseNumber_Text) sealed() {} + +func NewHouseNumber_Text( + f0 string, +) HouseNumber_Text { + return HouseNumber_Text{ + F0: f0, + } +} + +var _ buf.Encode = (*HouseNumber_Text)(nil) + +func (v *HouseNumber_Text) Encode(w []byte) []byte { + w = buf.EncodeField(w, 1, func (w []byte) []byte { + return buf.EncodeString(w, v.F0) + }) + w = buf.EncodeU32(w, buf.EndMarker) + return nil +} + +var _ buf.Decode = (*HouseNumber_Text)(nil) + +func (v *HouseNumber_Text) Decode(r []byte) ([]byte, error) { + foundF0 := false + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case 1: + r2, value, err := buf.DecodeString(r) + if err != nil { + return nil, err + } + r = r2 + v.F0 = value + foundF0 = true + case buf.EndMarker: + break + } + } + + if !foundF0 { + return nil, buf.MissingFieldError{ + ID: 1 + Field: "" + } + } + + return r, nil +} + +var _ buf.Size = (*HouseNumber_Text)(nil) + +func (v *HouseNumber_Text) Size() int { + size := 0 + size += buf.SizeField(1, func() int { + return buf.SizeString(v.F0) + }) + size += buf.SizeU32(buf.EndMarker) + return size +} + +// Probably the max age of a human, currently. +const MaxAge uint8 = 120 +--- sample/birthday.go + +// Code generated by stef-go (v0.1.0). DO NOT EDIT. + +// Details for defining birthdays. +package birthday + +type DayOfBirthVariant interface { + sealed() +} + +// As the name suggests, specifies details about birthdays. +type DayOfBirth DayOfBirthVariant + +type DayOfBirth_Specific struct { + Year uint16 + Month Month + Day uint8 +} + +func (v DayOfBirth_Specific) sealed() {} + +func NewDayOfBirth_Specific( + year uint16, + month Month, + day uint8, +) DayOfBirth_Specific { + return DayOfBirth_Specific{ + Year: year, + Month: month, + Day: day, + } +} + +var _ buf.Encode = (*DayOfBirth_Specific)(nil) + +func (v *DayOfBirth_Specific) Encode(w []byte) []byte { + w = buf.EncodeField(w, 1, func (w []byte) []byte { + return buf.EncodeU16(w, v.Year) + }) + w = buf.EncodeField(w, 2, func (w []byte) []byte { + return v.Month.Encode(w) + }) + w = buf.EncodeField(w, 3, func (w []byte) []byte { + return buf.EncodeU8(w, v.Day) + }) + w = buf.EncodeU32(w, buf.EndMarker) + return nil +} + +var _ buf.Decode = (*DayOfBirth_Specific)(nil) + +func (v *DayOfBirth_Specific) Decode(r []byte) ([]byte, error) { + foundYear := false + foundMonth := false + foundDay := false + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case 1: + r2, value, err := buf.DecodeU16(r) + if err != nil { + return nil, err + } + r = r2 + v.Year = value + foundYear = true + case 2: + r2, value, err := func(r []byte) ([]byte, Month, error) { + var value Month + return value.Decode(r) + }(r) + + if err != nil { + return nil, err + } + r = r2 + v.Month = value + foundMonth = true + case 3: + r2, value, err := buf.DecodeU8(r) + if err != nil { + return nil, err + } + r = r2 + v.Day = value + foundDay = true + case buf.EndMarker: + break + } + } + + if !foundYear { + return nil, buf.MissingFieldError{ + ID: 1 + Field: "year" + } + } + if !foundMonth { + return nil, buf.MissingFieldError{ + ID: 2 + Field: "month" + } + } + if !foundDay { + return nil, buf.MissingFieldError{ + ID: 3 + Field: "day" + } + } + + return r, nil +} + +var _ buf.Size = (*DayOfBirth_Specific)(nil) + +func (v *DayOfBirth_Specific) Size() int { + size := 0 + size += buf.SizeField(1, func() int { + return buf.SizeU16(v.Year) + }) + size += buf.SizeField(2, func() int { + return v.Month.Size() + }) + size += buf.SizeField(3, func() int { + return buf.SizeU8(v.Day) + }) + size += buf.SizeU32(buf.EndMarker) + return size +} + +// The user didn't want to say. +type DayOfBirth_Secret struct { + // Optional info from the user about why they didn't want to + // reveal their birthday. + Reason *string +} + +func (v DayOfBirth_Secret) sealed() {} + +func NewDayOfBirth_Secret( + reason *string, +) DayOfBirth_Secret { + return DayOfBirth_Secret{ + Reason: reason, + } +} + +var _ buf.Encode = (*DayOfBirth_Secret)(nil) + +func (v *DayOfBirth_Secret) Encode(w []byte) []byte { + w = buf.EncodeFieldOption[string](w, 1, &v.Reason, func (w []byte, v string) []byte { + return buf.EncodeString(w, v) + }) + w = buf.EncodeU32(w, buf.EndMarker) + return nil +} + +var _ buf.Decode = (*DayOfBirth_Secret)(nil) + +func (v *DayOfBirth_Secret) Decode(r []byte) ([]byte, error) { + foundReason := false + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case 1: + r2, value, err := buf.DecodeOption[string](r, func(r []byte) ([]byte, string, error) { + return buf.DecodeString(r) + }) + if err != nil { + return nil, err + } + r = r2 + v.Reason = value + foundReason = true + case buf.EndMarker: + break + } + } + + if !foundReason { + return nil, buf.MissingFieldError{ + ID: 1 + Field: "reason" + } + } + + return r, nil +} + +var _ buf.Size = (*DayOfBirth_Secret)(nil) + +func (v *DayOfBirth_Secret) Size() int { + size := 0 + size += buf.SizeFieldOption[string](1, &v.Reason, func (v string) []byte { + return buf.SizeString(v) + }) + size += buf.SizeU32(buf.EndMarker) + return size +} + +// We never asked and nobody knows. +type DayOfBirth_Unknown struct{} + +func (v DayOfBirth_Unknown) sealed() {} + +func NewDayOfBirth_Unknown() DayOfBirth_Unknown { + return DayOfBirth_Unknown{} +} + +var _ buf.Encode = (*DayOfBirth_Unknown)(nil) + +func (v *DayOfBirth_Unknown) Encode(w []byte) []byte { + return nil +} + +var _ buf.Decode = (*DayOfBirth_Unknown)(nil) + +func (v *DayOfBirth_Unknown) Decode(r []byte) ([]byte, error) { + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case buf.EndMarker: + break + } + } + + + return r, nil +} + +var _ buf.Size = (*DayOfBirth_Unknown)(nil) + +func (v *DayOfBirth_Unknown) Size() int { + size := 0 + return size +} + +// Let's assume we only have details of people born **after** this year. +const MinYear uint16 = 1900 +// Absolute maximum for a day, but might be even less depending +// on the month. +const MaxDay uint8 = 31 +type MonthVariant interface { + sealed() +} + +// The month of the year. +type Month MonthVariant + +type Month_January struct{} + +func (v Month_January) sealed() {} + +func NewMonth_January() Month_January { + return Month_January{} +} + +var _ buf.Encode = (*Month_January)(nil) + +func (v *Month_January) Encode(w []byte) []byte { + return nil +} + +var _ buf.Decode = (*Month_January)(nil) + +func (v *Month_January) Decode(r []byte) ([]byte, error) { + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case buf.EndMarker: + break + } + } + + + return r, nil +} + +var _ buf.Size = (*Month_January)(nil) + +func (v *Month_January) Size() int { + size := 0 + return size +} + +type Month_February struct{} + +func (v Month_February) sealed() {} + +func NewMonth_February() Month_February { + return Month_February{} +} + +var _ buf.Encode = (*Month_February)(nil) + +func (v *Month_February) Encode(w []byte) []byte { + return nil +} + +var _ buf.Decode = (*Month_February)(nil) + +func (v *Month_February) Decode(r []byte) ([]byte, error) { + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case buf.EndMarker: + break + } + } + + + return r, nil +} + +var _ buf.Size = (*Month_February)(nil) + +func (v *Month_February) Size() int { + size := 0 + return size +} + +type Month_March struct{} + +func (v Month_March) sealed() {} + +func NewMonth_March() Month_March { + return Month_March{} +} + +var _ buf.Encode = (*Month_March)(nil) + +func (v *Month_March) Encode(w []byte) []byte { + return nil +} + +var _ buf.Decode = (*Month_March)(nil) + +func (v *Month_March) Decode(r []byte) ([]byte, error) { + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case buf.EndMarker: + break + } + } + + + return r, nil +} + +var _ buf.Size = (*Month_March)(nil) + +func (v *Month_March) Size() int { + size := 0 + return size +} + +type Month_April struct{} + +func (v Month_April) sealed() {} + +func NewMonth_April() Month_April { + return Month_April{} +} + +var _ buf.Encode = (*Month_April)(nil) + +func (v *Month_April) Encode(w []byte) []byte { + return nil +} + +var _ buf.Decode = (*Month_April)(nil) + +func (v *Month_April) Decode(r []byte) ([]byte, error) { + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case buf.EndMarker: + break + } + } + + + return r, nil +} + +var _ buf.Size = (*Month_April)(nil) + +func (v *Month_April) Size() int { + size := 0 + return size +} + +type Month_May struct{} + +func (v Month_May) sealed() {} + +func NewMonth_May() Month_May { + return Month_May{} +} + +var _ buf.Encode = (*Month_May)(nil) + +func (v *Month_May) Encode(w []byte) []byte { + return nil +} + +var _ buf.Decode = (*Month_May)(nil) + +func (v *Month_May) Decode(r []byte) ([]byte, error) { + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case buf.EndMarker: + break + } + } + + + return r, nil +} + +var _ buf.Size = (*Month_May)(nil) + +func (v *Month_May) Size() int { + size := 0 + return size +} + +type Month_June struct{} + +func (v Month_June) sealed() {} + +func NewMonth_June() Month_June { + return Month_June{} +} + +var _ buf.Encode = (*Month_June)(nil) + +func (v *Month_June) Encode(w []byte) []byte { + return nil +} + +var _ buf.Decode = (*Month_June)(nil) + +func (v *Month_June) Decode(r []byte) ([]byte, error) { + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case buf.EndMarker: + break + } + } + + + return r, nil +} + +var _ buf.Size = (*Month_June)(nil) + +func (v *Month_June) Size() int { + size := 0 + return size +} + +type Month_July struct{} + +func (v Month_July) sealed() {} + +func NewMonth_July() Month_July { + return Month_July{} +} + +var _ buf.Encode = (*Month_July)(nil) + +func (v *Month_July) Encode(w []byte) []byte { + return nil +} + +var _ buf.Decode = (*Month_July)(nil) + +func (v *Month_July) Decode(r []byte) ([]byte, error) { + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case buf.EndMarker: + break + } + } + + + return r, nil +} + +var _ buf.Size = (*Month_July)(nil) + +func (v *Month_July) Size() int { + size := 0 + return size +} + +type Month_August struct{} + +func (v Month_August) sealed() {} + +func NewMonth_August() Month_August { + return Month_August{} +} + +var _ buf.Encode = (*Month_August)(nil) + +func (v *Month_August) Encode(w []byte) []byte { + return nil +} + +var _ buf.Decode = (*Month_August)(nil) + +func (v *Month_August) Decode(r []byte) ([]byte, error) { + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case buf.EndMarker: + break + } + } + + + return r, nil +} + +var _ buf.Size = (*Month_August)(nil) + +func (v *Month_August) Size() int { + size := 0 + return size +} + +type Month_September struct{} + +func (v Month_September) sealed() {} + +func NewMonth_September() Month_September { + return Month_September{} +} + +var _ buf.Encode = (*Month_September)(nil) + +func (v *Month_September) Encode(w []byte) []byte { + return nil +} + +var _ buf.Decode = (*Month_September)(nil) + +func (v *Month_September) Decode(r []byte) ([]byte, error) { + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case buf.EndMarker: + break + } + } + + + return r, nil +} + +var _ buf.Size = (*Month_September)(nil) + +func (v *Month_September) Size() int { + size := 0 + return size +} + +type Month_October struct{} + +func (v Month_October) sealed() {} + +func NewMonth_October() Month_October { + return Month_October{} +} + +var _ buf.Encode = (*Month_October)(nil) + +func (v *Month_October) Encode(w []byte) []byte { + return nil +} + +var _ buf.Decode = (*Month_October)(nil) + +func (v *Month_October) Decode(r []byte) ([]byte, error) { + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case buf.EndMarker: + break + } + } + + + return r, nil +} + +var _ buf.Size = (*Month_October)(nil) + +func (v *Month_October) Size() int { + size := 0 + return size +} + +type Month_November struct{} + +func (v Month_November) sealed() {} + +func NewMonth_November() Month_November { + return Month_November{} +} + +var _ buf.Encode = (*Month_November)(nil) + +func (v *Month_November) Encode(w []byte) []byte { + return nil +} + +var _ buf.Decode = (*Month_November)(nil) + +func (v *Month_November) Decode(r []byte) ([]byte, error) { + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case buf.EndMarker: + break + } + } + + + return r, nil +} + +var _ buf.Size = (*Month_November)(nil) + +func (v *Month_November) Size() int { + size := 0 + return size +} + +type Month_December struct{} + +func (v Month_December) sealed() {} + +func NewMonth_December() Month_December { + return Month_December{} +} + +var _ buf.Encode = (*Month_December)(nil) + +func (v *Month_December) Encode(w []byte) []byte { + return nil +} + +var _ buf.Decode = (*Month_December)(nil) + +func (v *Month_December) Decode(r []byte) ([]byte, error) { + + for len(r) > 0 { + r2, id, err := buf.DecodeID(r) + if err != nil { + return nil, err + } + r = r2 + + switch id { + case buf.EndMarker: + break + } + } + + + return r, nil +} + +var _ buf.Size = (*Month_December)(nil) + +func (v *Month_December) Size() int { + size := 0 + return size +} + + diff --git a/crates/stef-lsp/Cargo.toml b/crates/stef-lsp/Cargo.toml index bf28a37..82bae1b 100644 --- a/crates/stef-lsp/Cargo.toml +++ b/crates/stef-lsp/Cargo.toml @@ -24,6 +24,7 @@ ropey = "1.6.1" serde = { version = "1.0.193", features = ["derive"] } serde_json = "1.0.108" stef-compiler = { path = "../stef-compiler" } +stef-meta = { path = "../stef-meta" } stef-parser = { path = "../stef-parser" } time = { version = "0.3.30", features = ["formatting", "local-offset", "macros"] } diff --git a/crates/stef-lsp/src/handlers/hover.rs b/crates/stef-lsp/src/handlers/hover.rs index 2195fc4..a1c9e81 100644 --- a/crates/stef-lsp/src/handlers/hover.rs +++ b/crates/stef-lsp/src/handlers/hover.rs @@ -1,11 +1,11 @@ -use std::{borrow::Cow, fmt::Write, ops::Range}; +use std::{fmt::Write, ops::Range}; use anyhow::{Context, Result}; use line_index::{LineIndex, TextSize, WideLineCol}; use lsp_types::{Position, Range as LspRange}; use stef_parser::{ - Comment, Const, DataType, Definition, Enum, Fields, Module, NamedField, Schema, Span, Spanned, - Struct, Type, TypeAlias, Variant, + Comment, Const, Definition, Enum, Fields, Module, NamedField, Schema, Span, Spanned, Struct, + TypeAlias, Variant, }; pub fn visit_schema( @@ -61,7 +61,7 @@ fn visit_struct(item: &Struct<'_>, position: usize) -> Option<(String, Span)> { .then(|| { let mut text = fold_comment(&item.comment); - if let Some(next_id) = next_field_id(&item.fields) { + if let Some(next_id) = stef_meta::next_field_id(&item.fields) { let _ = writeln!(&mut text, "- next ID: `{next_id}`"); } @@ -78,7 +78,7 @@ fn visit_enum(item: &Enum<'_>, position: usize) -> Option<(String, Span)> { let _ = writeln!( &mut text, "- next ID: `{}`", - next_variant_id(&item.variants) + stef_meta::next_variant_id(&item.variants) ); (text, item.name.span()) @@ -95,7 +95,7 @@ fn visit_variant(item: &Variant<'_>, position: usize) -> Option<(String, Span)> .then(|| { let mut text = fold_comment(&item.comment); - if let Some(next_id) = next_field_id(&item.fields) { + if let Some(next_id) = stef_meta::next_field_id(&item.fields) { let _ = writeln!(&mut text, "- next ID: `{next_id}`"); } @@ -119,7 +119,7 @@ fn visit_named_field(item: &NamedField<'_>, position: usize) -> Option<(String, let mut text = fold_comment(&item.comment); let _ = write!(&mut text, "### Wire size\n\n"); - if let Some(size) = wire_size(&item.ty.value) { + if let Some(size) = stef_meta::wire_size(&item.ty.value) { size.print(&mut text, 0); } else { let _ = write!(&mut text, "_unknown_"); @@ -147,204 +147,6 @@ fn fold_comment(comment: &Comment<'_>) -> String { }) } -fn next_variant_id(variants: &[Variant<'_>]) -> u32 { - variants - .iter() - .map(|variant| variant.id.get()) - .max() - .unwrap_or(0) - + 1 -} - -fn next_field_id(fields: &Fields<'_>) -> Option { - match fields { - Fields::Named(named) => { - Some(named.iter().map(|field| field.id.get()).max().unwrap_or(0) + 1) - } - Fields::Unnamed(unnamed) => Some( - unnamed - .iter() - .map(|field| field.id.get()) - .max() - .unwrap_or(0) - + 1, - ), - Fields::Unit => None, - } -} - -struct WireSize { - label: Cow<'static, str>, - min: usize, - max: Option, - inner: Vec<(Cow<'static, str>, Option)>, -} - -impl WireSize { - fn fixed(label: impl Into>, size: usize) -> Self { - Self { - label: label.into(), - min: size, - max: Some(size), - inner: Vec::new(), - } - } - - fn range(label: impl Into>, min: usize, max: usize) -> Self { - Self { - label: label.into(), - min, - max: Some(max), - inner: Vec::new(), - } - } - - fn min(label: impl Into>, min: usize) -> Self { - Self { - label: label.into(), - min, - max: None, - inner: Vec::new(), - } - } - - fn print(&self, buf: &mut String, indent: usize) { - let _ = write!(buf, "**{}** ", self.label); - - let _ = match self.max { - Some(max) if self.min == max => write!(buf, "`{max}`"), - Some(max) => write!(buf, "`{}..{max}`", self.min), - None => write!(buf, "`{}..`", self.min), - }; - - for (label, size) in &self.inner { - let _ = write!(buf, "\n{:indent$}- {label}: ", "", indent = indent + 2); - if let Some(size) = size { - size.print(buf, indent + 2); - } else { - let _ = write!(buf, "_unknown_"); - } - } - } -} - -fn wire_size(ty: &DataType<'_>) -> Option { - Some(match ty { - DataType::Bool => WireSize::fixed("bool", 1), - DataType::U8 => WireSize::fixed("u8", 1), - DataType::I8 => WireSize::fixed("i8", 1), - DataType::U16 => WireSize::range("u16", 1, 3), - DataType::I16 => WireSize::range("i16", 1, 3), - DataType::U32 => WireSize::range("u32", 1, 5), - DataType::I32 => WireSize::range("i32", 1, 5), - DataType::U64 => WireSize::range("u64", 1, 10), - DataType::I64 => WireSize::range("i64", 1, 10), - DataType::U128 => WireSize::range("u128", 1, 19), - DataType::I128 => WireSize::range("i128", 1, 19), - DataType::F32 => WireSize::fixed("f32", 4), - DataType::F64 => WireSize::fixed("f64", 8), - DataType::String => WireSize::min("string", 1), - DataType::StringRef => WireSize::min("&string", 1), - DataType::Bytes => WireSize::min("bytes", 1), - DataType::BytesRef => WireSize::min("&bytes", 1), - DataType::Vec(ty) => WireSize { - label: "vec".into(), - min: 1, - max: None, - inner: vec![ - ("length".into(), wire_size(&DataType::U64)), - ("element".into(), wire_size(&ty.value)), - ], - }, - DataType::HashMap(kv) => WireSize { - label: "hash_map".into(), - min: 1, - max: None, - inner: vec![ - ("length".into(), wire_size(&DataType::U64)), - ("key".into(), wire_size(&kv.0.value)), - ("value".into(), wire_size(&kv.1.value)), - ], - }, - DataType::HashSet(ty) => WireSize { - label: "hash_set".into(), - min: 1, - max: None, - inner: vec![ - ("length".into(), wire_size(&DataType::U64)), - ("element".into(), wire_size(&ty.value)), - ], - }, - DataType::Option(ty) => { - let inner = wire_size(&ty.value); - WireSize { - label: "option".into(), - min: 0, - max: inner.as_ref().and_then(|size| size.max).map(|max| 1 + max), - inner: vec![("value".into(), inner)], - } - } - DataType::NonZero(ty) => { - let inner = wire_size(&ty.value); - WireSize { - label: "non_zero".into(), - min: 0, - max: inner.as_ref().and_then(|size| size.max).map(|max| 1 + max), - inner: vec![("value".into(), inner)], - } - } - DataType::BoxString => WireSize::min("box", 1), - DataType::BoxBytes => WireSize::min("box", 1), - DataType::Array(ty, size) => wire_size_array(ty, *size), - DataType::Tuple(types) => wire_size_tuple(types), - DataType::External(_) => return None, - }) -} - -fn wire_size_array(ty: &Type<'_>, size: u32) -> WireSize { - let length = varint_size(size); - let inner = wire_size(&ty.value); - - WireSize { - label: "array".into(), - min: length + inner.as_ref().map_or(0, |size| size.min) * size as usize, - max: inner - .as_ref() - .and_then(|size| size.max) - .map(|max| length + max * size as usize), - inner: vec![ - ("length".into(), Some(WireSize::fixed("u64", length))), - ("element".into(), inner), - ], - } -} - -fn wire_size_tuple(types: &[Type<'_>]) -> WireSize { - let inner = types - .iter() - .enumerate() - .map(|(i, ty)| (i.to_string().into(), wire_size(&ty.value))) - .collect::>(); - - WireSize { - label: "tuple".into(), - min: inner - .iter() - .filter_map(|(_, size)| size.as_ref()) - .map(|size| size.min) - .sum(), - max: inner - .iter() - .map(|(_, size)| size.as_ref().and_then(|s| s.max)) - .sum(), - inner, - } -} - -fn varint_size(value: u32) -> usize { - ((std::mem::size_of::() * 8 - value.leading_zeros() as usize + 6) / 7).max(1) -} - #[allow(clippy::cast_possible_truncation)] fn get_range(index: &LineIndex, span: Span) -> Result { let range = Range::from(span); diff --git a/crates/stef-meta/Cargo.toml b/crates/stef-meta/Cargo.toml new file mode 100644 index 0000000..8fc53fb --- /dev/null +++ b/crates/stef-meta/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "stef-meta" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +readme.workspace = true +homepage.workspace = true +repository.workspace = true +license.workspace = true + +[dependencies] +stef-parser = { path = "../stef-parser" } + +[lints] +workspace = true diff --git a/crates/stef-meta/src/lib.rs b/crates/stef-meta/src/lib.rs new file mode 100644 index 0000000..d6696d6 --- /dev/null +++ b/crates/stef-meta/src/lib.rs @@ -0,0 +1,217 @@ +//! Collection of utilities that can retrieve metadata information about a schema. +//! +//! Several tools like the LSP server and docs generator can use this common logic to provide +//! additional information about the schema shown. + +use std::{borrow::Cow, fmt::Write}; + +use stef_parser::{DataType, Fields, Type, Variant}; + +/// Get the next free ID for an enum variant. +#[must_use] +pub fn next_variant_id(variants: &[Variant<'_>]) -> u32 { + variants + .iter() + .map(|variant| variant.id.get()) + .max() + .unwrap_or(0) + + 1 +} + +/// Get the next free ID for a struct or enum variant field. +#[must_use] +pub fn next_field_id(fields: &Fields<'_>) -> Option { + match fields { + Fields::Named(named) => { + Some(named.iter().map(|field| field.id.get()).max().unwrap_or(0) + 1) + } + Fields::Unnamed(unnamed) => Some( + unnamed + .iter() + .map(|field| field.id.get()) + .max() + .unwrap_or(0) + + 1, + ), + Fields::Unit => None, + } +} + +/// Information about the wire (encoded) size of a data type. +pub struct WireSize { + label: Cow<'static, str>, + min: usize, + max: Option, + inner: Vec<(Cow<'static, str>, Option)>, +} + +impl WireSize { + fn fixed(label: impl Into>, size: usize) -> Self { + Self { + label: label.into(), + min: size, + max: Some(size), + inner: Vec::new(), + } + } + + fn range(label: impl Into>, min: usize, max: usize) -> Self { + Self { + label: label.into(), + min, + max: Some(max), + inner: Vec::new(), + } + } + + fn min(label: impl Into>, min: usize) -> Self { + Self { + label: label.into(), + min, + max: None, + inner: Vec::new(), + } + } + + /// Write the information in a descriptive tree structure made out of Markdown lists. + pub fn print(&self, buf: &mut String, indent: usize) { + let _ = write!(buf, "**{}** ", self.label); + + let _ = match self.max { + Some(max) if self.min == max => write!(buf, "`{max}`"), + Some(max) => write!(buf, "`{}..{max}`", self.min), + None => write!(buf, "`{}..`", self.min), + }; + + for (label, size) in &self.inner { + let _ = write!(buf, "\n{:indent$}- {label}: ", "", indent = indent + 2); + if let Some(size) = size { + size.print(buf, indent + 2); + } else { + let _ = write!(buf, "_unknown_"); + } + } + } +} + +/// Calculate the expected encoded byte size for a type. +/// +/// The resulting size can have various forms of precision, depending on the data type. For example, +/// it could be of fixed size, inside known bounds or even unknown. +#[must_use] +pub fn wire_size(ty: &DataType<'_>) -> Option { + Some(match ty { + DataType::Bool => WireSize::fixed("bool", 1), + DataType::U8 => WireSize::fixed("u8", 1), + DataType::I8 => WireSize::fixed("i8", 1), + DataType::U16 => WireSize::range("u16", 1, 3), + DataType::I16 => WireSize::range("i16", 1, 3), + DataType::U32 => WireSize::range("u32", 1, 5), + DataType::I32 => WireSize::range("i32", 1, 5), + DataType::U64 => WireSize::range("u64", 1, 10), + DataType::I64 => WireSize::range("i64", 1, 10), + DataType::U128 => WireSize::range("u128", 1, 19), + DataType::I128 => WireSize::range("i128", 1, 19), + DataType::F32 => WireSize::fixed("f32", 4), + DataType::F64 => WireSize::fixed("f64", 8), + DataType::String => WireSize::min("string", 1), + DataType::StringRef => WireSize::min("&string", 1), + DataType::Bytes => WireSize::min("bytes", 1), + DataType::BytesRef => WireSize::min("&bytes", 1), + DataType::Vec(ty) => WireSize { + label: "vec".into(), + min: 1, + max: None, + inner: vec![ + ("length".into(), wire_size(&DataType::U64)), + ("element".into(), wire_size(&ty.value)), + ], + }, + DataType::HashMap(kv) => WireSize { + label: "hash_map".into(), + min: 1, + max: None, + inner: vec![ + ("length".into(), wire_size(&DataType::U64)), + ("key".into(), wire_size(&kv.0.value)), + ("value".into(), wire_size(&kv.1.value)), + ], + }, + DataType::HashSet(ty) => WireSize { + label: "hash_set".into(), + min: 1, + max: None, + inner: vec![ + ("length".into(), wire_size(&DataType::U64)), + ("element".into(), wire_size(&ty.value)), + ], + }, + DataType::Option(ty) => { + let inner = wire_size(&ty.value); + WireSize { + label: "option".into(), + min: 0, + max: inner.as_ref().and_then(|size| size.max).map(|max| 1 + max), + inner: vec![("value".into(), inner)], + } + } + DataType::NonZero(ty) => { + let inner = wire_size(&ty.value); + WireSize { + label: "non_zero".into(), + min: 0, + max: inner.as_ref().and_then(|size| size.max).map(|max| 1 + max), + inner: vec![("value".into(), inner)], + } + } + DataType::BoxString => WireSize::min("box", 1), + DataType::BoxBytes => WireSize::min("box", 1), + DataType::Array(ty, size) => wire_size_array(ty, *size), + DataType::Tuple(types) => wire_size_tuple(types), + DataType::External(_) => return None, + }) +} + +fn wire_size_array(ty: &Type<'_>, size: u32) -> WireSize { + let length = varint_size(size); + let inner = wire_size(&ty.value); + + WireSize { + label: "array".into(), + min: length + inner.as_ref().map_or(0, |size| size.min) * size as usize, + max: inner + .as_ref() + .and_then(|size| size.max) + .map(|max| length + max * size as usize), + inner: vec![ + ("length".into(), Some(WireSize::fixed("u64", length))), + ("element".into(), inner), + ], + } +} + +fn wire_size_tuple(types: &[Type<'_>]) -> WireSize { + let inner = types + .iter() + .enumerate() + .map(|(i, ty)| (i.to_string().into(), wire_size(&ty.value))) + .collect::>(); + + WireSize { + label: "tuple".into(), + min: inner + .iter() + .filter_map(|(_, size)| size.as_ref()) + .map(|size| size.min) + .sum(), + max: inner + .iter() + .map(|(_, size)| size.as_ref().and_then(|s| s.max)) + .sum(), + inner, + } +} + +fn varint_size(value: u32) -> usize { + ((std::mem::size_of::() * 8 - value.leading_zeros() as usize + 6) / 7).max(1) +} diff --git a/crates/stef-parser/src/lib.rs b/crates/stef-parser/src/lib.rs index 775f539..c2d4b9c 100644 --- a/crates/stef-parser/src/lib.rs +++ b/crates/stef-parser/src/lib.rs @@ -461,6 +461,12 @@ impl Print for TypeAlias<'_> { } } +impl Display for TypeAlias<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.print(f, 0) + } +} + /// Possible kinds in which a the fields of a struct or enum variant can be represented. #[derive(Debug, Eq, PartialEq)] pub enum Fields<'a> { @@ -1031,6 +1037,12 @@ impl Print for Const<'_> { } } +impl Display for Const<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.print(f, 0) + } +} + /// In-schema definition of a literal value, together with a span into the schema to mark where it /// is defined. #[derive(Clone, Debug, PartialEq)] @@ -1092,6 +1104,7 @@ impl Display for LiteralValue { /// Import declaration for an external schema. #[derive(Debug, PartialEq)] pub struct Import<'a> { + pub full: Name<'a>, /// Individual elements that form the import path. pub segments: Vec>, /// Optional final element that allows to fully import the type, making it look as it would be @@ -1101,7 +1114,9 @@ pub struct Import<'a> { impl Print for Import<'_> { fn print(&self, f: &mut fmt::Formatter<'_>, level: usize) -> fmt::Result { - let Self { segments, element } = self; + let Self { + segments, element, .. + } = self; Self::indent(f, level)?; f.write_str("use ")?; diff --git a/crates/stef-parser/src/parser/imports.rs b/crates/stef-parser/src/parser/imports.rs index 13f1f5b..5e5abe8 100644 --- a/crates/stef-parser/src/parser/imports.rs +++ b/crates/stef-parser/src/parser/imports.rs @@ -77,12 +77,18 @@ pub(super) fn parse<'i>(input: &mut Input<'i>) -> Result, ParseError> enums::parse_name.map_err(Cause::from), )), )), - ), + ) + .with_recognized() + .with_span(), ';', )), ) .parse_next(input) - .map(|(segments, element)| Import { segments, element }) + .map(|(((segments, element), full), range)| Import { + full: (full, range).into(), + segments, + element, + }) .map_err(|e| { e.map(|cause| ParseError { at: location::from_until(*input, start, [';']), diff --git a/crates/stef-parser/tests/inputs/mixed.stef b/crates/stef-parser/tests/inputs/mixed.stef new file mode 100644 index 0000000..ab20436 --- /dev/null +++ b/crates/stef-parser/tests/inputs/mixed.stef @@ -0,0 +1,89 @@ +/// Basic user information. +/// +/// Uses various other `structs` and `enums` to compose the information +/// in a **type safe** way. +struct User { + name: FullName @1, + /// Physical address, might not be specified by the user. + address: option
@2, + age: u8 @3, + birthday: birthday::DayOfBirth @4, +} + +/// Full name of a user. +struct FullName { + first: string @1, + middle: option @2, + last: string @3, +} + +/// Simple alias for convenience. +/// +/// - Might be easier to remember. +/// - Often referenced as this. +type Name = FullName; + +struct Address { + /// Street name. + street: string @1, + /// Number of the house in the street. + house_no: HouseNumber @2, + city: string @3, +} + +/// The number on the house. +/// +/// More details can be found at [Wikipedia](https://en.wikipedia.org/wiki/House_numbering). +enum HouseNumber { + /// Digit only number. + Digit(u16 @1) @1, + /// Mixed _number_ with characters like `1a`. + Text(string @1) @2, +} + +/// Probably the max age of a human, currently. +const MAX_AGE: u8 = 120; + + +/// Details for defining birthdays. +mod birthday { + /// As the name suggests, specifies details about birthdays. + enum DayOfBirth { + Specific { + year: u16 @1, + month: Month @2, + day: u8 @3, + } @1, + /// The user didn't want to say. + Secret { + /// Optional info from the user about why they didn't want to + /// reveal their birthday. + reason: option @1, + } @2, + /// We never asked and nobody knows. + Unknown @3, + } + + /// Let's assume we only have details of people born **after** this year. + const MIN_YEAR: u16 = 1900; + + /// Absolute maximum for a day, but might be even less depending + /// on the month. + const MAX_DAY: u8 = 31; + + /// The month of the year. + enum Month { + January @1, + February @2, + March @3, + April @4, + May @5, + June @6, + July @7, + August @8, + September @9, + October @10, + November @11, + December @12, + } +} diff --git a/crates/stef-parser/tests/snapshots/parser__parse@import_basic.stef.snap b/crates/stef-parser/tests/snapshots/parser__parse@import_basic.stef.snap index c52e146..989e992 100644 --- a/crates/stef-parser/tests/snapshots/parser__parse@import_basic.stef.snap +++ b/crates/stef-parser/tests/snapshots/parser__parse@import_basic.stef.snap @@ -11,6 +11,13 @@ Schema { definitions: [ Import( Import { + full: Name { + value: "other::schema::Sample", + span: Span { + start: 4, + end: 25, + }, + }, segments: [ Name { value: "other", @@ -40,6 +47,13 @@ Schema { ), Import( Import { + full: Name { + value: "second::submodule", + span: Span { + start: 31, + end: 48, + }, + }, segments: [ Name { value: "second", diff --git a/crates/stef-parser/tests/snapshots/parser__parse@mixed.stef.snap b/crates/stef-parser/tests/snapshots/parser__parse@mixed.stef.snap new file mode 100644 index 0000000..6457a1b --- /dev/null +++ b/crates/stef-parser/tests/snapshots/parser__parse@mixed.stef.snap @@ -0,0 +1,1479 @@ +--- +source: crates/stef-parser/tests/parser.rs +description: "/// Basic user information.\n///\n/// Uses various other `structs` and `enums` to compose the information\n/// in a **type safe** way.\nstruct User {\n name: FullName @1,\n /// Physical address, might not be specified by the user.\n address: option
@2,\n age: u8 @3,\n birthday: birthday::DayOfBirth @4,\n}\n\n/// Full name of a user.\nstruct FullName {\n first: string @1,\n middle: option @2,\n last: string @3,\n}\n\n/// Simple alias for convenience.\n///\n/// - Might be easier to remember.\n/// - Often referenced as this.\ntype Name = FullName;\n\nstruct Address {\n /// Street name.\n street: string @1,\n /// Number of the house in the street.\n house_no: HouseNumber @2,\n city: string @3,\n}\n\n/// The number on the house.\n///\n/// More details can be found at [Wikipedia](https://en.wikipedia.org/wiki/House_numbering).\nenum HouseNumber {\n /// Digit only number.\n Digit(u16 @1) @1,\n /// Mixed _number_ with characters like `1a`.\n Text(string @1) @2,\n}\n\n/// Probably the max age of a human, currently.\nconst MAX_AGE: u8 = 120;\n\n\n/// Details for defining birthdays.\nmod birthday {\n /// As the name suggests, specifies details about birthdays.\n enum DayOfBirth {\n Specific {\n year: u16 @1,\n month: Month @2,\n day: u8 @3,\n } @1,\n /// The user didn't want to say.\n Secret {\n /// Optional info from the user about why they didn't want to\n /// reveal their birthday.\n reason: option @1,\n } @2,\n /// We never asked and nobody knows.\n Unknown @3,\n }\n\n /// Let's assume we only have details of people born **after** this year.\n const MIN_YEAR: u16 = 1900;\n\n /// Absolute maximum for a day, but might be even less depending\n /// on the month.\n const MAX_DAY: u8 = 31;\n\n /// The month of the year.\n enum Month {\n January @1,\n February @2,\n March @3,\n April @4,\n May @5,\n June @6,\n July @7,\n August @8,\n September @9,\n October @10,\n November @11,\n December @12,\n }\n}" +input_file: crates/stef-parser/tests/inputs/mixed.stef +--- +Schema { + path: Some( + "mixed.stef", + ), + source: "/// Basic user information.\n///\n/// Uses various other `structs` and `enums` to compose the information\n/// in a **type safe** way.\nstruct User {\n name: FullName @1,\n /// Physical address, might not be specified by the user.\n address: option
@2,\n age: u8 @3,\n birthday: birthday::DayOfBirth @4,\n}\n\n/// Full name of a user.\nstruct FullName {\n first: string @1,\n middle: option @2,\n last: string @3,\n}\n\n/// Simple alias for convenience.\n///\n/// - Might be easier to remember.\n/// - Often referenced as this.\ntype Name = FullName;\n\nstruct Address {\n /// Street name.\n street: string @1,\n /// Number of the house in the street.\n house_no: HouseNumber @2,\n city: string @3,\n}\n\n/// The number on the house.\n///\n/// More details can be found at [Wikipedia](https://en.wikipedia.org/wiki/House_numbering).\nenum HouseNumber {\n /// Digit only number.\n Digit(u16 @1) @1,\n /// Mixed _number_ with characters like `1a`.\n Text(string @1) @2,\n}\n\n/// Probably the max age of a human, currently.\nconst MAX_AGE: u8 = 120;\n\n\n/// Details for defining birthdays.\nmod birthday {\n /// As the name suggests, specifies details about birthdays.\n enum DayOfBirth {\n Specific {\n year: u16 @1,\n month: Month @2,\n day: u8 @3,\n } @1,\n /// The user didn't want to say.\n Secret {\n /// Optional info from the user about why they didn't want to\n /// reveal their birthday.\n reason: option @1,\n } @2,\n /// We never asked and nobody knows.\n Unknown @3,\n }\n\n /// Let's assume we only have details of people born **after** this year.\n const MIN_YEAR: u16 = 1900;\n\n /// Absolute maximum for a day, but might be even less depending\n /// on the month.\n const MAX_DAY: u8 = 31;\n\n /// The month of the year.\n enum Month {\n January @1,\n February @2,\n March @3,\n April @4,\n May @5,\n June @6,\n July @7,\n August @8,\n September @9,\n October @10,\n November @11,\n December @12,\n }\n}\n", + definitions: [ + Struct( + Struct { + comment: Comment( + [ + CommentLine { + value: "Basic user information.", + span: Span { + start: 0, + end: 27, + }, + }, + CommentLine { + value: "", + span: Span { + start: 28, + end: 31, + }, + }, + CommentLine { + value: "Uses various other `structs` and `enums` to compose the information", + span: Span { + start: 32, + end: 103, + }, + }, + CommentLine { + value: "in a **type safe** way.", + span: Span { + start: 104, + end: 131, + }, + }, + ], + ), + attributes: Attributes( + [], + ), + name: Name { + value: "User", + span: Span { + start: 139, + end: 143, + }, + }, + generics: Generics( + [], + ), + fields: Named( + [ + NamedField { + comment: Comment( + [], + ), + name: Name { + value: "name", + span: Span { + start: 150, + end: 154, + }, + }, + ty: Type { + value: External( + ExternalType { + path: [], + name: Name { + value: "FullName", + span: Span { + start: 156, + end: 164, + }, + }, + generics: [], + }, + ), + span: Span { + start: 156, + end: 164, + }, + }, + id: Id { + value: 1, + span: Span { + start: 165, + end: 167, + }, + }, + span: Span { + start: 150, + end: 167, + }, + }, + NamedField { + comment: Comment( + [ + CommentLine { + value: "Physical address, might not be specified by the user.", + span: Span { + start: 173, + end: 230, + }, + }, + ], + ), + name: Name { + value: "address", + span: Span { + start: 235, + end: 242, + }, + }, + ty: Type { + value: Option( + Type { + value: External( + ExternalType { + path: [], + name: Name { + value: "Address", + span: Span { + start: 251, + end: 258, + }, + }, + generics: [], + }, + ), + span: Span { + start: 251, + end: 258, + }, + }, + ), + span: Span { + start: 244, + end: 259, + }, + }, + id: Id { + value: 2, + span: Span { + start: 260, + end: 262, + }, + }, + span: Span { + start: 231, + end: 262, + }, + }, + NamedField { + comment: Comment( + [], + ), + name: Name { + value: "age", + span: Span { + start: 268, + end: 271, + }, + }, + ty: Type { + value: U8, + span: Span { + start: 273, + end: 275, + }, + }, + id: Id { + value: 3, + span: Span { + start: 276, + end: 278, + }, + }, + span: Span { + start: 268, + end: 278, + }, + }, + NamedField { + comment: Comment( + [], + ), + name: Name { + value: "birthday", + span: Span { + start: 284, + end: 292, + }, + }, + ty: Type { + value: External( + ExternalType { + path: [ + Name { + value: "birthday", + span: Span { + start: 294, + end: 302, + }, + }, + ], + name: Name { + value: "DayOfBirth", + span: Span { + start: 304, + end: 314, + }, + }, + generics: [], + }, + ), + span: Span { + start: 294, + end: 314, + }, + }, + id: Id { + value: 4, + span: Span { + start: 315, + end: 317, + }, + }, + span: Span { + start: 284, + end: 317, + }, + }, + ], + ), + }, + ), + Struct( + Struct { + comment: Comment( + [ + CommentLine { + value: "Full name of a user.", + span: Span { + start: 322, + end: 346, + }, + }, + ], + ), + attributes: Attributes( + [], + ), + name: Name { + value: "FullName", + span: Span { + start: 354, + end: 362, + }, + }, + generics: Generics( + [], + ), + fields: Named( + [ + NamedField { + comment: Comment( + [], + ), + name: Name { + value: "first", + span: Span { + start: 369, + end: 374, + }, + }, + ty: Type { + value: String, + span: Span { + start: 376, + end: 382, + }, + }, + id: Id { + value: 1, + span: Span { + start: 383, + end: 385, + }, + }, + span: Span { + start: 369, + end: 385, + }, + }, + NamedField { + comment: Comment( + [], + ), + name: Name { + value: "middle", + span: Span { + start: 391, + end: 397, + }, + }, + ty: Type { + value: Option( + Type { + value: String, + span: Span { + start: 406, + end: 412, + }, + }, + ), + span: Span { + start: 399, + end: 413, + }, + }, + id: Id { + value: 2, + span: Span { + start: 414, + end: 416, + }, + }, + span: Span { + start: 391, + end: 416, + }, + }, + NamedField { + comment: Comment( + [], + ), + name: Name { + value: "last", + span: Span { + start: 422, + end: 426, + }, + }, + ty: Type { + value: String, + span: Span { + start: 428, + end: 434, + }, + }, + id: Id { + value: 3, + span: Span { + start: 435, + end: 437, + }, + }, + span: Span { + start: 422, + end: 437, + }, + }, + ], + ), + }, + ), + TypeAlias( + TypeAlias { + comment: Comment( + [ + CommentLine { + value: "Simple alias for convenience.", + span: Span { + start: 442, + end: 475, + }, + }, + CommentLine { + value: "", + span: Span { + start: 476, + end: 479, + }, + }, + CommentLine { + value: "- Might be easier to remember.", + span: Span { + start: 480, + end: 514, + }, + }, + CommentLine { + value: "- Often referenced as this.", + span: Span { + start: 515, + end: 546, + }, + }, + ], + ), + name: Name { + value: "Name", + span: Span { + start: 552, + end: 556, + }, + }, + generics: Generics( + [], + ), + target: Type { + value: External( + ExternalType { + path: [], + name: Name { + value: "FullName", + span: Span { + start: 559, + end: 567, + }, + }, + generics: [], + }, + ), + span: Span { + start: 559, + end: 567, + }, + }, + }, + ), + Struct( + Struct { + comment: Comment( + [], + ), + attributes: Attributes( + [], + ), + name: Name { + value: "Address", + span: Span { + start: 577, + end: 584, + }, + }, + generics: Generics( + [], + ), + fields: Named( + [ + NamedField { + comment: Comment( + [ + CommentLine { + value: "Street name.", + span: Span { + start: 591, + end: 607, + }, + }, + ], + ), + name: Name { + value: "street", + span: Span { + start: 612, + end: 618, + }, + }, + ty: Type { + value: String, + span: Span { + start: 620, + end: 626, + }, + }, + id: Id { + value: 1, + span: Span { + start: 627, + end: 629, + }, + }, + span: Span { + start: 608, + end: 629, + }, + }, + NamedField { + comment: Comment( + [ + CommentLine { + value: "Number of the house in the street.", + span: Span { + start: 635, + end: 673, + }, + }, + ], + ), + name: Name { + value: "house_no", + span: Span { + start: 678, + end: 686, + }, + }, + ty: Type { + value: External( + ExternalType { + path: [], + name: Name { + value: "HouseNumber", + span: Span { + start: 688, + end: 699, + }, + }, + generics: [], + }, + ), + span: Span { + start: 688, + end: 699, + }, + }, + id: Id { + value: 2, + span: Span { + start: 700, + end: 702, + }, + }, + span: Span { + start: 674, + end: 702, + }, + }, + NamedField { + comment: Comment( + [], + ), + name: Name { + value: "city", + span: Span { + start: 708, + end: 712, + }, + }, + ty: Type { + value: String, + span: Span { + start: 714, + end: 720, + }, + }, + id: Id { + value: 3, + span: Span { + start: 721, + end: 723, + }, + }, + span: Span { + start: 708, + end: 723, + }, + }, + ], + ), + }, + ), + Enum( + Enum { + comment: Comment( + [ + CommentLine { + value: "The number on the house.", + span: Span { + start: 728, + end: 756, + }, + }, + CommentLine { + value: "", + span: Span { + start: 757, + end: 760, + }, + }, + CommentLine { + value: "More details can be found at [Wikipedia](https://en.wikipedia.org/wiki/House_numbering).", + span: Span { + start: 761, + end: 853, + }, + }, + ], + ), + attributes: Attributes( + [], + ), + name: Name { + value: "HouseNumber", + span: Span { + start: 859, + end: 870, + }, + }, + generics: Generics( + [], + ), + variants: [ + Variant { + comment: Comment( + [ + CommentLine { + value: "Digit only number.", + span: Span { + start: 877, + end: 899, + }, + }, + ], + ), + name: Name { + value: "Digit", + span: Span { + start: 904, + end: 909, + }, + }, + fields: Unnamed( + [ + UnnamedField { + ty: Type { + value: U16, + span: Span { + start: 910, + end: 913, + }, + }, + id: Id { + value: 1, + span: Span { + start: 914, + end: 916, + }, + }, + span: Span { + start: 910, + end: 916, + }, + }, + ], + ), + id: Id { + value: 1, + span: Span { + start: 918, + end: 920, + }, + }, + span: Span { + start: 900, + end: 920, + }, + }, + Variant { + comment: Comment( + [ + CommentLine { + value: "Mixed _number_ with characters like `1a`.", + span: Span { + start: 926, + end: 971, + }, + }, + ], + ), + name: Name { + value: "Text", + span: Span { + start: 976, + end: 980, + }, + }, + fields: Unnamed( + [ + UnnamedField { + ty: Type { + value: String, + span: Span { + start: 981, + end: 987, + }, + }, + id: Id { + value: 1, + span: Span { + start: 988, + end: 990, + }, + }, + span: Span { + start: 981, + end: 990, + }, + }, + ], + ), + id: Id { + value: 2, + span: Span { + start: 992, + end: 994, + }, + }, + span: Span { + start: 972, + end: 994, + }, + }, + ], + }, + ), + Const( + Const { + comment: Comment( + [ + CommentLine { + value: "Probably the max age of a human, currently.", + span: Span { + start: 999, + end: 1046, + }, + }, + ], + ), + name: Name { + value: "MAX_AGE", + span: Span { + start: 1053, + end: 1060, + }, + }, + ty: Type { + value: U8, + span: Span { + start: 1062, + end: 1064, + }, + }, + value: Literal { + value: Int( + 120, + ), + span: Span { + start: 1067, + end: 1070, + }, + }, + }, + ), + Module( + Module { + comment: Comment( + [ + CommentLine { + value: "Details for defining birthdays.", + span: Span { + start: 1074, + end: 1109, + }, + }, + ], + ), + name: Name { + value: "birthday", + span: Span { + start: 1114, + end: 1122, + }, + }, + definitions: [ + Enum( + Enum { + comment: Comment( + [ + CommentLine { + value: "As the name suggests, specifies details about birthdays.", + span: Span { + start: 1129, + end: 1189, + }, + }, + ], + ), + attributes: Attributes( + [], + ), + name: Name { + value: "DayOfBirth", + span: Span { + start: 1199, + end: 1209, + }, + }, + generics: Generics( + [], + ), + variants: [ + Variant { + comment: Comment( + [], + ), + name: Name { + value: "Specific", + span: Span { + start: 1220, + end: 1228, + }, + }, + fields: Named( + [ + NamedField { + comment: Comment( + [], + ), + name: Name { + value: "year", + span: Span { + start: 1243, + end: 1247, + }, + }, + ty: Type { + value: U16, + span: Span { + start: 1249, + end: 1252, + }, + }, + id: Id { + value: 1, + span: Span { + start: 1253, + end: 1255, + }, + }, + span: Span { + start: 1243, + end: 1255, + }, + }, + NamedField { + comment: Comment( + [], + ), + name: Name { + value: "month", + span: Span { + start: 1269, + end: 1274, + }, + }, + ty: Type { + value: External( + ExternalType { + path: [], + name: Name { + value: "Month", + span: Span { + start: 1276, + end: 1281, + }, + }, + generics: [], + }, + ), + span: Span { + start: 1276, + end: 1281, + }, + }, + id: Id { + value: 2, + span: Span { + start: 1282, + end: 1284, + }, + }, + span: Span { + start: 1269, + end: 1284, + }, + }, + NamedField { + comment: Comment( + [], + ), + name: Name { + value: "day", + span: Span { + start: 1298, + end: 1301, + }, + }, + ty: Type { + value: U8, + span: Span { + start: 1303, + end: 1305, + }, + }, + id: Id { + value: 3, + span: Span { + start: 1306, + end: 1308, + }, + }, + span: Span { + start: 1298, + end: 1308, + }, + }, + ], + ), + id: Id { + value: 1, + span: Span { + start: 1320, + end: 1322, + }, + }, + span: Span { + start: 1220, + end: 1322, + }, + }, + Variant { + comment: Comment( + [ + CommentLine { + value: "The user didn't want to say.", + span: Span { + start: 1332, + end: 1364, + }, + }, + ], + ), + name: Name { + value: "Secret", + span: Span { + start: 1373, + end: 1379, + }, + }, + fields: Named( + [ + NamedField { + comment: Comment( + [ + CommentLine { + value: "Optional info from the user about why they didn't want to", + span: Span { + start: 1394, + end: 1455, + }, + }, + CommentLine { + value: "reveal their birthday.", + span: Span { + start: 1468, + end: 1494, + }, + }, + ], + ), + name: Name { + value: "reason", + span: Span { + start: 1507, + end: 1513, + }, + }, + ty: Type { + value: Option( + Type { + value: String, + span: Span { + start: 1522, + end: 1528, + }, + }, + ), + span: Span { + start: 1515, + end: 1529, + }, + }, + id: Id { + value: 1, + span: Span { + start: 1530, + end: 1532, + }, + }, + span: Span { + start: 1495, + end: 1532, + }, + }, + ], + ), + id: Id { + value: 2, + span: Span { + start: 1544, + end: 1546, + }, + }, + span: Span { + start: 1365, + end: 1546, + }, + }, + Variant { + comment: Comment( + [ + CommentLine { + value: "We never asked and nobody knows.", + span: Span { + start: 1556, + end: 1592, + }, + }, + ], + ), + name: Name { + value: "Unknown", + span: Span { + start: 1601, + end: 1608, + }, + }, + fields: Unit, + id: Id { + value: 3, + span: Span { + start: 1609, + end: 1611, + }, + }, + span: Span { + start: 1593, + end: 1611, + }, + }, + ], + }, + ), + Const( + Const { + comment: Comment( + [ + CommentLine { + value: "Let's assume we only have details of people born **after** this year.", + span: Span { + start: 1624, + end: 1697, + }, + }, + ], + ), + name: Name { + value: "MIN_YEAR", + span: Span { + start: 1708, + end: 1716, + }, + }, + ty: Type { + value: U16, + span: Span { + start: 1718, + end: 1721, + }, + }, + value: Literal { + value: Int( + 1900, + ), + span: Span { + start: 1724, + end: 1728, + }, + }, + }, + ), + Const( + Const { + comment: Comment( + [ + CommentLine { + value: "Absolute maximum for a day, but might be even less depending", + span: Span { + start: 1735, + end: 1799, + }, + }, + CommentLine { + value: "on the month.", + span: Span { + start: 1804, + end: 1821, + }, + }, + ], + ), + name: Name { + value: "MAX_DAY", + span: Span { + start: 1832, + end: 1839, + }, + }, + ty: Type { + value: U8, + span: Span { + start: 1841, + end: 1843, + }, + }, + value: Literal { + value: Int( + 31, + ), + span: Span { + start: 1846, + end: 1848, + }, + }, + }, + ), + Enum( + Enum { + comment: Comment( + [ + CommentLine { + value: "The month of the year.", + span: Span { + start: 1855, + end: 1881, + }, + }, + ], + ), + attributes: Attributes( + [], + ), + name: Name { + value: "Month", + span: Span { + start: 1891, + end: 1896, + }, + }, + generics: Generics( + [], + ), + variants: [ + Variant { + comment: Comment( + [], + ), + name: Name { + value: "January", + span: Span { + start: 1907, + end: 1914, + }, + }, + fields: Unit, + id: Id { + value: 1, + span: Span { + start: 1915, + end: 1917, + }, + }, + span: Span { + start: 1907, + end: 1917, + }, + }, + Variant { + comment: Comment( + [], + ), + name: Name { + value: "February", + span: Span { + start: 1927, + end: 1935, + }, + }, + fields: Unit, + id: Id { + value: 2, + span: Span { + start: 1936, + end: 1938, + }, + }, + span: Span { + start: 1927, + end: 1938, + }, + }, + Variant { + comment: Comment( + [], + ), + name: Name { + value: "March", + span: Span { + start: 1948, + end: 1953, + }, + }, + fields: Unit, + id: Id { + value: 3, + span: Span { + start: 1954, + end: 1956, + }, + }, + span: Span { + start: 1948, + end: 1956, + }, + }, + Variant { + comment: Comment( + [], + ), + name: Name { + value: "April", + span: Span { + start: 1966, + end: 1971, + }, + }, + fields: Unit, + id: Id { + value: 4, + span: Span { + start: 1972, + end: 1974, + }, + }, + span: Span { + start: 1966, + end: 1974, + }, + }, + Variant { + comment: Comment( + [], + ), + name: Name { + value: "May", + span: Span { + start: 1984, + end: 1987, + }, + }, + fields: Unit, + id: Id { + value: 5, + span: Span { + start: 1988, + end: 1990, + }, + }, + span: Span { + start: 1984, + end: 1990, + }, + }, + Variant { + comment: Comment( + [], + ), + name: Name { + value: "June", + span: Span { + start: 2000, + end: 2004, + }, + }, + fields: Unit, + id: Id { + value: 6, + span: Span { + start: 2005, + end: 2007, + }, + }, + span: Span { + start: 2000, + end: 2007, + }, + }, + Variant { + comment: Comment( + [], + ), + name: Name { + value: "July", + span: Span { + start: 2017, + end: 2021, + }, + }, + fields: Unit, + id: Id { + value: 7, + span: Span { + start: 2022, + end: 2024, + }, + }, + span: Span { + start: 2017, + end: 2024, + }, + }, + Variant { + comment: Comment( + [], + ), + name: Name { + value: "August", + span: Span { + start: 2034, + end: 2040, + }, + }, + fields: Unit, + id: Id { + value: 8, + span: Span { + start: 2041, + end: 2043, + }, + }, + span: Span { + start: 2034, + end: 2043, + }, + }, + Variant { + comment: Comment( + [], + ), + name: Name { + value: "September", + span: Span { + start: 2053, + end: 2062, + }, + }, + fields: Unit, + id: Id { + value: 9, + span: Span { + start: 2063, + end: 2065, + }, + }, + span: Span { + start: 2053, + end: 2065, + }, + }, + Variant { + comment: Comment( + [], + ), + name: Name { + value: "October", + span: Span { + start: 2075, + end: 2082, + }, + }, + fields: Unit, + id: Id { + value: 10, + span: Span { + start: 2083, + end: 2086, + }, + }, + span: Span { + start: 2075, + end: 2086, + }, + }, + Variant { + comment: Comment( + [], + ), + name: Name { + value: "November", + span: Span { + start: 2096, + end: 2104, + }, + }, + fields: Unit, + id: Id { + value: 11, + span: Span { + start: 2105, + end: 2108, + }, + }, + span: Span { + start: 2096, + end: 2108, + }, + }, + Variant { + comment: Comment( + [], + ), + name: Name { + value: "December", + span: Span { + start: 2118, + end: 2126, + }, + }, + fields: Unit, + id: Id { + value: 12, + span: Span { + start: 2127, + end: 2130, + }, + }, + span: Span { + start: 2118, + end: 2130, + }, + }, + ], + }, + ), + ], + }, + ), + ], +} diff --git a/crates/stef-parser/tests/snapshots/parser__print@mixed.stef.snap b/crates/stef-parser/tests/snapshots/parser__print@mixed.stef.snap new file mode 100644 index 0000000..14de9b0 --- /dev/null +++ b/crates/stef-parser/tests/snapshots/parser__print@mixed.stef.snap @@ -0,0 +1,91 @@ +--- +source: crates/stef-parser/tests/parser.rs +description: "/// Basic user information.\n///\n/// Uses various other `structs` and `enums` to compose the information\n/// in a **type safe** way.\nstruct User {\n name: FullName @1,\n /// Physical address, might not be specified by the user.\n address: option
@2,\n age: u8 @3,\n birthday: birthday::DayOfBirth @4,\n}\n\n/// Full name of a user.\nstruct FullName {\n first: string @1,\n middle: option @2,\n last: string @3,\n}\n\n/// Simple alias for convenience.\n///\n/// - Might be easier to remember.\n/// - Often referenced as this.\ntype Name = FullName;\n\nstruct Address {\n /// Street name.\n street: string @1,\n /// Number of the house in the street.\n house_no: HouseNumber @2,\n city: string @3,\n}\n\n/// The number on the house.\n///\n/// More details can be found at [Wikipedia](https://en.wikipedia.org/wiki/House_numbering).\nenum HouseNumber {\n /// Digit only number.\n Digit(u16 @1) @1,\n /// Mixed _number_ with characters like `1a`.\n Text(string @1) @2,\n}\n\n/// Probably the max age of a human, currently.\nconst MAX_AGE: u8 = 120;\n\n\n/// Details for defining birthdays.\nmod birthday {\n /// As the name suggests, specifies details about birthdays.\n enum DayOfBirth {\n Specific {\n year: u16 @1,\n month: Month @2,\n day: u8 @3,\n } @1,\n /// The user didn't want to say.\n Secret {\n /// Optional info from the user about why they didn't want to\n /// reveal their birthday.\n reason: option @1,\n } @2,\n /// We never asked and nobody knows.\n Unknown @3,\n }\n\n /// Let's assume we only have details of people born **after** this year.\n const MIN_YEAR: u16 = 1900;\n\n /// Absolute maximum for a day, but might be even less depending\n /// on the month.\n const MAX_DAY: u8 = 31;\n\n /// The month of the year.\n enum Month {\n January @1,\n February @2,\n March @3,\n April @4,\n May @5,\n June @6,\n July @7,\n August @8,\n September @9,\n October @10,\n November @11,\n December @12,\n }\n}" +input_file: crates/stef-parser/tests/inputs/mixed.stef +--- +/// Basic user information. +/// +/// Uses various other `structs` and `enums` to compose the information +/// in a **type safe** way. +struct User { + name: FullName @1, + /// Physical address, might not be specified by the user. + address: option
@2, + age: u8 @3, + birthday: birthday::DayOfBirth @4, +} + +/// Full name of a user. +struct FullName { + first: string @1, + middle: option @2, + last: string @3, +} + +/// Simple alias for convenience. +/// +/// - Might be easier to remember. +/// - Often referenced as this. +type Name = FullName; +struct Address { + /// Street name. + street: string @1, + /// Number of the house in the street. + house_no: HouseNumber @2, + city: string @3, +} + +/// The number on the house. +/// +/// More details can be found at [Wikipedia](https://en.wikipedia.org/wiki/House_numbering). +enum HouseNumber { + /// Digit only number. + Digit(u16 @1) @1, + /// Mixed _number_ with characters like `1a`. + Text(string @1) @2, +} + +/// Probably the max age of a human, currently. +const MAX_AGE: u8 = 120; +/// Details for defining birthdays. +mod birthday { + /// As the name suggests, specifies details about birthdays. + enum DayOfBirth { + Specific { + year: u16 @1, + month: Month @2, + day: u8 @3, + } @1, + /// The user didn't want to say. + Secret { + /// Optional info from the user about why they didn't want to + /// reveal their birthday. + reason: option @1, + } @2, + /// We never asked and nobody knows. + Unknown @3, + } + + /// Let's assume we only have details of people born **after** this year. + const MIN_YEAR: u16 = 1900; + /// Absolute maximum for a day, but might be even less depending + /// on the month. + const MAX_DAY: u8 = 31; + /// The month of the year. + enum Month { + January @1, + February @2, + March @3, + April @4, + May @5, + June @6, + July @7, + August @8, + September @9, + October @10, + November @11, + December @12, + } +} + + diff --git a/package.json b/package.json index 3817b30..7f4c789 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,9 @@ { "name": "stef", "private": true, - "workspaces": ["book/highlight", "vscode-extension"] + "workspaces": [ + "book/highlight", + "crates/stef-doc", + "vscode-extension" + ] } diff --git a/vscode-extension/package.json b/vscode-extension/package.json index 11e1818..97d3a72 100644 --- a/vscode-extension/package.json +++ b/vscode-extension/package.json @@ -116,7 +116,7 @@ "@biomejs/biome": "^1.4.1", "@types/vscode": "^1.85.0", "@vscode/vsce": "^2.22.0", - "esbuild": "^0.19.9", + "esbuild": "^0.19.10", "js-yaml": "^4.1.0" } }