# ggsci-ggsql
[](https://github.com/nanxstats/ggsci-rs/actions/workflows/ci.yml)
[](https://docs.rs/ggsci-ggsql)
[](https://crates.io/crates/ggsci-ggsql)
Use [ggsci](https://crates.io/crates/ggsci) color palettes in
[ggsql](https://ggsql.org) visualizations.
This release supports ggsql 0.4.1 and requires Rust 1.86, matching ggsql's MSRV.
The adapter disables ggsql's default features: palette conversion and
parser integration do not enable DuckDB, SQLite, ADBC, ODBC, Parquet,
Vega-Lite, spatial, or built-in data features.
## Explicit array integration
ggsql 0.4.1 resolves named palette identifiers in its own scale code and does
not expose a stable third-party palette provider or registry API. Consequently,
this adapter does not register ggsci names globally. It resolves a palette with
ggsci and passes ggsql an explicit array of uppercase `#RRGGBB` strings.
`GgsqlPalette` is the central wrapper. It keeps the resolved colors together
with their source `ggsci::PaletteKind` until the caller selects a textual scale
kind or converts to ggsql's typed `OutputRange`:
```rust
use ggsci_ggsql::GgsqlPalette;
fn main() -> Result<(), ggsci_ggsql::Error> {
let palette = GgsqlPalette::from_spec("observable:observable10", 3)?;
assert_eq!(
palette.to_sql_array(),
"['#4269D0', '#EFB118', '#FF725C']"
);
Ok(())
}
```
## Typed output ranges
Use `to_output_range()`, `output_range()`, or either `From<GgsqlPalette>`
implementation when building a ggsql plot programmatically:
```rust
use ggsci_ggsql::GgsqlPalette;
use ggsql::plot::{scale::OutputRange, ArrayElement};
fn main() -> Result<(), ggsci_ggsql::Error> {
let palette = GgsqlPalette::from_spec("npg:nrc", 3)?;
let output: OutputRange = (&palette).into();
let OutputRange::Array(colors) = output else {
unreachable!("ggsci always emits an explicit array");
};
assert!(colors
.iter()
.all(|color| matches!(color, ArrayElement::String(_))));
Ok(())
}
```
`OutputRange::Array` contains only the color values and intentionally loses
scale-kind metadata. Keep `GgsqlPalette` until the scale kind has been selected
when that distinction matters.
## Textual scale clauses
`to_default_scale_clause()` derives the ggsql keyword from the source palette:
```rust
use ggsci_ggsql::GgsqlPalette;
fn main() -> Result<(), ggsci_ggsql::Error> {
let categories = GgsqlPalette::from_spec("npg:nrc", 3)?;
let gradient = GgsqlPalette::from_spec("material:blue-grey", 256)?;
assert!(categories
.to_default_scale_clause("color")?
.starts_with("SCALE DISCRETE color TO"));
assert!(gradient
.to_default_scale_clause("fill")?
.starts_with("SCALE CONTINUOUS fill TO"));
Ok(())
}
```
`ggsci::PaletteKind` describes the source palette's semantic domain.
`ggsci_ggsql::ScaleKind` describes the ggsql `SCALE` clause being emitted.
Their default mapping is discrete to `DISCRETE` and continuous to
`CONTINUOUS`, but the enums remain distinct so callers can make valid explicit
choices. For example, a sampled continuous palette can drive a binned scale,
and ordered category colors can drive an ordinal scale:
```rust
use ggsci_ggsql::{GgsqlPalette, ScaleKind};
fn main() -> Result<(), ggsci_ggsql::Error> {
let gradient = GgsqlPalette::from_spec("material:blue-grey", 12)?;
let clause = gradient.to_scale_clause(ScaleKind::Binned, "fill")?;
assert!(clause.starts_with("SCALE BINNED fill TO"));
Ok(())
}
```
Textual helpers validate aesthetics as trimmed ASCII identifiers matching
`[A-Za-z_][A-Za-z0-9_]*`. The original `color_array()` and
`scale_discrete()` compatibility helpers remain discrete-only and preserve
their `ggsci::Error` return type; continuous palettes return
`ggsci::Error::NotDiscretePalette`.
## Continuous palettes
`from_spec()` uses kind-aware `Palette::sample()`, so continuous records are
interpolated rather than truncated to their stored anchors. Use
`from_continuous()` when reverse behavior should be explicit:
```rust
use ggsci::ContinuousOptions;
use ggsci_ggsql::GgsqlPalette;
fn main() -> Result<(), ggsci_ggsql::Error> {
let palette = GgsqlPalette::from_continuous(
"material:blue-grey",
512,
ContinuousOptions::new().with_reverse(true),
)?;
assert_eq!(palette.colors().len(), 512);
Ok(())
}
```
The `scale_continuous()` convenience function performs the same explicit
continuous resolution and emits a `SCALE CONTINUOUS` clause.
## iTerm palettes
iTerm themes are fixed discrete palettes with normal and bright variants:
```rust
use ggsci::ItermVariant;
use ggsci_ggsql::GgsqlPalette;
fn main() -> Result<(), ggsci_ggsql::Error> {
let palette = GgsqlPalette::from_iterm("Rose Pine", ItermVariant::Normal, 6)?;
let clause = palette.to_default_scale_clause("stroke")?;
assert!(clause.starts_with("SCALE DISCRETE stroke TO"));
Ok(())
}
```
## Gephi palettes
Gephi palettes generate discrete category colors. Prefer seeded generation for
reproducible SQL and tests:
```rust
use ggsci_ggsql::GgsqlPalette;
fn main() -> Result<(), ggsci_ggsql::Error> {
let first = GgsqlPalette::from_gephi_with_seed("fancy-light", 20, 42)?;
let second = GgsqlPalette::from_gephi_with_seed("fancy-light", 20, 42)?;
assert_eq!(first, second);
Ok(())
}
```
`scale_iterm_discrete()` and `scale_gephi_discrete_with_seed()` provide direct
textual equivalents. Unseeded `from_gephi()` is available when fresh random
output is desired.
## Parser build note
The published `tree-sitter-ggsql` 0.4.1 build script regenerates its parser by
default. Set `GGSQL_SKIP_GENERATE=1` to use the generated `src/parser.c` that is
already included in its crate package when `tree-sitter-cli` is not installed.
The ggsci-rs workspace and CI use this upstream supported mode automatically;
downstream workspaces without `tree-sitter-cli` should set the same variable.
## Future provider API
There is currently no ggsql plugin, palette registry, or provider trait for
third-party named palettes. If ggsql defines an official provider API in a
future release, this adapter intends to adopt it while retaining explicit
array conversion for portable, self-contained queries.