diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..190cf08 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Jesús Pérez + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..34f67aa --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,231 @@ +# TypeDialog License + +## Project License + +TypeDialog is licensed under the **MIT License**. + +See [LICENSE](LICENSE) file for the full MIT license text. + +--- + +## Dependencies + +This project includes the following dependencies under their respective licenses: + +### Apache-2.0 Only (3) +- rpassword 7.4.0 +- rtoolbox 0.0.3 +- sync_wrapper 1.0.2 + +### MIT Only (66) +- atty 0.2.14 +- axum 0.8.7 +- axum-core 0.5.5 +- bytes 1.11.0 +- castaway 0.2.4 +- compact_str 0.8.1 +- console 0.16.2 +- convert_case 0.10.0 +- crossterm 0.28.1 +- crossterm 0.29.0 +- crossterm_winapi 0.9.1 +- darling 0.20.11 +- darling_core 0.20.11 +- darling_macro 0.20.11 +- derive_more 2.1.0 +- derive_more-impl 2.1.0 +- dialoguer 0.12.0 +- typedialog 0.1.0 +- typedialog-core 0.1.0 +- typedialog-tui 0.1.0 +- typedialog-web 0.1.0 +- fuzzy-matcher 0.3.7 +- generic-array 0.14.7 +- globwalk 0.9.1 +- http-body 1.0.1 +- http-body-util 0.1.3 +- http-range-header 0.4.2 +- hyper 1.8.1 +- hyper-util 0.1.19 +- inquire 0.9.1 +- instability 0.3.10 +- libm 0.2.15 +- libredox 0.1.10 +- lru 0.12.5 +- mime_guess 2.0.5 +- mio 1.1.1 +- nu-ansi-term 0.50.3 +- parse-zoneinfo 0.3.1 +- phf 0.11.3 +- phf_codegen 0.11.3 +- phf_generator 0.11.3 +- phf_shared 0.11.3 +- ratatui 0.29.0 +- redox_syscall 0.5.18 +- redox_users 0.5.2 +- sharded-slab 0.1.7 +- slab 0.4.11 +- strsim 0.11.1 +- strum 0.26.3 +- strum_macros 0.26.4 +- tera 1.20.1 +- tokio 1.48.0 +- tokio-macros 2.6.0 +- tokio-util 0.7.17 +- tower 0.5.2 +- tower-http 0.6.8 +- tower-layer 0.3.3 +- tower-service 0.3.3 +- tracing 0.1.43 +- tracing-attributes 0.1.31 +- tracing-core 0.1.35 +- tracing-log 0.2.0 +- tracing-subscriber 0.3.22 +- unsafe-libyaml 0.2.11 +- valuable 0.1.1 +- winnow 0.7.14 + +### Apache-2.0 OR MIT (190) + +Most dependencies use dual licensing between Apache-2.0 and MIT. + +- allocator-api2 0.2.21 | - android_system_properties 0.1.5 | - anstream 0.6.21 +- anstyle 1.0.13 | - anstyle-parse 0.2.7 | - anstyle-query 1.1.5 +- anstyle-wincon 3.0.11 | - anyhow 1.0.100 | - async-trait 0.1.89 +- atomic-waker 1.1.2 | - autocfg 1.5.0 | - bitflags 2.10.0 +- block-buffer 0.10.4 | - bstr 1.12.1 | - bumpalo 3.19.0 +- cassowary 0.3.0 | - cc 1.2.49 | - cfg-if 1.0.4 +- chrono 0.4.42 | - chrono-tz 0.9.0 | - chrono-tz-build 0.3.0 +- clap 4.5.53 | - clap_builder 4.5.53 | - clap_derive 4.5.49 +- clap_lex 0.7.6 | - colorchoice 1.0.4 | - core-foundation-sys 0.8.7 +- cpufeatures 0.2.17 | - crossbeam-deque 0.8.6 | - crossbeam-epoch 0.9.18 +- crossbeam-utils 0.8.21 | - crypto-common 0.1.7 | - digest 0.10.7 +- dirs 6.0.0 | - dirs-sys 0.5.0 | - displaydoc 0.2.5 +- document-features 0.2.12 | - dyn-clone 1.0.20 | - either 1.15.0 +- encode_unicode 1.0.0 | - equivalent 1.0.2 | - errno 0.3.14 +- fastrand 2.3.0 | - find-msvc-tools 0.1.5 | - fluent 0.17.0 +- fluent-bundle 0.16.0 | - fluent-langneg 0.13.1 | - fluent-syntax 0.12.0 +- fnv 1.0.7 | - form_urlencoded 1.2.2 | - futures 0.3.31 +- futures-channel 0.3.31 | - futures-core 0.3.31 | - futures-executor 0.3.31 +- futures-io 0.3.31 | - futures-macro 0.3.31 | - futures-sink 0.3.31 +- futures-task 0.3.31 | - futures-util 0.3.31 | - getrandom 0.2.16 +- getrandom 0.3.4 | - hashbrown 0.15.5 | - hashbrown 0.16.1 +- heck 0.5.0 | - hermit-abi 0.1.19 | - http 1.4.0 +- httparse 1.10.1 | - httpdate 1.0.3 | - humansize 2.1.3 +- iana-time-zone 0.1.64 | - iana-time-zone-haiku 0.1.2 | - ident_case 1.0.1 +- indexmap 2.12.1 | - indoc 2.0.7 | - intl-memoizer 0.5.3 +- intl_pluralrules 7.0.2 | - is_terminal_polyfill 1.70.2 | - itertools 0.13.0 +- itoa 1.0.15 | - js-sys 0.3.83 | - lazy_static 1.5.0 +- libc 0.2.178 | - litrs 1.0.0 | - lock_api 0.4.14 +- log 0.4.29 | - mime 0.3.17 | - num-traits 0.2.19 +- once_cell 1.21.3 | - once_cell_polyfill 1.70.2 | - parking_lot 0.12.5 +- parking_lot_core 0.9.12 | - paste 1.0.15 | - percent-encoding 2.3.2 +- pest 2.8.4 | - pest_derive 2.8.4 | - pest_generator 2.8.4 +- pest_meta 2.8.4 | - pin-project-lite 0.2.16 | - pin-utils 0.1.0 +- ppv-lite86 0.2.21 | - proc-macro2 1.0.103 | - quote 1.0.42 +- rand 0.8.5 | - rand_chacha 0.3.1 | - rand_core 0.6.4 +- regex 1.12.2 | - regex-automata 0.4.13 | - regex-syntax 0.8.8 +- rustc-hash 2.1.1 | - rustc_version 0.4.1 | - rustversion 1.0.22 +- scopeguard 1.2.0 | - semver 1.0.27 | - serde 1.0.228 +- serde_core 1.0.228 | - serde_derive 1.0.228 | - serde_json 1.0.145 +- serde_path_to_error 0.1.20 | - serde_spanned 1.0.3 | - serde_urlencoded 0.7.1 +- serde_yaml 0.9.34+deprecated | - sha2 0.10.9 | - shell-words 1.1.0 +- shlex 1.3.0 | - signal-hook 0.3.18 | - signal-hook-mio 0.2.5 +- signal-hook-registry 1.4.7 | - siphasher 1.0.1 | - slug 0.1.6 +- smallvec 1.15.1 | - socket2 0.6.1 | - static_assertions 1.1.0 +- syn 2.0.111 | - sys-locale 0.3.2 | - tempfile 3.23.0 +- thiserror 2.0.17 | - thiserror-impl 2.0.17 | - thread_local 1.1.9 +- toml 0.9.8 | - toml_datetime 0.7.3 | - toml_parser 1.0.4 +- toml_writer 1.0.4 | - type-map 0.5.1 | - typenum 1.19.0 +- ucd-trie 0.1.7 | - unic-langid 0.9.6 | - unic-langid-impl 0.9.6 +- unicase 2.8.1 | - unicode-segmentation 1.12.0 | - unicode-truncate 1.1.0 +- unicode-width 0.1.14 | - unicode-width 0.2.0 | - utf8parse 0.2.2 +- version_check 0.9.5 | - wasm-bindgen 0.2.106 | - wasm-bindgen-macro 0.2.106 +- wasm-bindgen-macro-support 0.2.106 | - wasm-bindgen-shared 0.2.106 | - winapi 0.3.9 +- winapi-i686-pc-windows-gnu 0.4.0 | - winapi-x86_64-pc-windows-gnu 0.4.0 | - windows-core 0.62.2 +- windows-implement 0.60.2 | - windows-interface 0.59.3 | - windows-link 0.2.1 +- windows-result 0.4.1 | - windows-strings 0.5.1 | - windows-sys 0.52.0 +- windows-sys 0.59.0 | - windows-sys 0.60.2 | - windows-sys 0.61.2 +- windows-targets 0.52.6 | - windows-targets 0.53.5 | - windows_aarch64_gnullvm 0.52.6 +- windows_aarch64_gnullvm 0.53.1 | - windows_aarch64_msvc 0.52.6 | - windows_aarch64_msvc 0.53.1 +- windows_i686_gnu 0.52.6 | - windows_i686_gnu 0.53.1 | - windows_i686_gnullvm 0.52.6 +- windows_i686_gnullvm 0.53.1 | - windows_i686_msvc 0.52.6 | - windows_i686_msvc 0.53.1 +- windows_x86_64_gnu 0.52.6 | - windows_x86_64_gnu 0.53.1 | - windows_x86_64_gnullvm 0.52.6 +- windows_x86_64_gnullvm 0.53.1 | - windows_x86_64_msvc 0.52.6 | - windows_x86_64_msvc 0.53.1 +- zeroize 1.8.2 | + +### MIT OR Unlicense (7) +- aho-corasick 1.1.4 +- globset 0.4.18 +- ignore 0.4.25 +- memchr 2.7.6 +- same-file 1.0.6 +- walkdir 2.5.0 +- winapi-util 0.1.11 + +### Other Licenses + +**(Apache-2.0 OR MIT) AND Unicode-3.0** (1) +- unicode-ident 1.0.22 + +**Apache-2.0 OR Apache-2.0 WITH LLVM-exception OR MIT** (7) +- linux-raw-sys 0.4.15 +- linux-raw-sys 0.11.0 +- rustix 0.38.44 +- rustix 1.1.2 +- wasi 0.11.1+wasi-snapshot-preview1 +- wasip2 1.0.1+wasi-0.2.4 +- wit-bindgen 0.46.0 + +**Apache-2.0 OR BSD-2-Clause OR MIT** (2) +- zerocopy 0.8.31 +- zerocopy-derive 0.8.31 + +**Apache-2.0 OR BSL-1.0** (1) +- ryu 1.0.20 + +**Apache-2.0 OR GPL-2.0** (1) +- self_cell 1.2.1 + +**Apache-2.0 OR LGPL-2.1-or-later OR MIT** (1) +- r-efi 5.3.0 + +**BSD-3-Clause** (1) +- deunicode 1.6.2 + +**BSD-3-Clause AND MIT** (1) +- matchit 0.8.4 + +**MPL-2.0** (1) +- option-ext 0.2.0 + +**Unicode-3.0** (3) +- tinystr 0.8.2 +- zerofrom 0.1.6 +- zerovec 0.11.5 + +**Zlib** (1) +- foldhash 0.1.5 + +--- + +## Summary + +- **Project License**: MIT +- **Total Dependencies**: 286 +- **Unique License Types**: 15 different combinations +- **Primary License Pattern**: Apache-2.0 OR MIT (most dependencies) + +### Compliance + +All dependencies are compatible with the MIT license under: +- Permissive licenses (MIT, Apache-2.0, BSD-3-Clause, MPL-2.0, Zlib) +- Weak copyleft (LGPL-2.1-or-later, MPL-2.0) +- Public domain (Unlicense, Unicode-3.0) + +### Generated + +- Date: 2025-12-17T13:08:58.510231 +- Tool: cargo-license + +See [DEPENDENCIES.md](DEPENDENCIES.md) for the complete dependency tree. diff --git a/config/README.md b/config/README.md new file mode 100644 index 0000000..c3add23 --- /dev/null +++ b/config/README.md @@ -0,0 +1,358 @@ +# Configuration Files + +Pre-configured settings for each typedialog backend and environment. + +## Overview + +Configuration files are organized by **backend** (CLI, TUI, Web) and **environment** (default, dev, production). + +``` +config/ +├── cli/ +│ ├── default.toml # Standard CLI settings +│ ├── dev.toml # Development (debugging enabled) +│ └── production.toml # Production (optimized, hardened) +├── tui/ +│ ├── default.toml # Standard TUI settings +│ ├── dev.toml # Development features enabled +│ └── production.toml # Optimized for deployment +└── web/ + ├── default.toml # Standard web server settings + ├── dev.toml # Development (hot reload) + └── production.toml # Hardened for production +``` + +## Backend Configurations + +### CLI Backend + +**command-line interface** - Simple text-based forms for scripts and automation. + +| Config | Purpose | Debug | Colors | Timeout | +|--------|---------|-------|--------|---------| +| default | Standard | No | Yes | 300s | +| dev | Development | Yes | Yes | 300s | +| production | Production | No | Yes | 3600s | + +**Usage:** +```bash +typedialog --config config/cli/production.toml form.toml +``` + +**Features:** +- Inline validation +- Optional mouse support +- Placeholder text display +- Help text display + +### TUI Backend + +**terminal user interface** - Interactive multi-panel forms with navigation. + +| Config | Purpose | Borders | Animations | Render | +|--------|---------|---------|-----------|--------| +| default | Standard | Rounded | Enabled | Auto | +| dev | Development | Double | Enabled | Debug | +| production | Production | Rounded | Disabled | Optimized | + +**Usage:** +```bash +typedialog-tui --config config/tui/production.toml form.toml +``` + +**Features:** +- 3-panel layout (fields, input, buttons) +- Mouse support +- Keyboard navigation +- Real-time field updates + +### Web Backend + +**HTTP server** - Browser-based forms with REST API. + +| Config | Purpose | CORS | HTTPS | Rate Limit | +|--------|---------|------|-------|-----------| +| default | Standard | Localhost | No | Unlimited | +| dev | Development | Enabled | No | Unlimited | +| production | Production | Restricted | Required | 100/min | + +**Usage:** +```bash +typedialog-web --config config/web/production.toml +# Server starts on http://localhost:8080 +``` + +**Features:** +- HTML/CSS rendering +- CSRF protection +- Response caching +- Gzip compression + +## Configuration by Environment + +### Development Configuration + +Enabled features for development and debugging: + +```toml +# CLI Dev +debug_output = true +log_level = "debug" +show_field_types = true + +# TUI Dev +show_field_indices = true +trace_rendering = false + +# Web Dev +hot_reload = true +debug = true +logs = "/tmp/typedialog-web.log" +``` + +**Usage:** +```bash +typedialog --config config/cli/dev.toml form.toml +typedialog-tui --config config/tui/dev.toml form.toml +typedialog-web --config config/web/dev.toml +``` + +### Production Configuration + +Hardened settings optimized for deployment: + +```toml +# CLI Production +debug_output = false +log_level = "error" +show_placeholders = false +timeout = 3600 + +# TUI Production +enable_animations = false +render_throttle = 16ms +max_fps = 60 + +# Web Production +require_https = true +csrf_enabled = true +rate_limit = 100 +cache_ttl = 3600 +``` + +**Usage:** +```bash +typedialog --config config/cli/production.toml form.toml +typedialog-tui --config config/tui/production.toml form.toml +typedialog-web --config config/web/production.toml +``` + +## Common Settings + +### Form Configuration + +```toml +[form] +title = "Form Title" +description = "Optional description" + +[form.validation] +validate_on_change = true +show_errors_inline = true +strict_validation = true +``` + +### Output Configuration + +```toml +[output] +format = "json" # json, yaml, toml, text +pretty_print = true +debug_output = false +``` + +### Logging + +```toml +[logging] +level = "info" # debug, info, warn, error +file = "/var/log/typedialog/app.log" +``` + +## Custom Configuration + +### Creating Custom Configurations + +Create a new TOML file based on an environment template: + +```bash +# Copy production config as base +cp config/cli/production.toml config/cli/custom.toml + +# Edit for your needs +nano config/cli/custom.toml + +# Use it +typedialog --config config/cli/custom.toml form.toml +``` + +### Override Specific Settings + +Use environment variables to override config: + +```bash +# CLI Backend +export TYPEDIALOG_DEBUG=1 +export TYPEDIALOG_LOG_LEVEL=debug +typedialog --config config/cli/default.toml form.toml + +# TUI Backend +export TYPEDIALOG_TUI_BORDER=double +typedialog-tui --config config/tui/default.toml form.toml + +# Web Backend +export TYPEDIALOG_WEB_PORT=3000 +export TYPEDIALOG_WEB_CORS_ORIGINS="localhost,example.com" +typedialog-web --config config/web/default.toml +``` + +## CLI Backend Configuration Details + +```toml +[terminal] +use_raw_mode = true # Enable raw terminal mode +enable_mouse = false # Mouse support +use_color = true # Colored output + +[appearance] +theme = "default" # Color theme +show_help = true # Display field help +show_placeholders = true # Show placeholder text + +[validation] +validate_on_change = true # Real-time validation +show_errors_inline = true # Inline error messages + +[timeout] +max_duration = 3600 # Max form time (seconds) +input_timeout = 300 # Field input timeout +``` + +## TUI Backend Configuration Details + +```toml +[terminal] +use_raw_mode = true +enable_mouse = true +enable_scrolling = true +height = -1 # -1 = auto +width = -1 # -1 = auto + +[ui] +show_borders = true +show_focus = true +highlight_on_hover = true +enable_animations = true + +[appearance] +theme = "default" +border_style = "rounded" # rounded, double, simple +color_scheme = "default" + +[keyboard] +vi_mode = false +emacs_mode = false + +[performance] +render_throttle = 16 # milliseconds +max_fps = 60 # frames per second +``` + +## Web Backend Configuration Details + +```toml +[server] +host = "0.0.0.0" +port = 8080 +workers = 4 + +[html] +css_framework = "none" # bootstrap, tailwind, none +inline_styles = false +responsive = true +dark_mode = true + +[submission] +method = "post" +webhook_url = "https://api.example.com/forms" +redirect_on_success = true +redirect_url = "https://example.com/thank-you" + +[security] +csrf_enabled = true +rate_limit = 100 # requests per minute +require_https = true +add_security_headers = true + +[performance] +cache_static = true +cache_ttl = 3600 +enable_compression = true +compression_threshold = 1024 +``` + +## Distribution Configurations + +When creating release distributions, all configurations are included: + +```bash +# Build and package +just distro::build-release +just distro::create-package + +# Result includes all configs +distribution/typedialog-0.1.0/ +├── config/ +│ ├── cli/ +│ ├── tui/ +│ └── web/ +└── ... +``` + +Users can then choose configs during installation: + +```bash +# Extract distribution +tar -xzf typedialog-0.1.0.tar.gz +cd typedialog-0.1.0 + +# Install +bash installers/install.sh + +# Use specific config +typedialog --config ~/.config/typedialog/cli/production.toml form.toml +``` + +## Best Practices + +### Development +- Use `dev` configurations for local development +- Enable debugging and verbose logging +- Use shorter timeouts for faster iteration + +### Testing +- Use `default` configurations for testing +- Run integration tests with all environments + +### Production +- Always use `production` configurations +- Verify HTTPS is enabled (web backend) +- Set appropriate rate limits +- Configure logging to persistent location +- Test thoroughly in staging first + +## Related Documentation + +- [BUILD_AND_DISTRIBUTION.md](../BUILD_AND_DISTRIBUTION.md) - Build guide +- [DISTRIBUTION_WORKFLOW.md](../DISTRIBUTION_WORKFLOW.md) - Release workflow +- [README.md](../README.md) - Main documentation diff --git a/config/cli/default.toml b/config/cli/default.toml new file mode 100644 index 0000000..24891f5 --- /dev/null +++ b/config/cli/default.toml @@ -0,0 +1,30 @@ +# CLI Backend - Default Configuration +# Used for standard command-line form rendering + +[form] +title = "CLI Form" +description = "Standard command-line interface form" + +[form.validation] +validate_on_change = true +show_errors_inline = true + +[output] +format = "json" +pretty_print = true + +[terminal] +# Use raw mode for better terminal control +use_raw_mode = true +# Enable mouse support if available +enable_mouse = false +# Use color output +use_color = true + +[appearance] +# Theme: default, monochrome, dark +theme = "default" +# Show field help text +show_help = true +# Show field placeholders +show_placeholders = true diff --git a/config/cli/dev.toml b/config/cli/dev.toml new file mode 100644 index 0000000..2d6e544 --- /dev/null +++ b/config/cli/dev.toml @@ -0,0 +1,32 @@ +# CLI Backend - Development Configuration +# Extended configuration for development and testing + +[form] +title = "CLI Form (Dev)" +description = "Development CLI form with debugging enabled" + +[form.validation] +validate_on_change = true +show_errors_inline = true +strict_validation = true + +[output] +format = "json" +pretty_print = true +debug_output = true + +[terminal] +use_raw_mode = true +enable_mouse = true +use_color = true + +[appearance] +theme = "default" +show_help = true +show_placeholders = true +show_field_types = true + +[debug] +enabled = true +trace_execution = false +log_level = "info" diff --git a/config/cli/production.toml b/config/cli/production.toml new file mode 100644 index 0000000..17c732e --- /dev/null +++ b/config/cli/production.toml @@ -0,0 +1,37 @@ +# CLI Backend - Production Configuration +# Optimized for production deployment + +[form] +title = "Form" +description = "" + +[form.validation] +validate_on_change = true +show_errors_inline = true +strict_validation = true + +[output] +format = "json" +pretty_print = false +# Suppress debugging info +debug_output = false + +[terminal] +use_raw_mode = true +enable_mouse = false +use_color = true + +[appearance] +theme = "default" +show_help = false +show_placeholders = false + +[logging] +level = "error" +file = "/var/log/typedialog/cli.log" + +[timeout] +# Maximum form completion time (seconds) +max_duration = 3600 +# Field input timeout +input_timeout = 300 diff --git a/config/tui/default.toml b/config/tui/default.toml new file mode 100644 index 0000000..9e9dccc --- /dev/null +++ b/config/tui/default.toml @@ -0,0 +1,46 @@ +# TUI Backend - Default Configuration +# Terminal User Interface rendering + +[form] +title = "TUI Form" +description = "Interactive terminal user interface form" + +[form.validation] +validate_on_change = true +show_errors_inline = true + +[output] +format = "json" +pretty_print = true + +[terminal] +# Full TUI features +use_raw_mode = true +enable_mouse = true +enable_scrolling = true +# Fixed height (-1 = auto) +height = -1 +# Fixed width (-1 = auto) +width = -1 + +[ui] +# Show field borders +show_borders = true +# Show field focus indicator +show_focus = true +# Highlight on hover +highlight_on_hover = true +# Animation enabled +enable_animations = true + +[appearance] +theme = "default" +border_style = "rounded" +# Color scheme: default, dark, light, high_contrast +color_scheme = "default" + +[keyboard] +# Vi-style navigation (hjkl) +vi_mode = false +# Emacs-style navigation +emacs_mode = false diff --git a/config/tui/dev.toml b/config/tui/dev.toml new file mode 100644 index 0000000..631aaef --- /dev/null +++ b/config/tui/dev.toml @@ -0,0 +1,45 @@ +# TUI Backend - Development Configuration +# Extended TUI features for development + +[form] +title = "TUI Form (Dev)" +description = "Development TUI form with all features enabled" + +[form.validation] +validate_on_change = true +show_errors_inline = true +strict_validation = true + +[output] +format = "json" +pretty_print = true +debug_output = true + +[terminal] +use_raw_mode = true +enable_mouse = true +enable_scrolling = true +height = -1 +width = -1 + +[ui] +show_borders = true +show_focus = true +highlight_on_hover = true +enable_animations = true +# Show field indices for debugging +show_field_indices = true + +[appearance] +theme = "default" +border_style = "double" +color_scheme = "default" + +[keyboard] +vi_mode = false +emacs_mode = false + +[debug] +enabled = true +log_level = "debug" +trace_rendering = false diff --git a/config/tui/production.toml b/config/tui/production.toml new file mode 100644 index 0000000..f3a5e12 --- /dev/null +++ b/config/tui/production.toml @@ -0,0 +1,48 @@ +# TUI Backend - Production Configuration +# Optimized TUI for production deployment + +[form] +title = "" +description = "" + +[form.validation] +validate_on_change = true +show_errors_inline = true +strict_validation = true + +[output] +format = "json" +pretty_print = false +debug_output = false + +[terminal] +use_raw_mode = true +enable_mouse = true +enable_scrolling = true +height = -1 +width = -1 + +[ui] +show_borders = true +show_focus = true +highlight_on_hover = true +enable_animations = false + +[appearance] +theme = "default" +border_style = "rounded" +color_scheme = "default" + +[keyboard] +vi_mode = false +emacs_mode = false + +[logging] +level = "error" +file = "/var/log/typedialog/tui.log" + +[performance] +# Render throttle (milliseconds) +render_throttle = 16 +# Max update frequency (Hz) +max_fps = 60 diff --git a/config/web/default.toml b/config/web/default.toml new file mode 100644 index 0000000..d81fd1d --- /dev/null +++ b/config/web/default.toml @@ -0,0 +1,48 @@ +# Web Backend - Default Configuration +# HTTP server and web form rendering + +[server] +host = "localhost" +port = 3000 +# CORS settings +cors_enabled = true +cors_origins = ["localhost", "127.0.0.1"] + +[form] +title = "Web Form" +description = "Interactive web form" + +[form.validation] +validate_on_change = true +show_errors_inline = true +client_validation = true + +[output] +format = "json" + +[html] +# CSS framework: bootstrap, tailwind, none +css_framework = "none" +# Include inline styles +inline_styles = false +# Mobile responsive +responsive = true +# Dark mode support +dark_mode = true + +[submission] +# Submission method: post, put, patch +method = "post" +# Optional webhook URL for submissions +webhook_url = "" +# Redirect after submission +redirect_on_success = false +redirect_url = "" + +[security] +# CSRF protection +csrf_enabled = true +# Rate limiting (requests per minute) +rate_limit = 0 +# Require HTTPS +require_https = false diff --git a/config/web/dev.toml b/config/web/dev.toml new file mode 100644 index 0000000..be7e51d --- /dev/null +++ b/config/web/dev.toml @@ -0,0 +1,50 @@ +# Web Backend - Development Configuration +# Enhanced features for development + +[server] +host = "0.0.0.0" +port = 3000 +# Enable hot reload +hot_reload = true +# Debug mode +debug = true + +[form] +title = "Web Form (Dev)" +description = "Development web form" + +[form.validation] +validate_on_change = true +show_errors_inline = true +client_validation = true + +[output] +format = "json" + +[html] +css_framework = "none" +inline_styles = true +responsive = true +dark_mode = true +# Show field metadata +show_field_metadata = true + +[submission] +method = "post" +webhook_url = "http://localhost:8000/webhook" +redirect_on_success = false +log_submissions = true + +[security] +csrf_enabled = true +rate_limit = 0 +require_https = false + +[logging] +level = "debug" +file = "/tmp/typedialog-web.log" + +[api] +# API documentation enabled +enable_docs = true +docs_path = "/docs" diff --git a/config/web/production.toml b/config/web/production.toml new file mode 100644 index 0000000..df7a059 --- /dev/null +++ b/config/web/production.toml @@ -0,0 +1,65 @@ +# Web Backend - Production Configuration +# Hardened configuration for production deployment + +[server] +host = "0.0.0.0" +port = 8080 +# Disable development features +hot_reload = false +debug = false +# Worker threads +workers = 4 + +[form] +title = "" +description = "" + +[form.validation] +validate_on_change = true +show_errors_inline = true +client_validation = true + +[output] +format = "json" + +[html] +css_framework = "none" +inline_styles = false +responsive = true +dark_mode = true + +[submission] +method = "post" +# Required: webhook for production submissions +webhook_url = "https://api.example.com/forms" +redirect_on_success = true +redirect_url = "https://example.com/thank-you" + +[security] +# Strict CSRF protection +csrf_enabled = true +# Rate limiting: requests per minute per IP +rate_limit = 100 +# Require HTTPS +require_https = true +# Security headers +add_security_headers = true + +[logging] +level = "error" +file = "/var/log/typedialog/web.log" + +[performance] +# Cache static assets +cache_static = true +cache_ttl = 3600 +# Enable gzip compression +enable_compression = true +# Minimum response size for compression (bytes) +compression_threshold = 1024 + +[tls] +# Optional TLS configuration +enabled = false +cert_path = "/etc/typedialog/cert.pem" +key_path = "/etc/typedialog/key.pem" diff --git a/installers/bootstrap/README.md b/installers/bootstrap/README.md new file mode 100644 index 0000000..4e65e98 --- /dev/null +++ b/installers/bootstrap/README.md @@ -0,0 +1,183 @@ +# TypeDialog Bootstrap Installers + +Quick installation scripts for all platforms. + +## Linux & macOS + +Install with `curl`: +```bash +curl -fsSL https://github.com/anthropics/typedialog/releases/download/latest/install.sh | bash +``` + +Or with `wget`: +```bash +wget -qO - https://github.com/anthropics/typedialog/releases/download/latest/install.sh | bash +``` + +## Windows + +Open PowerShell and run: +```powershell +irm https://github.com/anthropics/typedialog/releases/download/latest/install.ps1 | iex +``` + +## Manual Installation + +### Linux & macOS + +```bash +# Download script +wget https://github.com/anthropics/typedialog/releases/download/latest/install.sh + +# Run installer +bash install.sh + +# Or with environment variables +INSTALL_DIR="$HOME/bin" CONFIG_DIR="$HOME/.typedialog" bash install.sh +``` + +### Windows + +```powershell +# Download script +Invoke-WebRequest -Uri "https://github.com/anthropics/typedialog/releases/download/latest/install.ps1" -OutFile "install.ps1" + +# Run installer +.\install.ps1 + +# Or with parameters +.\install.ps1 -InstallDir "C:\Program Files\typedialog" -ConfigDir "C:\Users\$env:USERNAME\AppData\Local\typedialog" +``` + +## Configuration + +### Linux & macOS Environment Variables + +- `INSTALL_DIR` - Installation directory (default: `$HOME/.local/bin`) +- `CONFIG_DIR` - Configuration directory (default: `$HOME/.config/typedialog`) +- `VERSION` - Release version (default: `latest`) + +### Windows Parameters + +- `-InstallDir` - Installation directory (default: `$PROFILE\.local\bin`) +- `-ConfigDir` - Configuration directory (default: `$PROFILE\.config\typedialog`) +- `-Version` - Release version (default: `latest`) + +## What Gets Installed + +### Binaries +- `typedialog` - CLI backend +- `typedialog-tui` - TUI backend +- `typedialog-web` - Web backend + +### Configuration Files +- CLI configs: `cli/default.toml`, `cli/dev.toml`, `cli/production.toml` +- TUI configs: `tui/default.toml`, `tui/dev.toml`, `tui/production.toml` +- Web configs: `web/default.toml`, `web/dev.toml`, `web/production.toml` + +## Verification + +After installation, verify everything works: + +```bash +# Check CLI backend +typedialog --version + +# Check TUI backend +typedialog-tui --version + +# Check web backend +typedialog-web --version + +# List available configurations +ls ~/.config/typedialog/ +``` + +## Troubleshooting + +### Command not found + +Update your PATH: + +**bash:** +```bash +echo 'export PATH="$PATH:$HOME/.local/bin"' >> ~/.bashrc +source ~/.bashrc +``` + +**zsh:** +```bash +echo 'export PATH="$PATH:$HOME/.local/bin"' >> ~/.zshrc +source ~/.zshrc +``` + +### Permission denied + +Make binaries executable: +```bash +chmod +x ~/.local/bin/typedialog* +``` + +### Configuration issues + +Check configuration directory: +```bash +cat ~/.config/typedialog/cli/default.toml +``` + +## Uninstallation + +Remove installed files: + +```bash +# Remove binaries +rm ~/.local/bin/typedialog* + +# Remove configurations (optional) +rm -rf ~/.config/typedialog/ +``` + +## Platform Support + +| Platform | Status | Architecture | +|----------|--------|--------------| +| Linux (glibc) | ✓ | x86_64, aarch64 | +| macOS | ✓ | x86_64, aarch64 | +| Windows | ✓ | x86_64 | + +## Requirements + +### Linux & macOS +- `curl` or `wget` +- `tar` and `gzip` +- Bash 4.0+ + +### Windows +- PowerShell 3.0+ +- .NET Framework 4.5+ (usually pre-installed) + +## Manual Build & Install + +To build and install from source: + +```bash +# Clone repository +git clone https://github.com/anthropics/typedialog.git +cd typedialog + +# Build release +./scripts/build_all.sh release + +# Create distribution +./scripts/create_distro.sh v0.1.0 + +# Install from distribution +tar -xzf distribution/typedialog-0.1.0.tar.gz +cp typedialog-0.1.0/bin/* ~/.local/bin/ +cp -r typedialog-0.1.0/config/* ~/.config/typedialog/ +``` + +## Support + +- [GitHub Issues](https://github.com/anthropics/typedialog/issues) +- [Documentation](https://github.com/anthropics/typedialog/blob/main/README.md) diff --git a/installers/bootstrap/install.ps1 b/installers/bootstrap/install.ps1 new file mode 100644 index 0000000..7c107ce --- /dev/null +++ b/installers/bootstrap/install.ps1 @@ -0,0 +1,185 @@ +# TypeDialog Installation Script for Windows +# Installs typedialog binaries and configuration + +param( + [string]$InstallDir = "$env:USERPROFILE\.local\bin", + [string]$ConfigDir = "$env:USERPROFILE\.config\typedialog", + [string]$Version = "latest" +) + +$ErrorActionPreference = "Stop" +$GitHubRepo = "anthropics/typedialog" + +# Function: Print info message +function Write-Info { + param([string]$Message) + Write-Host "[INFO] $Message" -ForegroundColor Cyan +} + +# Function: Print error message +function Write-Err { + param([string]$Message) + Write-Host "[ERROR] $Message" -ForegroundColor Red +} + +# Function: Print success message +function Write-Success { + param([string]$Message) + Write-Host "[✓] $Message" -ForegroundColor Green +} + +# Function: Detect platform +function Get-Platform { + $arch = $env:PROCESSOR_ARCHITECTURE + + switch ($arch) { + "AMD64" { return "windows-x86_64" } + "ARM64" { return "windows-aarch64" } + default { + Write-Err "Unsupported architecture: $arch" + exit 1 + } + } +} + +# Function: Create directories +function New-DirectoryStructure { + param([string]$InstallDir, [string]$ConfigDir) + + Write-Info "Creating directories..." + + if (-not (Test-Path $InstallDir)) { + New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null + } + + if (-not (Test-Path $ConfigDir)) { + New-Item -ItemType Directory -Path $ConfigDir -Force | Out-Null + } + + Write-Success "Directories created" +} + +# Function: Download release +function Get-Release { + param([string]$Platform, [string]$Version) + + $DownloadUrl = "https://github.com/$GitHubRepo/releases/download/$Version/typedialog-$Platform.zip" + $TempFile = [System.IO.Path]::GetTempFileName() + + Write-Info "Downloading: $DownloadUrl" + + try { + Invoke-WebRequest -Uri $DownloadUrl -OutFile $TempFile -ErrorAction Stop + Write-Success "Download complete" + return $TempFile + } + catch { + Write-Err "Download failed: $_" + exit 1 + } +} + +# Function: Extract archive +function Expand-Release { + param([string]$Archive) + + $ExtractDir = New-Item -ItemType Directory -Path (Join-Path $env:TEMP "typedialog-extract") -Force | Select-Object -ExpandProperty FullName + + Write-Info "Extracting archive..." + + try { + Expand-Archive -Path $Archive -DestinationPath $ExtractDir -Force + Write-Success "Extraction complete" + return $ExtractDir + } + catch { + Write-Err "Extraction failed: $_" + exit 1 + } +} + +# Function: Install binaries +function Install-Binaries { + param([string]$ExtractDir, [string]$InstallDir) + + $Binaries = @("typedialog.exe", "typedialog-tui.exe", "typedialog-web.exe") + $BinDir = Join-Path $ExtractDir "bin" + + foreach ($binary in $Binaries) { + $BinaryPath = Join-Path $BinDir $binary + + if (Test-Path $BinaryPath) { + Copy-Item -Path $BinaryPath -Destination $InstallDir -Force + Write-Success "Installed: $binary" + } + } +} + +# Function: Install configurations +function Install-Configs { + param([string]$ExtractDir, [string]$ConfigDir) + + $ConfigSrc = Join-Path $ExtractDir "config" + + if (Test-Path $ConfigSrc) { + Copy-Item -Path "$ConfigSrc\*" -Destination $ConfigDir -Recurse -Force + Write-Success "Configurations installed" + } +} + +# Function: Update PATH +function Update-Path { + param([string]$InstallDir) + + $CurrentPath = [Environment]::GetEnvironmentVariable("PATH", "User") + + if ($CurrentPath -notlike "*$InstallDir*") { + $NewPath = "$CurrentPath;$InstallDir" + [Environment]::SetEnvironmentVariable("PATH", $NewPath, "User") + Write-Info "Updated PATH with $InstallDir" + } +} + +# Function: Verify installation +function Test-Installation { + $ExePath = Join-Path $env:USERPROFILE ".local\bin\typedialog.exe" + + if (Test-Path $ExePath) { + Write-Success "TypeDialog installed successfully" + return $true + } + else { + Write-Err "Installation verification failed" + return $false + } +} + +# Main entry point +function Main { + Write-Host "TypeDialog Installation" -ForegroundColor Yellow + Write-Host "=======================" -ForegroundColor Yellow + + $Platform = Get-Platform + Write-Info "Detected platform: $Platform" + + New-DirectoryStructure -InstallDir $InstallDir -ConfigDir $ConfigDir + + $Archive = Get-Release -Platform $Platform -Version $Version + $ExtractDir = Expand-Release -Archive $Archive + + Install-Binaries -ExtractDir $ExtractDir -InstallDir $InstallDir + Install-Configs -ExtractDir $ExtractDir -ConfigDir $ConfigDir + + Update-Path -InstallDir $InstallDir + + if (Test-Installation) { + Write-Success "Installation complete!" + Write-Info "Install directory: $InstallDir" + Write-Info "Config directory: $ConfigDir" + } + else { + exit 1 + } +} + +Main diff --git a/installers/bootstrap/install.sh b/installers/bootstrap/install.sh new file mode 100755 index 0000000..3211aab --- /dev/null +++ b/installers/bootstrap/install.sh @@ -0,0 +1,225 @@ +#!/bin/bash +set -euo pipefail + +# TypeDialog Installation Script +# Installs TypeDialog binaries and configuration + +INSTALL_DIR="${INSTALL_DIR:-$HOME/.local/bin}" +CONFIG_DIR="${CONFIG_DIR:-$HOME/.config/typedialog}" +GITHUB_REPO="anthropics/typedialog" +VERSION="${VERSION:-latest}" + +# Function: Print info message +log_info() { + echo "[INFO] $*" >&2 +} + +# Function: Print error message +log_error() { + echo "[ERROR] $*" >&2 +} + +# Function: Print success message +log_success() { + echo "[✓] $*" >&2 +} + +# Function: Detect OS and architecture +detect_platform() { + local os + local arch + + os=$(uname -s) + arch=$(uname -m) + + case "$os" in + Linux*) + os="linux" + ;; + Darwin*) + os="darwin" + ;; + *) + log_error "Unsupported OS: $os" + return 1 + ;; + esac + + case "$arch" in + x86_64) + arch="x86_64" + ;; + aarch64) + arch="aarch64" + ;; + arm64) + arch="aarch64" + ;; + *) + log_error "Unsupported architecture: $arch" + return 1 + ;; + esac + + echo "${os}-${arch}" +} + +# Function: Create directories +create_dirs() { + mkdir -p "$INSTALL_DIR" || { + log_error "Failed to create install directory: $INSTALL_DIR" + return 1 + } + + mkdir -p "$CONFIG_DIR" || { + log_error "Failed to create config directory: $CONFIG_DIR" + return 1 + } + + log_success "Directories created" +} + +# Function: Download release +download_release() { + local platform="$1" + local download_url="https://github.com/${GITHUB_REPO}/releases/download/${VERSION}/typedialog-${platform}.tar.gz" + local temp_file + temp_file=$(mktemp) || return 1 + trap "rm -f '$temp_file'" EXIT + + log_info "Downloading: $download_url" + + if command -v curl &>/dev/null; then + curl -fsSL "$download_url" -o "$temp_file" || { + log_error "Download failed" + return 1 + } + elif command -v wget &>/dev/null; then + wget -q "$download_url" -O "$temp_file" || { + log_error "Download failed" + return 1 + } + else + log_error "curl or wget required" + return 1 + fi + + log_success "Download complete" + echo "$temp_file" +} + +# Function: Extract archive +extract_archive() { + local archive="$1" + local extract_dir + extract_dir=$(mktemp -d) || return 1 + + log_info "Extracting archive..." + tar -xzf "$archive" -C "$extract_dir" || { + log_error "Extraction failed" + rm -rf "$extract_dir" + return 1 + } + + echo "$extract_dir" +} + +# Function: Install binaries +install_binaries() { + local extract_dir="$1" + + for binary in typedialog typedialog-tui typedialog-web; do + local binary_path="${extract_dir}/bin/${binary}" + + if [[ -f "$binary_path" ]]; then + cp "$binary_path" "${INSTALL_DIR}/${binary}" || { + log_error "Failed to install: $binary" + return 1 + } + chmod +x "${INSTALL_DIR}/${binary}" + log_success "Installed: $binary" + fi + done +} + +# Function: Install configurations +install_configs() { + local extract_dir="$1" + local config_src="${extract_dir}/config" + + if [[ -d "$config_src" ]]; then + cp -r "$config_src"/* "$CONFIG_DIR/" || { + log_error "Failed to install configurations" + return 1 + } + log_success "Configurations installed" + fi +} + +# Function: Update PATH if needed +update_path() { + local shell_rc="" + + if [[ "$SHELL" == *"bash"* ]]; then + shell_rc="$HOME/.bashrc" + elif [[ "$SHELL" == *"zsh"* ]]; then + shell_rc="$HOME/.zshrc" + else + return 0 + fi + + if [[ -f "$shell_rc" ]]; then + if ! grep -q "$INSTALL_DIR" "$shell_rc"; then + echo "export PATH=\"\$PATH:$INSTALL_DIR\"" >> "$shell_rc" + log_info "Updated $shell_rc with $INSTALL_DIR" + fi + fi +} + +# Function: Verify installation +verify_installation() { + log_info "Verifying installation..." + + if command -v typedialog &>/dev/null; then + local version + version=$(typedialog --version 2>/dev/null || echo "unknown") + log_success "TypeDialog installed: $version" + else + log_error "Installation verification failed" + return 1 + fi +} + +# Function: Main entry point +main() { + log_info "TypeDialog Installation" + log_info "========================" + + local platform + platform=$(detect_platform) || return 1 + log_info "Detected platform: $platform" + + create_dirs || return 1 + + local archive + archive=$(download_release "$platform") || return 1 + + local extract_dir + extract_dir=$(extract_archive "$archive") || return 1 + trap "rm -rf '$extract_dir'" EXIT + + install_binaries "$extract_dir" || return 1 + install_configs "$extract_dir" || return 1 + + update_path + + verify_installation || return 1 + + log_success "TypeDialog installation complete!" + log_info "Install directory: $INSTALL_DIR" + log_info "Config directory: $CONFIG_DIR" + + return 0 +} + +main "$@" diff --git a/scripts/build_all.sh b/scripts/build_all.sh new file mode 100755 index 0000000..4639bd7 --- /dev/null +++ b/scripts/build_all.sh @@ -0,0 +1,58 @@ +#!/bin/bash +set -euo pipefail + +# Description: Build all workspace targets for current platform +# Arguments: [profile] - debug or release (default: debug) +# Returns: 0 on success, 1 on failure +# Output: Build progress and results + +readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +readonly WORKSPACE_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" +readonly PROFILE="${1:-debug}" + +# Function: Print info message +log_info() { + echo "[INFO] $*" >&2 +} + +# Function: Print error message +log_error() { + echo "[ERROR] $*" >&2 +} + +# Function: Validate profile +validate_profile() { + local profile="$1" + [[ "$profile" == "debug" || "$profile" == "release" ]] || { + log_error "Invalid profile: $profile (must be debug or release)" + return 1 + } +} + +# Function: Build workspace +build_workspace() { + local profile="$1" + local cargo_args=("--workspace") + + if [[ "$profile" == "release" ]]; then + cargo_args+=("--release") + fi + + log_info "Building workspace ($profile profile)..." + cd "$WORKSPACE_ROOT" + cargo build "${cargo_args[@]}" +} + +# Function: Main entry point +main() { + log_info "TypeDialog Build All" + log_info "=====================" + + validate_profile "$PROFILE" || return 1 + build_workspace "$PROFILE" + + log_info "Build complete: $PROFILE profile" + return 0 +} + +main "$@" diff --git a/scripts/build_cross.sh b/scripts/build_cross.sh new file mode 100755 index 0000000..a37d645 --- /dev/null +++ b/scripts/build_cross.sh @@ -0,0 +1,94 @@ +#!/bin/bash +set -euo pipefail + +# Description: Cross-compile for multiple platforms using cargo-cross +# Arguments: [target] - specific target (default: all targets) +# Returns: 0 on success, 1 on failure +# Output: Cross-compilation progress + +readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +readonly WORKSPACE_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" +readonly TARGET="${1:-}" + +# Supported targets for cross-compilation +declare -a TARGETS=( + "x86_64-unknown-linux-gnu" + "x86_64-apple-darwin" + "aarch64-apple-darwin" + "x86_64-pc-windows-gnu" +) + +# Function: Print info message +log_info() { + echo "[INFO] $*" >&2 +} + +# Function: Print error message +log_error() { + echo "[ERROR] $*" >&2 +} + +# Function: Check for cross tool +check_cross() { + command -v cross >/dev/null || { + log_error "cross not found. Install: cargo install cross" + return 1 + } +} + +# Function: Validate target +is_valid_target() { + local target="$1" + for t in "${TARGETS[@]}"; do + [[ "$t" == "$target" ]] && return 0 + done + return 1 +} + +# Function: Build single target +build_target() { + local target="$1" + log_info "Building target: $target" + + cd "$WORKSPACE_ROOT" + cross build --target "$target" --release +} + +# Function: Build all targets +build_all_targets() { + local failed=0 + + for target in "${TARGETS[@]}"; do + if ! build_target "$target"; then + log_error "Failed to build: $target" + ((failed++)) + fi + done + + return $((failed > 0 ? 1 : 0)) +} + +# Function: Main entry point +main() { + log_info "TypeDialog Cross-Compilation" + log_info "==============================" + + check_cross || return 1 + + if [[ -z "$TARGET" ]]; then + log_info "Building all targets..." + build_all_targets + else + is_valid_target "$TARGET" || { + log_error "Invalid target: $TARGET" + log_error "Supported: ${TARGETS[*]}" + return 1 + } + build_target "$TARGET" + fi + + log_info "Cross-compilation complete" + return 0 +} + +main "$@" diff --git a/scripts/create_distro.sh b/scripts/create_distro.sh new file mode 100755 index 0000000..9584345 --- /dev/null +++ b/scripts/create_distro.sh @@ -0,0 +1,195 @@ +#!/bin/bash +set -euo pipefail + +# Description: Create distribution package with binaries and configs +# Arguments: [version] - version string (default: from Cargo.toml) +# Returns: 0 on success, 1 on failure +# Output: Distribution package path + +readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +readonly WORKSPACE_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" +readonly DISTRO_DIR="${WORKSPACE_ROOT}/distribution" +readonly BUILD_DIR="${WORKSPACE_ROOT}/target/release" + +# Extract version from Cargo.toml +get_version() { + grep '^version' "${WORKSPACE_ROOT}/crates/typedialog-core/Cargo.toml" | + head -1 | + sed 's/.*"\([^"]*\)".*/\1/' +} + +# Function: Print info message +log_info() { + echo "[INFO] $*" >&2 +} + +# Function: Print error message +log_error() { + echo "[ERROR] $*" >&2 +} + +# Function: Validate files exist +validate_files() { + local missing=0 + + for file in "$@"; do + if [[ ! -f "$file" ]]; then + log_error "Missing: $file" + ((missing++)) + fi + done + + return $((missing > 0 ? 1 : 0)) +} + +# Function: Create distribution directory +create_distro_structure() { + local version="$1" + local distro_path="${DISTRO_DIR}/typedialog-${version}" + + mkdir -p "${distro_path}"/{bin,config,docs,lib} + + echo "$distro_path" +} + +# Function: Copy binaries +copy_binaries() { + local distro_path="$1" + + if [[ -f "${BUILD_DIR}/typedialog" ]]; then + cp "${BUILD_DIR}/typedialog" "${distro_path}/bin/" + fi + + if [[ -f "${BUILD_DIR}/typedialog-tui" ]]; then + cp "${BUILD_DIR}/typedialog-tui" "${distro_path}/bin/" + fi + + if [[ -f "${BUILD_DIR}/typedialog-web" ]]; then + cp "${BUILD_DIR}/typedialog-web" "${distro_path}/bin/" + fi +} + +# Function: Copy configurations +copy_configs() { + local distro_path="$1" + + cp -r "${WORKSPACE_ROOT}/config"/* "${distro_path}/config/" || { + log_error "Failed to copy configurations" + return 1 + } +} + +# Function: Copy installers +copy_installers() { + local distro_path="$1" + + mkdir -p "${distro_path}/installers" + + if [[ -f "${WORKSPACE_ROOT}/installers/bootstrap/install.sh" ]]; then + cp "${WORKSPACE_ROOT}/installers/bootstrap/install.sh" "${distro_path}/installers/" + chmod +x "${distro_path}/installers/install.sh" + fi + + if [[ -f "${WORKSPACE_ROOT}/installers/bootstrap/install.ps1" ]]; then + cp "${WORKSPACE_ROOT}/installers/bootstrap/install.ps1" "${distro_path}/installers/" + fi + + if [[ -f "${WORKSPACE_ROOT}/installers/bootstrap/README.md" ]]; then + cp "${WORKSPACE_ROOT}/installers/bootstrap/README.md" "${distro_path}/installers/" + fi +} + +# Function: Create manifest +create_manifest() { + local distro_path="$1" + local version="$2" + local manifest="${distro_path}/MANIFEST.json" + + cat > "$manifest" <&2 +} + +# Function: Print error message +log_error() { + echo "[ERROR] $*" >&2 +} + +# Function: Print success message +log_success() { + echo "[✓] $*" >&2 +} + +# Function: Create release directory +create_release_dir() { + mkdir -p "$RELEASE_DIR" +} + +# Function: Copy distribution packages +copy_packages() { + local version="$1" + local distro_tar="${DISTRO_DIR}/typedialog-${version}.tar.gz" + + if [[ ! -f "$distro_tar" ]]; then + log_error "Distribution package not found: $distro_tar" + return 1 + fi + + cp "$distro_tar" "$RELEASE_DIR/" + log_success "Copied distribution package" +} + +# Function: Generate checksums +generate_checksums() { + local version="$1" + local checksum_file="${RELEASE_DIR}/SHA256SUMS" + + log_info "Generating checksums..." + + cd "$RELEASE_DIR" + sha256sum "typedialog-${version}.tar.gz" > "$checksum_file" + + log_success "Checksums generated: $checksum_file" +} + +# Function: Create release notes +create_release_notes() { + local version="$1" + local notes_file="${RELEASE_DIR}/RELEASE_NOTES.md" + + cat > "$notes_file" < 0); + + // Verify fields have groups + let server_hostname = form.fields.iter().find(|f| f.name == "server_hostname").unwrap(); + assert_eq!(server_hostname.group, Some("server".to_string())); + + // Create results + let mut results = HashMap::new(); + results.insert("server_hostname".to_string(), json!("api.example.com")); + results.insert("server_port".to_string(), json!(9000)); + results.insert("database_host".to_string(), json!("db.example.com")); + + // Serialize and verify nested structure is restored + let nickel_output = NickelSerializer::serialize(&results, &schema) + .expect("Serialization failed"); + + assert!(nickel_output.contains("server")); + assert!(nickel_output.contains("database")); + assert!(nickel_output.contains("api.example.com")); + assert!(nickel_output.contains("db.example.com")); + assert!(nickel_output.contains("9000")); +} + +#[test] +fn test_array_field_serialization() { + let schema = NickelSchemaIR { + name: "array_config".to_string(), + description: None, + fields: vec![ + NickelFieldIR { + path: vec!["tags".to_string()], + flat_name: "tags".to_string(), + nickel_type: NickelType::Array(Box::new(NickelType::String)), + doc: Some("Configuration tags".to_string()), + default: None, + optional: false, + contract: None, + group: None, + }, + ], + }; + + let mut results = HashMap::new(); + results.insert("tags".to_string(), json!(["prod", "critical", "network"])); + + let nickel_output = NickelSerializer::serialize(&results, &schema) + .expect("Serialization failed"); + + assert!(nickel_output.contains("prod")); + assert!(nickel_output.contains("critical")); + assert!(nickel_output.contains("network")); + assert!(nickel_output.contains("[")); + assert!(nickel_output.contains("]")); +} + +#[test] +fn test_contract_validation_non_empty_string() { + // Valid non-empty string + let result = ContractValidator::validate( + &json!("hello"), + "String | std.string.NonEmpty", + ); + assert!(result.is_ok()); + + // Empty string should fail + let result = ContractValidator::validate( + &json!(""), + "String | std.string.NonEmpty", + ); + assert!(result.is_err()); +} + +#[test] +fn test_contract_validation_number_range() { + // Valid number in range + let result = ContractValidator::validate( + &json!(50), + "Number | std.number.between 0 100", + ); + assert!(result.is_ok()); + + // Number out of range + let result = ContractValidator::validate( + &json!(150), + "Number | std.number.between 0 100", + ); + assert!(result.is_err()); +} + +#[test] +fn test_contract_validation_string_length() { + // Valid length + let result = ContractValidator::validate( + &json!("hello"), + "String | std.string.length.min 3", + ); + assert!(result.is_ok()); + + // Too short + let result = ContractValidator::validate( + &json!("hi"), + "String | std.string.length.min 3", + ); + assert!(result.is_err()); + + // Valid max length + let result = ContractValidator::validate( + &json!("hi"), + "String | std.string.length.max 5", + ); + assert!(result.is_ok()); + + // Too long + let result = ContractValidator::validate( + &json!("hello world"), + "String | std.string.length.max 5", + ); + assert!(result.is_err()); +} + +#[test] +fn test_form_definition_from_schema_ir() { + // Create schema + let schema = NickelSchemaIR { + name: "test_form".to_string(), + description: Some("Test form".to_string()), + fields: vec![ + NickelFieldIR { + path: vec!["name".to_string()], + flat_name: "name".to_string(), + nickel_type: NickelType::String, + doc: Some("Your name".to_string()), + default: None, + optional: false, + contract: Some("String | std.string.NonEmpty".to_string()), + group: None, + }, + ], + }; + + // Generate form + let form = TomlGenerator::generate(&schema, false, false) + .expect("Form generation failed"); + + // Convert to TOML + let toml_str = toml::to_string_pretty(&form) + .expect("TOML serialization failed"); + + // Parse back + let parsed_form: form_parser::FormDefinition = toml::from_str(&toml_str) + .expect("TOML parsing failed"); + + // Verify round-trip + assert_eq!(parsed_form.name, "test_form"); + assert_eq!(parsed_form.fields.len(), 1); + assert_eq!(parsed_form.fields[0].name, "name"); + assert_eq!( + parsed_form.fields[0].nickel_contract, + Some("String | std.string.NonEmpty".to_string()) + ); +} + +#[test] +fn test_metadata_extraction_with_optional_fields() { + // Parse JSON metadata (simulating nickel query output) + let metadata = json!({ + "name": { + "doc": "User full name", + "type": "String", + "default": "Anonymous" + }, + "age": { + "doc": "User age in years", + "type": "Number", + "optional": true + }, + "active": { + "type": "Bool", + "optional": false, + "default": true + } + }); + + let schema = MetadataParser::parse(metadata) + .expect("Metadata parsing failed"); + + // Verify fields + assert_eq!(schema.fields.len(), 3); + + // Check optional flags + let name_field = schema.fields.iter().find(|f| f.flat_name == "name").unwrap(); + assert!(!name_field.optional); + + let age_field = schema.fields.iter().find(|f| f.flat_name == "age").unwrap(); + assert!(age_field.optional); + + let active_field = schema.fields.iter().find(|f| f.flat_name == "active").unwrap(); + assert!(!active_field.optional); +} + +#[test] +fn test_type_mapping_all_types() { + let schema = NickelSchemaIR { + name: "types_test".to_string(), + description: None, + fields: vec![ + NickelFieldIR { + path: vec!["str_field".to_string()], + flat_name: "str_field".to_string(), + nickel_type: NickelType::String, + doc: None, + default: None, + optional: false, + contract: None, + group: None, + }, + NickelFieldIR { + path: vec!["num_field".to_string()], + flat_name: "num_field".to_string(), + nickel_type: NickelType::Number, + doc: None, + default: None, + optional: false, + contract: None, + group: None, + }, + NickelFieldIR { + path: vec!["bool_field".to_string()], + flat_name: "bool_field".to_string(), + nickel_type: NickelType::Bool, + doc: None, + default: None, + optional: false, + contract: None, + group: None, + }, + NickelFieldIR { + path: vec!["array_field".to_string()], + flat_name: "array_field".to_string(), + nickel_type: NickelType::Array(Box::new(NickelType::String)), + doc: None, + default: None, + optional: false, + contract: None, + group: None, + }, + ], + }; + + let form = TomlGenerator::generate(&schema, false, false) + .expect("Form generation failed"); + + // Verify type mapping + assert_eq!(form.fields[0].field_type, form_parser::FieldType::Text); + assert_eq!(form.fields[1].field_type, form_parser::FieldType::Custom); + assert_eq!(form.fields[1].custom_type, Some("f64".to_string())); + assert_eq!(form.fields[2].field_type, form_parser::FieldType::Confirm); + assert_eq!(form.fields[3].field_type, form_parser::FieldType::Editor); +} + +#[test] +fn test_enum_options_extraction_from_doc() { + let field = NickelFieldIR { + path: vec!["status".to_string()], + flat_name: "status".to_string(), + nickel_type: NickelType::Array(Box::new(NickelType::String)), + doc: Some("Status selection. Options: pending, active, completed".to_string()), + default: None, + optional: false, + contract: None, + group: None, + }; + + let schema = NickelSchemaIR { + name: "test".to_string(), + description: None, + fields: vec![field], + }; + + let form = TomlGenerator::generate(&schema, false, false) + .expect("Form generation failed"); + + // Verify options extracted + let status_field = &form.fields[0]; + assert_eq!( + status_field.options, + Some(vec![ + "pending".to_string(), + "active".to_string(), + "completed".to_string(), + ]) + ); +} + +#[test] +fn test_template_rendering_simple() { + #[cfg(feature = "templates")] + { + let mut engine = TemplateEngine::new(); + let mut values = HashMap::new(); + values.insert("hostname".to_string(), json!("server1")); + values.insert("port".to_string(), json!(8080)); + + let template = r#" +server { + hostname : String = "{{ hostname }}" + port : Number = {{ port }} +} +"#; + + let result = engine.render_str(template, &values); + assert!(result.is_ok()); + + let output = result.unwrap(); + assert!(output.contains("server1")); + assert!(output.contains("8080")); + } +} + +#[test] +fn test_template_rendering_with_loop() { + #[cfg(feature = "templates")] + { + let mut engine = TemplateEngine::new(); + let mut values = HashMap::new(); + values.insert("servers".to_string(), json!([ + {"name": "web-01", "ip": "192.168.1.10"}, + {"name": "web-02", "ip": "192.168.1.11"}, + ])); + + let template = r#"servers = [ +{% for server in servers %} + { name = "{{ server.name }}", ip = "{{ server.ip }}" }, +{% endfor %} +]"#; + + let result = engine.render_str(template, &values); + assert!(result.is_ok()); + + let output = result.unwrap(); + assert!(output.contains("web-01")); + assert!(output.contains("web-02")); + assert!(output.contains("192.168.1.10")); + } +} + +#[test] +fn test_template_rendering_with_conditional() { + #[cfg(feature = "templates")] + { + let mut engine = TemplateEngine::new(); + let mut values = HashMap::new(); + values.insert("production".to_string(), json!(true)); + values.insert("replicas".to_string(), json!(3)); + + let template = r#"{% if production %} +replicas : Number = {{ replicas }} +{% else %} +replicas : Number = 1 +{% endif %}"#; + + let result = engine.render_str(template, &values); + assert!(result.is_ok()); + + let output = result.unwrap(); + assert!(output.contains("replicas")); + assert!(output.contains("3")); + assert!(!output.contains("= 1")); + } +} + +#[test] +fn test_full_workflow_integration() { + // Create a realistic schema + let schema = NickelSchemaIR { + name: "app_config".to_string(), + description: Some("Application configuration".to_string()), + fields: vec![ + NickelFieldIR { + path: vec!["app".to_string(), "name".to_string()], + flat_name: "app_name".to_string(), + nickel_type: NickelType::String, + doc: Some("Application name".to_string()), + default: None, + optional: false, + contract: Some("String | std.string.NonEmpty".to_string()), + group: Some("app".to_string()), + }, + NickelFieldIR { + path: vec!["app".to_string(), "version".to_string()], + flat_name: "app_version".to_string(), + nickel_type: NickelType::String, + doc: Some("Application version (e.g., 1.0.0)".to_string()), + default: Some(json!("1.0.0")), + optional: false, + contract: None, + group: Some("app".to_string()), + }, + NickelFieldIR { + path: vec!["server".to_string(), "host".to_string()], + flat_name: "server_host".to_string(), + nickel_type: NickelType::String, + doc: Some("Server hostname".to_string()), + default: Some(json!("localhost")), + optional: false, + contract: None, + group: Some("server".to_string()), + }, + NickelFieldIR { + path: vec!["server".to_string(), "port".to_string()], + flat_name: "server_port".to_string(), + nickel_type: NickelType::Number, + doc: Some("Server port".to_string()), + default: Some(json!(8000)), + optional: false, + contract: Some("Number | std.number.between 1 65535".to_string()), + group: Some("server".to_string()), + }, + ], + }; + + // Step 1: Generate form + let form = TomlGenerator::generate(&schema, false, true) + .expect("Form generation failed"); + assert_eq!(form.fields.len(), 4); + + // Step 2: Serialize to TOML and parse back + let toml_str = toml::to_string_pretty(&form) + .expect("TOML serialization failed"); + let parsed_form: form_parser::FormDefinition = toml::from_str(&toml_str) + .expect("TOML parsing failed"); + assert_eq!(parsed_form.fields.len(), 4); + + // Step 3: Create form results + let mut results = HashMap::new(); + results.insert("app_name".to_string(), json!("MyApp")); + results.insert("app_version".to_string(), json!("2.0.0")); + results.insert("server_host".to_string(), json!("0.0.0.0")); + results.insert("server_port".to_string(), json!(3000)); + + // Step 4: Validate contracts + assert!(ContractValidator::validate(&json!("MyApp"), "String | std.string.NonEmpty").is_ok()); + assert!(ContractValidator::validate(&json!(3000), "Number | std.number.between 1 65535").is_ok()); + + // Step 5: Serialize to Nickel + let nickel_output = NickelSerializer::serialize(&results, &schema) + .expect("Serialization failed"); + + // Step 6: Verify output + assert!(nickel_output.contains("MyApp")); + assert!(nickel_output.contains("2.0.0")); + assert!(nickel_output.contains("0.0.0.0")); + assert!(nickel_output.contains("3000")); + assert!(nickel_output.contains("String | std.string.NonEmpty")); + assert!(nickel_output.contains("Number | std.number.between 1 65535")); + + // Verify nested structure + assert!(nickel_output.contains("app")); + assert!(nickel_output.contains("server")); +} + +#[test] +fn test_optional_fields_handling() { + let schema = NickelSchemaIR { + name: "optional_test".to_string(), + description: None, + fields: vec![ + NickelFieldIR { + path: vec!["required_field".to_string()], + flat_name: "required_field".to_string(), + nickel_type: NickelType::String, + doc: None, + default: None, + optional: false, + contract: None, + group: None, + }, + NickelFieldIR { + path: vec!["optional_field".to_string()], + flat_name: "optional_field".to_string(), + nickel_type: NickelType::String, + doc: None, + default: None, + optional: true, + contract: None, + group: None, + }, + ], + }; + + let form = TomlGenerator::generate(&schema, false, false) + .expect("Form generation failed"); + + // Check required field + let required = form.fields.iter().find(|f| f.name == "required_field").unwrap(); + assert_eq!(required.required, Some(true)); + + // Check optional field + let optional = form.fields.iter().find(|f| f.name == "optional_field").unwrap(); + assert_eq!(optional.required, Some(false)); +} + +#[test] +fn test_defaults_preservation() { + let schema = NickelSchemaIR { + name: "defaults_test".to_string(), + description: None, + fields: vec![ + NickelFieldIR { + path: vec!["string_with_default".to_string()], + flat_name: "string_with_default".to_string(), + nickel_type: NickelType::String, + doc: None, + default: Some(json!("default_value")), + optional: false, + contract: None, + group: None, + }, + NickelFieldIR { + path: vec!["number_with_default".to_string()], + flat_name: "number_with_default".to_string(), + nickel_type: NickelType::Number, + doc: None, + default: Some(json!(42)), + optional: false, + contract: None, + group: None, + }, + ], + }; + + let form = TomlGenerator::generate(&schema, false, false) + .expect("Form generation failed"); + + // Check defaults are preserved + let string_field = form.fields.iter().find(|f| f.name == "string_with_default").unwrap(); + assert_eq!(string_field.default, Some("default_value".to_string())); + + let number_field = form.fields.iter().find(|f| f.name == "number_with_default").unwrap(); + assert_eq!(number_field.default, Some("42".to_string())); +}