Skip to content

Commit 9c7d41b

Browse files
committed
Localize strings in dscbicep
1 parent 612615f commit 9c7d41b

4 files changed

Lines changed: 138 additions & 45 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ registry = { version = "1.3" }
178178
rmcp = { version = "0.12" }
179179
# dsc_lib
180180
rt-format = { version = "0.3" }
181-
# dsc, dsc-lib, dscecho, registry, dsc-lib-registry, runcommandonset, sshdconfig
181+
# dsc, dsc-lib, dscbicep, dscecho, registry, dsc-lib-registry, runcommandonset, sshdconfig
182182
rust-i18n = { version = "3.1" }
183183
# dsc, dsc-lib, dscecho, registry, dsc-lib-registry, sshdconfig, dsctest, test_group_resource
184184
schemars = { version = "1.2", features = ["preserve_order"] }

dscbicep/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ dsc-lib = { workspace = true }
1313
async-stream = { workspace = true }
1414
clap = { workspace = true }
1515
prost = { workspace = true }
16+
rust-i18n = { workspace = true }
1617
tokio = { workspace = true, features = ["rt-multi-thread", "macros", "signal", "io-std", "net"] }
1718
tokio-stream = { workspace = true, features = ["net"] }
1819
tonic = { workspace = true }

dscbicep/locales/en-us.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
_version = 1
2+
3+
[dscbicep]
4+
functionCalled = "%{function} called for %{resourceType}@%{version}: %{properties}"
5+
starting = "Starting Bicep gRPC server on %{transport}: %{address}"
6+
waitForDebugger = "Press any key to continue after attaching to PID: {pid}"
7+
serverError = "Server error: %{error}"

dscbicep/src/main.rs

Lines changed: 129 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ use dsc_lib::{
1111
},
1212
DscManager,
1313
};
14+
use rust_i18n::{i18n, t};
1415
use std::{env, io, process};
1516
use tonic::{transport::Server, Request, Response, Status};
1617

@@ -26,6 +27,8 @@ use proto::{
2627
TypeFilesResponse,
2728
};
2829

30+
i18n!("locales", fallback = "en-us");
31+
2932
#[derive(Debug, Default)]
3033
pub struct BicepExtensionService;
3134

