Skip to content

Commit 8c87893

Browse files
committed
Add utoipa support and refactor Language traits
- Add `utoipa` as an optional dependency and implement `ToSchema`. - Replace `FromStr` with `TryFrom<&str>` for `Language`. - Split `fmt` task in `Makefile.toml` into `fmt-rust` and `fmt-taplo`. - Bump version to 0.4.1.
1 parent 96bc37f commit 8c87893

6 files changed

Lines changed: 112 additions & 31 deletions

File tree

Cargo.lock

Lines changed: 27 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ name = "language"
1010
readme = "README.md"
1111
repository = "https://github.com/hack-ink/language"
1212
resolver = "3"
13-
version = "0.4.0"
13+
version = "0.4.1"
1414

1515
[package.metadata.docs.rs]
1616
all-features = true
@@ -54,6 +54,7 @@ scraper = { version = "0.25", optional = true }
5454
serde = { version = "1.0", optional = true, features = ["derive"] }
5555
sqlx = { version = "0.8", optional = true, default-features = false }
5656
thiserror = { version = "2.0" }
57+
utoipa = { version = "5", optional = true }
5758
whatlang = { version = "0.18", optional = true }
5859

5960
[dev-dependencies]

Makefile.toml

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,25 @@ args = [
2222

2323
[tasks.fmt]
2424
workspace = false
25-
command = "bash"
25+
dependencies = [
26+
"fmt-rust",
27+
"fmt-taplo",
28+
]
29+
30+
[tasks.fmt-rust]
31+
workspace = false
32+
command = "cargo"
33+
toolchain = "nightly"
2634
args = [
27-
"-lc",
28-
"taplo fmt && cargo +nightly fmt --all",
35+
"fmt",
36+
"--all",
2937
]
3038

39+
[tasks.fmt-taplo]
40+
workspace = false
41+
command = "taplo"
42+
args = ["fmt"]
43+
3144
[tasks.nextest]
3245
workspace = false
3346
command = "cargo"

build/codegen.rs

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ fn render(specs: &[TagSpec]) -> String {
200200
out.push_str(
201201
"\
202202
// std
203-
use std::{convert::TryFrom, str::FromStr};
203+
use std::convert::TryFrom;
204204
// self
205205
use crate::prelude::*;
206206
use Language::*;
@@ -302,10 +302,10 @@ impl Language {
302302
" }
303303
}
304304
}
305-
impl FromStr for Language {
306-
type Err = Error;
305+
impl TryFrom<&str> for Language {
306+
type Error = Error;
307307
308-
fn from_str(tag: &str) -> Result<Self, Self::Err> {
308+
fn try_from(tag: &str) -> Result<Self, Self::Error> {
309309
let this = match tag {
310310
",
311311
);
@@ -325,18 +325,11 @@ impl FromStr for Language {
325325
Ok(this)
326326
}
327327
}
328-
impl TryFrom<&str> for Language {
329-
type Error = Error;
330-
331-
fn try_from(value: &str) -> Result<Self, Self::Error> {
332-
Self::from_str(value)
333-
}
334-
}
335328
impl TryFrom<String> for Language {
336329
type Error = Error;
337330
338331
fn try_from(value: String) -> Result<Self, Self::Error> {
339-
Self::from_str(&value)
332+
Language::try_from(value.as_str())
340333
}
341334
}
342335
#[cfg(feature = \"serde\")]
@@ -356,9 +349,26 @@ impl<'de> serde::Deserialize<'de> for Language {
356349
{
357350
let tag = String::deserialize(deserializer)?;
358351
359-
Language::from_str(&tag).map_err(|_| serde::de::Error::unknown_variant(&tag, &[]))
352+
Language::try_from(tag.as_str()).map_err(|_| serde::de::Error::unknown_variant(&tag, &[]))
353+
}
354+
}
355+
#[cfg(feature = \"utoipa\")]
356+
impl utoipa::PartialSchema for Language {
357+
fn schema() -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
358+
let enum_values =
359+
Language::all().iter().map(|language| language.tag().to_string()).collect::<Vec<_>>();
360+
let object = utoipa::openapi::schema::ObjectBuilder::new()
361+
.schema_type(utoipa::openapi::schema::SchemaType::Type(
362+
utoipa::openapi::schema::Type::String,
363+
))
364+
.enum_values(Some(enum_values))
365+
.build();
366+
367+
utoipa::openapi::RefOr::T(utoipa::openapi::schema::Schema::Object(object))
360368
}
361369
}
370+
#[cfg(feature = \"utoipa\")]
371+
impl utoipa::ToSchema for Language {}
362372
",
363373
);
364374

src/generated.rs

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// std
2-
use std::{convert::TryFrom, str::FromStr};
2+
use std::convert::TryFrom;
33
// self
44
use crate::prelude::*;
55
use Language::*;
@@ -1492,10 +1492,10 @@ impl Language {
14921492
}
14931493
}
14941494
}
1495-
impl FromStr for Language {
1496-
type Err = Error;
1495+
impl TryFrom<&str> for Language {
1496+
type Error = Error;
14971497

1498-
fn from_str(tag: &str) -> Result<Self, Self::Err> {
1498+
fn try_from(tag: &str) -> Result<Self, Self::Error> {
14991499
let this = match tag {
15001500
"af" => Af,
15011501
"ak" => Ak,
@@ -1746,18 +1746,11 @@ impl FromStr for Language {
17461746
Ok(this)
17471747
}
17481748
}
1749-
impl TryFrom<&str> for Language {
1750-
type Error = Error;
1751-
1752-
fn try_from(value: &str) -> Result<Self, Self::Error> {
1753-
Self::from_str(value)
1754-
}
1755-
}
17561749
impl TryFrom<String> for Language {
17571750
type Error = Error;
17581751

17591752
fn try_from(value: String) -> Result<Self, Self::Error> {
1760-
Self::from_str(&value)
1753+
Language::try_from(value.as_str())
17611754
}
17621755
}
17631756
#[cfg(feature = "serde")]
@@ -1777,6 +1770,23 @@ impl<'de> serde::Deserialize<'de> for Language {
17771770
{
17781771
let tag = String::deserialize(deserializer)?;
17791772

1780-
Language::from_str(&tag).map_err(|_| serde::de::Error::unknown_variant(&tag, &[]))
1773+
Language::try_from(tag.as_str()).map_err(|_| serde::de::Error::unknown_variant(&tag, &[]))
1774+
}
1775+
}
1776+
#[cfg(feature = "utoipa")]
1777+
impl utoipa::PartialSchema for Language {
1778+
fn schema() -> utoipa::openapi::RefOr<utoipa::openapi::schema::Schema> {
1779+
let enum_values =
1780+
Language::all().iter().map(|language| language.tag().to_string()).collect::<Vec<_>>();
1781+
let object = utoipa::openapi::schema::ObjectBuilder::new()
1782+
.schema_type(utoipa::openapi::schema::SchemaType::Type(
1783+
utoipa::openapi::schema::Type::String,
1784+
))
1785+
.enum_values(Some(enum_values))
1786+
.build();
1787+
1788+
utoipa::openapi::RefOr::T(utoipa::openapi::schema::Schema::Object(object))
17811789
}
17821790
}
1791+
#[cfg(feature = "utoipa")]
1792+
impl utoipa::ToSchema for Language {}

tests/utoipa.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#![cfg(feature = "utoipa")]
2+
3+
// crates.io
4+
use utoipa::PartialSchema;
5+
// self
6+
use language::prelude::*;
7+
8+
#[test]
9+
fn utoipa_schema_should_include_language_tags() {
10+
let schema = Language::schema();
11+
let value = serde_json::to_value(schema).unwrap();
12+
let enum_values = value
13+
.get("enum")
14+
.and_then(|values| values.as_array())
15+
.expect("Language schema should include enum values.");
16+
17+
let has_tag = |tag: &str| enum_values.iter().any(|value| value.as_str() == Some(tag));
18+
19+
assert!(has_tag("en"), "Language schema should include the 'en' tag.");
20+
assert!(has_tag("zh-Hans"), "Language schema should include the 'zh-Hans' tag.");
21+
}

0 commit comments

Comments
 (0)