1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-12-08 13:08:37 +03:00
Files
authentication-service/crates/iana-codegen/src/main.rs
Quentin Gliech a0f5f3c642 Enable clippy lints on a workspace level
This enables a lot more lints than before in some crates, so this fixed a lot of warnings as well.
2023-12-05 17:20:42 +01:00

218 lines
6.2 KiB
Rust

// Copyright 2022, 2023 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::{collections::HashMap, fmt::Display, sync::Arc};
use camino::{Utf8Path, Utf8PathBuf};
use tokio::io::AsyncWriteExt;
use tracing::Level;
type Client = hyper::Client<hyper::client::HttpConnector>;
mod gen;
pub mod jose;
pub mod oauth;
pub mod traits;
#[derive(Debug)]
struct File {
client: Arc<Client>,
registry_name: &'static str,
registry_url: &'static str,
sections: Vec<Section>,
items: HashMap<&'static str, Vec<EnumMember>>,
}
fn resolve_path(relative: impl AsRef<Utf8Path>) -> Utf8PathBuf {
let crate_root = Utf8Path::new(env!("CARGO_MANIFEST_DIR"));
let workspace_root = crate_root.parent().unwrap().parent().unwrap();
workspace_root.join(relative)
}
impl File {
#[tracing::instrument(skip(client))]
fn new(registry_name: &'static str, registry_url: &'static str, client: Arc<Client>) -> Self {
tracing::info!("Generating file from IANA registry");
Self {
client,
registry_name,
registry_url,
sections: Vec::new(),
items: HashMap::new(),
}
}
#[tracing::instrument(skip_all, fields(url))]
async fn load<T: EnumEntry>(mut self) -> anyhow::Result<Self> {
tracing::Span::current().record("url", T::URL);
self.sections.extend(T::sections());
for (key, value) in T::fetch(&self.client).await? {
self.items.entry(key).or_default().push(value);
}
Ok(self)
}
#[tracing::instrument(skip_all)]
async fn write(&self, path: impl AsRef<Utf8Path>) -> anyhow::Result<()> {
let mut file = tokio::fs::OpenOptions::new()
.create(true)
.truncate(true)
.write(true)
.open(path.as_ref())
.await?;
tracing::info!("Writing file");
file.write_all(format!("{self}").as_bytes()).await?;
Ok(())
}
}
impl Display for File {
#[allow(clippy::too_many_lines)]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(
f,
r#"// Copyright 2023 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Enums from the {:?} IANA registry
//! See <{}>
// Do not edit this file manually"#,
self.registry_name, self.registry_url,
)?;
for section in &self.sections {
let Some(list) = self.items.get(section.key) else {
continue;
};
let is_exhaustive = section.key == "OAuthAuthorizationEndpointResponseType";
writeln!(f)?;
self::gen::struct_def(f, section, list, is_exhaustive)?;
writeln!(f)?;
// Write the Display impl
self::gen::display_impl(f, section, list, is_exhaustive)?;
writeln!(f)?;
// Write the FromStr impl
self::gen::from_str_impl(f, section, list, is_exhaustive)?;
writeln!(f)?;
// Write the Serialize and Deserialize impls
self::gen::serde_impl(f, section)?;
writeln!(f)?;
// Write the JsonSchema impl
self::gen::json_schema_impl(f, section, list)?;
}
Ok(())
}
}
use self::traits::{EnumEntry, EnumMember, Section};
#[tracing::instrument(skip_all, fields(%path))]
async fn generate_jose(
client: &Arc<Client>,
path: impl AsRef<Utf8Path> + std::fmt::Display,
) -> anyhow::Result<()> {
let path = resolve_path(path);
let client = client.clone();
let file = File::new(
"JSON Object Signing and Encryption",
"https://www.iana.org/assignments/oauth-parameters/oauth-parameters.xhtml",
client.clone(),
)
.load::<jose::WebEncryptionSignatureAlgorithm>()
.await?
.load::<jose::WebEncryptionCompressionAlgorithm>()
.await?
.load::<jose::WebKeyType>()
.await?
.load::<jose::WebKeyEllipticCurve>()
.await?
.load::<jose::WebKeyUse>()
.await?
.load::<jose::WebKeyOperation>()
.await?;
file.write(path).await?;
Ok(())
}
#[tracing::instrument(skip_all, fields(%path))]
async fn generate_oauth(
client: &Arc<Client>,
path: impl AsRef<Utf8Path> + std::fmt::Display,
) -> anyhow::Result<()> {
let path = resolve_path(path);
let client = client.clone();
let file = File::new(
"OAuth Parameters",
"https://www.iana.org/assignments/jose/jose.xhtml",
client.clone(),
)
.load::<oauth::AccessTokenType>()
.await?
.load::<oauth::AuthorizationEndpointResponseType>()
.await?
.load::<oauth::TokenTypeHint>()
.await?
.load::<oauth::TokenEndpointAuthenticationMethod>()
.await?
.load::<oauth::PkceCodeChallengeMethod>()
.await?;
file.write(path).await?;
Ok(())
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
tracing_subscriber::fmt()
.with_max_level(Level::INFO)
.pretty()
.init();
let client = Client::new();
let client = Arc::new(client);
let iana_crate_root = Utf8Path::new("crates/iana/");
generate_jose(&client, iana_crate_root.join("src/jose.rs")).await?;
generate_oauth(&client, iana_crate_root.join("src/oauth.rs")).await?;
Ok(())
}