@@ -40,18 +43,38 @@ impl BicepExtension for BicepExtensionService {
4043
let version = spec.api_version;
4144
let properties = spec.properties;
4245

43-
tracing::debug!("CreateOrUpdate called for {resource_type}@{version:?}: {properties}");
46+
tracing::debug!(
47+
"{}",
48+
t!(
49+
"dscbicep.functionCalled",
50+
function = "CreateOrUpdate",
51+
resourceType = resource_type,
52+
version = format!("{version:?}"),
53+
properties = properties
54+
)
55+
);
4456

4557
let mut dsc = DscManager::new();
46-
let Some(resource) = dsc.find_resource(&DiscoveryFilter::new(&resource_type, version.as_deref(), None)).unwrap_or(None) else {
47-
return Err(Status::not_found("Resource not found"));
58+
let Some(resource) = dsc
59+
.find_resource(&DiscoveryFilter::new(
60+
&resource_type,
61+
version.as_deref(),
62+
None,
63+
))
64+
.unwrap_or(None)
65+
else {
66+
return Err(Status::not_found(
67+
t!("dscerror.resourceNotFound").to_string(),
68+
));
4869
};
4970

5071
let SetResult::Resource(result) = resource
5172
.set(&properties, false, &ExecutionKind::Actual)
5273
.map_err(|e| Status::aborted(e.to_string()))?
5374
else {
54-
return Err(Status::unimplemented("Group resources not supported"));
75+
return Err(Status::unimplemented(
76+
t!("dscerror.notSupported").to_string(),
77+
));
5578
};
5679

5780
Ok(Response::new(LocalExtensibilityOperationResponse {
@@ -75,18 +98,38 @@ impl BicepExtension for BicepExtensionService {
7598
let version = spec.api_version;
7699
let properties = spec.properties;
77100

78-
tracing::debug!("Preview called for {resource_type}@{version:?}: {properties}");
101+
tracing::debug!(
102+
"{}",
103+
t!(
104+
"dscbicep.functionCalled",
105+
function = "Preview",
106+
resourceType = resource_type,
107+
version = format!("{version:?}"),
108+
properties = properties
109+
)
110+
);
79111

80112
let mut dsc = DscManager::new();
81-
let Some(resource) = dsc.find_resource(&DiscoveryFilter::new(&resource_type, version.as_deref(), None)).unwrap_or(None) else {
82-
return Err(Status::not_found("Resource not found"));
113+
let Some(resource) = dsc
114+
.find_resource(&DiscoveryFilter::new(
115+
&resource_type,
116+
version.as_deref(),
117+
None,
118+
))
119+
.unwrap_or(None)
120+
else {
121+
return Err(Status::not_found(
122+
t!("dscerror.resourceNotFound").to_string(),
123+
));
83124
};
84125

85126
let SetResult::Resource(result) = resource
86127
.set(&properties, false, &ExecutionKind::WhatIf)
87128
.map_err(|e| Status::aborted(e.to_string()))?
88129
else {
89-
return Err(Status::unimplemented("Group resources not supported"));
130+
return Err(Status::unimplemented(
131+
t!("dscerror.notSupported").to_string(),
132+
));
90133
};
91134

92135
Ok(Response::new(LocalExtensibilityOperationResponse {
@@ -110,18 +153,38 @@ impl BicepExtension for BicepExtensionService {
110153
let version = reference.api_version.clone();
111154
let identifiers = reference.identifiers.clone();
112155

113-
tracing::debug!("Get called for {resource_type}@{version:?}: {identifiers}");
156+
tracing::debug!(
157+
"{}",
158+
t!(
159+
"dscbicep.functionCalled",
160+
function = "Get",
161+
resourceType = resource_type,
162+
version = format!("{version:?}"),
163+
properties = identifiers
164+
)
165+
);
114166

115167
let mut dsc = DscManager::new();
116-
let Some(resource) = dsc.find_resource(&DiscoveryFilter::new(&resource_type, version.as_deref(), None)).unwrap_or(None) else {
117-
return Err(Status::not_found("Resource not found"));
168+
let Some(resource) = dsc
169+
.find_resource(&DiscoveryFilter::new(
170+
&resource_type,
171+
version.as_deref(),
172+
None,
173+
))
174+
.unwrap_or(None)
175+
else {
176+
return Err(Status::not_found(
177+
t!("dscerror.resourceNotFound").to_string(),
178+
));
118179
};
119180

120181
let GetResult::Resource(result) = resource
121182
.get(&identifiers)
122183
.map_err(|e| Status::aborted(e.to_string()))?
123184
else {
124-
return Err(Status::unimplemented("Group resources not supported"));
185+
return Err(Status::unimplemented(
186+
t!("dscerror.notSupported").to_string(),
187+
));
125188
};
126189

127190
Ok(Response::new(LocalExtensibilityOperationResponse {
@@ -146,15 +209,28 @@ impl BicepExtension for BicepExtensionService {
146209
let identifiers = reference.identifiers.clone();
147210

148211
tracing::debug!(
149-
"Delete called for {}@{:?}: {}",
150-
resource_type,
151-
version,
152-
identifiers
212+
"{}",
213+
t!(
214+
"dscbicep.functionCalled",
215+
function = "Delete",
216+
resourceType = resource_type,
217+
version = format!("{version:?}"),
218+
properties = identifiers
219+
)
153220
);
154221

155222
let mut dsc = DscManager::new();
156-
let Some(resource) = dsc.find_resource(&DiscoveryFilter::new(&resource_type, version.as_deref(), None)).unwrap_or(None) else {
157-
return Err(Status::not_found("Resource not found"));
223+
let Some(resource) = dsc
224+
.find_resource(&DiscoveryFilter::new(
225+
&resource_type,
226+
version.as_deref(),
227+
None,
228+
))
229+
.unwrap_or(None)
230+
else {
231+
return Err(Status::not_found(
232+
t!("dscerror.resourceNotFound").to_string(),
233+
));
158234
};
159235

160236
resource
@@ -177,15 +253,13 @@ impl BicepExtension for BicepExtensionService {
177253
&self,
178254
_request: Request<Empty>,
179255
) -> Result<Response<TypeFilesResponse>, Status> {
180-
tracing::debug!("GetTypeFiles called");
181-
182-
// TODO: Return actual Bicep type definitions...yet the extension already has these?
183-
// Perhaps this is where we can dynamically get them from the current system.
184-
Err(Status::unimplemented("GetTypeFiles not yet implemented"))
256+
// TODO: Dynamically return type definitions for DSC resources found on the system.
257+
Err(Status::unimplemented(
258+
t!("dscerror.notImplemented").to_string(),
259+
))
185260
}
186261

187262
async fn ping(&self, _request: Request<Empty>) -> Result<Response<Empty>, Status> {
188-
tracing::debug!("Ping called");
189263
Ok(Response::new(Empty {}))
190264
}
191265
}
@@ -224,7 +298,14 @@ async fn run_server(
224298
use tokio::net::UnixListener;
225299
use tokio_stream::wrappers::UnixListenerStream;
226300

227-
tracing::info!("Starting Bicep gRPC server on Unix socket: {socket_path}");
301+
tracing::info!(
302+
"{}",
303+
t!(
304+
"dscbicep.starting",
305+
transport = "socket",
306+
address = socket_path
307+
)
308+
);
228309

229310
// Remove the socket file if it exists
230311
let _ = std::fs::remove_file(&socket_path);
@@ -298,7 +379,14 @@ async fn run_server(
298379

299380
// Windows named pipes must be in the format \\.\pipe\{name}
300381
let full_pipe_path = format!(r"\\.\pipe\{}", pipe_name);
301-
tracing::info!("Starting Bicep gRPC server on named pipe: {full_pipe_path}");
382+
tracing::info!(
383+
"{}",
384+
t!(
385+
"dscbicep.starting",
386+
transport = "named pipe",
387+
address = full_pipe_path
388+
)
389+
);
302390

303391
// Create a stream that accepts connections on the named pipe
304392
let incoming = async_stream::stream! {
@@ -318,21 +406,19 @@ async fn run_server(
318406
let server = match pipe {
319407
Ok(server) => server,
320408
Err(e) => {
321-
tracing::error!("Failed to create named pipe: {}", e);
409+
tracing::error!("{}", t!("dscbicep.serverError", error = e.to_string()));
322410
break;
323411
}
324412
};
325413

326414
is_first = false;
327415

328-
tracing::debug!("Waiting for client to connect to named pipe...");
329416
match server.connect().await {
330417
Ok(()) => {
331-
tracing::info!("Client connected to named pipe");
332418
yield Ok::<_, std::io::Error>(NamedPipeConnection(server));
333419
}
334420
Err(e) => {
335-
tracing::error!("Failed to accept connection: {}", e);
421+
tracing::error!("{}", t!("dscbicep.serverError", error = e.to_string()));
336422
break;
337423
}
338424
}
@@ -348,8 +434,15 @@ async fn run_server(
348434
}
349435

350436
// Default to HTTP server on [::1]:50051 if no transport specified
351-
let addr = http.unwrap_or_else(|| "[::1]:50051".to_string()).parse()?;
352-
tracing::info!("Starting Bicep gRPC server on HTTP: {addr}");
437+
let addr = http.unwrap_or_else(|| "[::1]:50051".to_string());
438+
tracing::info!(
439+
"{}",
440+
t!(
441+
"dscbicep.starting",
442+
transport = "HTTP",
443+
address = addr.to_string()
444+
)
445+
);
353446

354447
let reflection_service = tonic_reflection::server::Builder::configure()
355448
.register_encoded_file_descriptor_set(proto::FILE_DESCRIPTOR_SET)
@@ -359,7 +452,7 @@ async fn run_server(
359452
Server::builder()
360453
.add_service(reflection_service)
361454
.add_service(BicepExtensionServer::new(service))
362-
.serve(addr)
455+
.serve(addr.parse()?)
363456
.await?;
364457

365458
Ok(())
@@ -386,36 +479,28 @@ async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
386479
.init();
387480

388481
let args = Args::parse();
389-
tracing::debug!("Args are {args:#?}");
390482

391483
if args.wait_for_debugger
392484
|| env::var_os("DSC_GRPC_DEBUG").is_some_and(|v| v.eq_ignore_ascii_case("true"))
393485
{
394-
tracing::warn!(
395-
"Press any key to continue after attaching to PID: {}",
396-
process::id()
397-
);
486+
tracing::warn!("{}", t!("dscbicep.waitForDebugger", pid = process::id()));
398487
let mut input = String::new();
399488
io::stdin().read_line(&mut input)?;
400489
}
401490

402491
// Set up graceful shutdown on SIGTERM/SIGINT
403492
let shutdown_signal = async {
404-
tokio::signal::ctrl_c()
405-
.await
406-
.expect("Failed to listen for shutdown signal");
407-
tracing::info!("Received shutdown signal, terminating gracefully...");
493+
tokio::signal::ctrl_c().await.unwrap();
408494
};
409495

410496
tokio::select! {
411497
result = run_server(args.socket, args.pipe, args.http) => {
412498
if let Err(e) = result {
413-
tracing::error!("Server error: {e}");
499+
tracing::error!("{}", t!("dscbicep.serverError", error = e.to_string()));
414500
return Err(e);
415501
}
416502
}
417503
_ = shutdown_signal => {
418-
tracing::info!("Shutdown complete");
419504
}
420505
}
421506

0 commit comments

Comments
 (0)