Skip to content

Commit d3cc025

Browse files
authored
feat: add support to known if env is default or not (#23)
1 parent 8db393b commit d3cc025

File tree

12 files changed

+142
-34
lines changed

12 files changed

+142
-34
lines changed

examples/apps/grpc_with_lifecycle/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ impl lifecycle::Lifecycle for Context {
7171
async fn on_start(&mut self, ctx: Arc<context::Context>) -> errors::Result<()> {
7272
println!("grpc on_start called");
7373
self.value = 42;
74-
self.greeter = Some(link_grpc_service!(ctx, GreeterClient, "greeter"));
74+
// self.greeter = Some(link_grpc_service!(ctx, GreeterClient, "greeter"));
7575
Ok(())
7676
}
7777

examples/apps/http_with_protobuf/src/api/router.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
// Code generated by protoc-gen-mikros-extensions. DO NOT EDIT.
22

3-
use axum::Json;
43
use axum::extract::Path;
54
use axum::extract::{Extension, State};
65
use axum::http::header::HeaderMap;
76
use axum::routing::{delete, get, post, put};
7+
use axum::Json;
88
use mikros::tonic::Request;
9-
use mikros::{Deserialize, Serialize};
109
use mikros::{axum, errors, http::ServiceState};
10+
use mikros::{Deserialize, Serialize};
1111
use std::sync::Arc;
1212

1313
use crate::card;
@@ -291,10 +291,9 @@ impl Router {
291291
pub fn routes(self) -> axum::Router<Arc<mikros::Mutex<ServiceState>>> {
292292
axum::Router::new()
293293
.route("/card/v1/cards", post(create_card))
294-
.route("/card/v1/cards/:id", get(get_card))
295-
.route("/card/v1/cards/:id", put(update_card))
296-
.route("/card/v1/cards/:id", delete(delete_card))
294+
.route("/card/v1/cards/{id}", get(get_card))
295+
.route("/card/v1/cards/{id}", put(update_card))
296+
.route("/card/v1/cards/{id}", delete(delete_card))
297297
.layer(Extension(self.clone()))
298298
}
299299
}
300-

examples/apps/native/service.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ types = ["native"]
66
envs = [ "CUSTOM_ENV" ]
77

88
[features.simple_api]
9-
enabled = false
9+
enabled = true
1010
collections = [ "name1", "name2" ]
1111

1212
[features.another_api]
1313
enabled = true
1414
use_tls = true
15-
host = "localhost"
15+
host = "localhost"

mikros-macros/Cargo.toml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,6 @@ categories.workspace = true
1515
proc-macro = true
1616

1717
[dependencies]
18-
proc-macro2 = "1.0.92"
18+
proc-macro2 = "1.0.93"
1919
quote = "1.0.38"
2020
syn = "2.0.96"
21-
22-
[dev-dependencies]
23-
mikros-tests = { workspace = true }

mikros-macros/src/env.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use syn::DeriveInput;
66

77
pub fn generate(input: DeriveInput) -> TokenStream {
88
let struct_name = input.ident.clone();
9-
let (field_initializers, attributes) = match parser::parse_fields(input) {
9+
let (field_initializers, default_checks, attributes) = match parser::parse_fields(input) {
1010
Ok(fields) => fields,
1111
Err(err) => panic!("{}", err),
1212
};
@@ -41,6 +41,12 @@ pub fn generate(input: DeriveInput) -> TokenStream {
4141

4242
std::env::var(key)
4343
}
44+
45+
pub fn check_defaults(&self) -> Vec<(&'static str, bool)> {
46+
vec![
47+
#(#default_checks),*
48+
]
49+
}
4450
}
4551
};
4652

mikros-macros/src/env/parser.rs

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ impl Default for StructAttributes {
7777
}
7878
}
7979

80-
pub(crate) fn parse_fields(input: DeriveInput) -> Result<(Vec<TokenStream>, StructAttributes), String> {
80+
pub(crate) fn parse_fields(input: DeriveInput) -> Result<(Vec<TokenStream>, Vec<TokenStream>, StructAttributes), String> {
8181
let struct_name = &input.ident;
8282

8383
// Parse struct level attributes
@@ -90,11 +90,16 @@ pub(crate) fn parse_fields(input: DeriveInput) -> Result<(Vec<TokenStream>, Stru
9090
};
9191

9292
let mut field_initializers: Vec<TokenStream> = Vec::new();
93+
let mut default_checks: Vec<TokenStream> = Vec::new();
94+
9395
for field in &fields {
94-
field_initializers.push(parse_field(field, struct_name)?);
96+
let (initializer, check) = parse_field(field, struct_name)?;
97+
98+
field_initializers.push(initializer);
99+
default_checks.push(check);
95100
}
96101

97-
Ok((field_initializers, attributes))
102+
Ok((field_initializers, default_checks, attributes))
98103
}
99104

100105
fn parse_struct_attributes(input: &DeriveInput) -> Result<StructAttributes, String> {
@@ -117,7 +122,7 @@ fn parse_struct_attributes(input: &DeriveInput) -> Result<StructAttributes, Stri
117122
Ok(attributes)
118123
}
119124

120-
fn parse_field(field: &Field, struct_name: &Ident) -> Result<TokenStream, String> {
125+
fn parse_field(field: &Field, struct_name: &Ident) -> Result<(TokenStream, TokenStream), String> {
121126
let Some(field_name) = field.ident.as_ref() else {
122127
return Err("expected a field name".to_string());
123128
};
@@ -129,9 +134,13 @@ fn parse_field(field: &Field, struct_name: &Ident) -> Result<TokenStream, String
129134
}
130135
}
131136

132-
Ok(attributes
137+
let default_check = generate_default_check(field_name, &attributes.default_value, &field.ty);
138+
let initializer = attributes
133139
.into_token_stream(field_name, struct_name, &field.ty)
134-
.into())
140+
.into();
141+
142+
143+
Ok((initializer, default_check))
135144
}
136145

137146
fn parse_attribute(attr: &Attribute, field_name: &Ident) -> Result<FieldAttributes, String> {
@@ -169,3 +178,30 @@ fn parse_attribute(attr: &Attribute, field_name: &Ident) -> Result<FieldAttribut
169178
default_value,
170179
})
171180
}
181+
182+
fn generate_default_check(field_name: &Ident, default_value: &Option<String>, field_type: &syn::Type) -> TokenStream {
183+
let is_option = match field_type {
184+
syn::Type::Path(type_path) if type_path.path.segments.len() == 1 => {
185+
type_path.path.segments[0].ident == "Option"
186+
}
187+
_ => false,
188+
};
189+
190+
let check = if let Some(default) = default_value {
191+
if is_option {
192+
if default == "None" {
193+
quote! { self.#field_name.is_none() }
194+
} else {
195+
quote! { self.#field_name.as_ref().map(|v| v.to_string()) == Some(#default.to_string()) }
196+
}
197+
} else {
198+
quote! { self.#field_name.to_string() == #default }
199+
}
200+
} else {
201+
quote! { false } // No default specified
202+
};
203+
204+
quote! {
205+
(stringify!(#field_name), #check)
206+
}
207+
}

mikros/src/definition.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
pub(crate) mod errors;
22
pub mod service;
33
mod validation;
4+
mod name;
45

56
use std::cmp::PartialEq;
67
use std::collections::HashMap;
@@ -11,6 +12,8 @@ use std::sync::Arc;
1112
use serde::de::DeserializeOwned;
1213
use validator::ValidateArgs;
1314

15+
use crate::definition::name::ServiceName;
16+
1417
// ServiceInfo represents the service information loaded from the 'service.toml'
1518
// file.
1619
#[derive(serde_derive::Deserialize, validator::Validate, Debug)]
@@ -21,7 +24,7 @@ use validator::ValidateArgs;
2124
use_context
2225
))]
2326
pub struct Definitions {
24-
pub name: String,
27+
pub name: ServiceName,
2528
pub version: String,
2629
pub language: String,
2730
pub product: String,

mikros/src/definition/name.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
use std::ops::Deref;
2+
3+
#[derive(Debug, Clone, serde_derive::Deserialize)]
4+
pub struct ServiceName(String);
5+
6+
impl ServiceName {
7+
pub fn new(name: &str) -> Self {
8+
Self(name.to_string())
9+
}
10+
11+
pub fn to_snake_case(&self) -> String {
12+
let mut result = String::new();
13+
let mut prev_char: Option<char> = None;
14+
15+
for (i, c) in self.0.char_indices() {
16+
if i > 0 && c.is_uppercase() {
17+
if let Some(prev) = prev_char {
18+
if prev != '_' && prev != '-' {
19+
result.push('_');
20+
}
21+
}
22+
}
23+
24+
if c == '-' {
25+
result.push('_');
26+
} else {
27+
result.push(c.to_ascii_lowercase());
28+
}
29+
30+
prev_char = Some(c);
31+
}
32+
33+
result
34+
}
35+
}
36+
37+
impl Deref for ServiceName {
38+
type Target = str;
39+
fn deref(&self) -> &Self::Target {
40+
&self.0
41+
}
42+
}

mikros/src/env.rs

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,13 @@ impl Env {
3737
let mut env = Env::from_env();
3838
env.defined_envs = Self::load_defined_envs(defs)?;
3939

40+
// Get the service name in snake_case to be compatible with environment
41+
// variables declaration.
42+
let name = defs.name.to_snake_case();
43+
4044
// Load the same values but using the service as suffix to override the
4145
// previous values.
42-
let svc_env = Env::from_env_with_suffix(&defs.name, false);
46+
let svc_env = Env::from_env_with_suffix(&name, false);
4347

4448
Ok(Arc::new(env.merge(svc_env)))
4549
}
@@ -80,23 +84,32 @@ impl Env {
8084
tracker_header_name: Self::string_other(&other.tracker_header_name, &self.tracker_header_name),
8185
coupled_namespace: Self::string_other(&other.coupled_namespace, &self.coupled_namespace),
8286
coupled_port: Self::string_other(&other.coupled_port, &self.coupled_port),
83-
grpc_port: other.grpc_port | self.grpc_port,
84-
http_port: other.http_port | self.http_port,
87+
grpc_port: Self::i32_other(other.grpc_port, self.grpc_port),
88+
http_port: Self::i32_other(other.http_port, self.http_port),
8589
hide_response_fields: other.hide_response_fields.or(self.hide_response_fields),
8690
defined_envs: self.defined_envs,
8791
}
8892
}
8993

9094
fn string_other(a: &str, b: &str) -> String {
91-
println!("a:{a}, b:{b}");
92-
if !a.is_empty() {
93-
return a.to_string()
94-
}
95+
if !a.is_empty() { a.to_string() } else { b.to_string() }
96+
}
9597

96-
b.to_string()
98+
fn i32_other(a: i32, b: i32) -> i32 {
99+
if a != 0 { a } else { b }
97100
}
98101
}
99102

103+
#[macro_export]
104+
macro_rules! env_is_default {
105+
($struct:expr, $field:ident) => {{
106+
let defaults = $struct.check_defaults();
107+
defaults.iter().find(|&&(name, _)| name == stringify!($field))
108+
.map(|&(_, is_default)| is_default)
109+
.unwrap_or(false)
110+
}};
111+
}
112+
100113
#[cfg(test)]
101114
mod tests {
102115
use super::*;

mikros/src/service/context.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ impl Context {
6565
/// Returns the current service name.
6666
#[must_use]
6767
pub fn service_name(&self) -> String {
68-
self.definitions.name.clone()
68+
self.definitions.name.to_string()
6969
}
7070

7171
/// Returns the URL connection string for linking services.

mikros/src/service/grpc.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ use tonic::body::BoxBody;
1212
use tonic::server::NamedService;
1313
use tonic::transport::Server;
1414

15-
use crate::errors as merrors;
1615
use crate::grpc;
1716
use crate::service::context::Context;
1817
use crate::service::lifecycle::Lifecycle;
1918
use crate::{definition, env, plugin};
19+
use crate::{env_is_default, errors as merrors};
2020

2121
#[derive(Clone)]
2222
pub(crate) struct Grpc<S> {
@@ -110,7 +110,13 @@ where
110110
Ok(service_type) => {
111111
self.port = match service_type.1 {
112112
None => envs.grpc_port,
113-
Some(port) => port,
113+
Some(port) => {
114+
if !env_is_default!(envs, grpc_port) {
115+
envs.grpc_port
116+
} else {
117+
port
118+
}
119+
}
114120
}
115121
}
116122
}

mikros/src/service/http.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ use crate::http::ServiceState;
1515
use crate::plugin::service::ServiceExecutionMode;
1616
use crate::service::context::Context;
1717
use crate::service::lifecycle::Lifecycle;
18-
use crate::{definition, env, errors as merrors, plugin};
18+
use crate::{definition, env, env_is_default, errors as merrors, plugin};
1919

2020
#[derive(Clone)]
2121
pub(crate) struct Http {
@@ -141,7 +141,13 @@ impl plugin::service::Service for Http {
141141
Ok(service_type) => {
142142
self.port = match service_type.1 {
143143
None => envs.http_port,
144-
Some(port) => port,
144+
Some(port) => {
145+
if !env_is_default!(envs, http_port) {
146+
envs.http_port
147+
} else {
148+
port
149+
}
150+
}
145151
}
146152
}
147153
}

0 commit comments

Comments
 (0)