Skip to content

Commit b40b807

Browse files
committed
refactor(lsp): use trait for ComponentFactory
1 parent 9bc8f45 commit b40b807

File tree

12 files changed

+543
-582
lines changed

12 files changed

+543
-582
lines changed

src/app/component_factory.rs

Lines changed: 14 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,31 @@
11
use std::env::VarError;
22

3-
use bollard::Docker;
43
use serde::Deserialize;
54
use thiserror::Error;
65
use tower_lsp::jsonrpc::{Error as LspError, ErrorCode};
76

8-
use crate::infra::{DockerImageBuilder, SysdigAPIToken, SysdigImageScanner};
7+
use super::{ImageBuilder, ImageScanner};
98

109
#[derive(Clone, Debug, Default, Deserialize)]
1110
pub struct Config {
12-
sysdig: SysdigConfig,
11+
pub sysdig: SysdigConfig,
1312
}
1413

1514
#[derive(Clone, Debug, Default, Deserialize)]
1615
pub struct SysdigConfig {
17-
api_url: String,
18-
api_token: Option<SysdigAPIToken>,
16+
#[serde(alias = "apiUrl")]
17+
pub api_url: String,
18+
#[serde(alias = "apiToken")]
19+
pub api_token: Option<String>,
1920
}
2021

21-
#[derive(Clone)]
22-
pub struct ComponentFactory {
23-
scanner: SysdigImageScanner,
24-
builder: DockerImageBuilder,
22+
pub struct Components {
23+
pub scanner: Box<dyn ImageScanner + Send + Sync>,
24+
pub builder: Box<dyn ImageBuilder + Send + Sync>,
25+
}
26+
27+
pub trait ComponentFactory: Send + Sync {
28+
fn create_components(&self, config: Config) -> Result<Components, ComponentFactoryError>;
2529
}
2630

2731
#[derive(Error, Debug)]
@@ -30,33 +34,7 @@ pub enum ComponentFactoryError {
3034
UnableToRetrieveAPITokenFromEnvVar(#[from] VarError),
3135

3236
#[error("docker client error: {0:?}")]
33-
DockerClientError(#[from] bollard::errors::Error),
34-
}
35-
36-
impl ComponentFactory {
37-
pub fn new(config: Config) -> Result<Self, ComponentFactoryError> {
38-
let token = config
39-
.sysdig
40-
.api_token
41-
.clone()
42-
.map(Ok)
43-
.unwrap_or_else(|| std::env::var("SECURE_API_TOKEN").map(SysdigAPIToken))?;
44-
45-
let scanner = SysdigImageScanner::new(config.sysdig.api_url.clone(), token);
46-
47-
let docker_client = Docker::connect_with_local_defaults()?;
48-
let builder = DockerImageBuilder::new(docker_client);
49-
50-
Ok(Self { scanner, builder })
51-
}
52-
53-
pub fn image_scanner(&self) -> &SysdigImageScanner {
54-
&self.scanner
55-
}
56-
57-
pub fn image_builder(&self) -> &DockerImageBuilder {
58-
&self.builder
59-
}
37+
DockerClientError(String),
6038
}
6139

6240
impl From<ComponentFactoryError> for LspError {
@@ -78,14 +56,3 @@ impl From<ComponentFactoryError> for LspError {
7856
}
7957
}
8058
}
81-
82-
#[cfg(test)]
83-
mod test {
84-
use super::{ComponentFactory, Config};
85-
86-
#[test]
87-
fn it_creates_a_factory() {
88-
let factory = ComponentFactory::new(Config::default());
89-
assert!(factory.is_ok());
90-
}
91-
}

src/app/lsp_server/commands/build_and_scan.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use crate::{
1414

1515
use super::LspCommand;
1616

17-
pub struct BuildAndScanCommand<'a, C, B, S>
17+
pub struct BuildAndScanCommand<'a, C, B: ?Sized, S: ?Sized>
1818
where
1919
B: ImageBuilder,
2020
S: ImageScanner,
@@ -25,7 +25,7 @@ where
2525
location: Location,
2626
}
2727

28-
impl<'a, C, B, S> BuildAndScanCommand<'a, C, B, S>
28+
impl<'a, C, B: ?Sized, S: ?Sized> BuildAndScanCommand<'a, C, B, S>
2929
where
3030
B: ImageBuilder,
3131
S: ImageScanner,
@@ -46,7 +46,7 @@ where
4646
}
4747

4848
#[async_trait::async_trait]
49-
impl<'a, C, B, S> LspCommand for BuildAndScanCommand<'a, C, B, S>
49+
impl<'a, C, B: ?Sized, S: ?Sized> LspCommand for BuildAndScanCommand<'a, C, B, S>
5050
where
5151
C: LSPClient + Sync,
5252
B: ImageBuilder + Sync,

src/app/lsp_server/commands/scan_base_image.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::{
88

99
use super::LspCommand;
1010

11-
pub struct ScanBaseImageCommand<'a, C, S>
11+
pub struct ScanBaseImageCommand<'a, C, S: ?Sized>
1212
where
1313
S: ImageScanner,
1414
{
@@ -18,7 +18,7 @@ where
1818
image: String,
1919
}
2020

21-
impl<'a, C, S> ScanBaseImageCommand<'a, C, S>
21+
impl<'a, C, S: ?Sized> ScanBaseImageCommand<'a, C, S>
2222
where
2323
S: ImageScanner,
2424
{
@@ -38,7 +38,7 @@ where
3838
}
3939

4040
#[async_trait::async_trait]
41-
impl<'a, C, S> LspCommand for ScanBaseImageCommand<'a, C, S>
41+
impl<'a, C, S: ?Sized> LspCommand for ScanBaseImageCommand<'a, C, S>
4242
where
4343
C: LSPClient + Sync,
4444
S: ImageScanner + Sync,

src/app/lsp_server/lsp_server_inner.rs

Lines changed: 40 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -9,56 +9,58 @@ use tower_lsp::lsp_types::{
99
};
1010
use tracing::{debug, info};
1111

12-
use super::super::LspInteractor;
13-
use super::super::component_factory::{ComponentFactory, Config};
12+
use super::super::component_factory::{ComponentFactory, Components, Config};
1413
use super::super::queries::QueryExecutor;
1514
use super::command_generator;
1615
use super::commands::{
1716
LspCommand, build_and_scan::BuildAndScanCommand, scan_base_image::ScanBaseImageCommand,
1817
};
1918
use super::{InMemoryDocumentDatabase, LSPClient, WithContext};
19+
use crate::app::LspInteractor;
2020

2121
use super::supported_commands::SupportedCommands;
2222

23-
pub struct LSPServerInner<C> {
23+
pub struct LSPServerInner<C, F: ComponentFactory> {
2424
interactor: LspInteractor<C>,
2525
query_executor: QueryExecutor,
26-
component_factory: Option<ComponentFactory>,
26+
component_factory: F,
27+
components: Option<Components>,
2728
}
2829

29-
impl<C> LSPServerInner<C> {
30-
pub fn new(client: C) -> LSPServerInner<C> {
30+
impl<C, F: ComponentFactory> LSPServerInner<C, F> {
31+
pub fn new(client: C, component_factory: F) -> LSPServerInner<C, F> {
3132
let document_database = InMemoryDocumentDatabase::default();
3233

3334
LSPServerInner {
3435
interactor: LspInteractor::new(client, document_database.clone()),
3536
query_executor: QueryExecutor::new(document_database.clone()),
36-
component_factory: None, // to be initialized in the initialize method of the LSP
37+
component_factory,
38+
components: None,
3739
}
3840
}
3941
}
4042

41-
impl<C> LSPServerInner<C>
43+
impl<C, F: ComponentFactory> LSPServerInner<C, F>
4244
where
4345
C: LSPClient + Send + Sync + 'static,
4446
{
45-
fn update_component_factory(&mut self, config: &Value) -> Result<()> {
47+
fn update_components(&mut self, config: &Value) -> Result<()> {
4648
let config = serde_json::from_value::<Config>(config.clone()).map_err(|e| {
4749
Error::internal_error()
4850
.with_message(format!("unable to transform json into config: {e}"))
4951
})?;
5052

5153
debug!("updating with configuration: {config:?}");
5254

53-
let factory = ComponentFactory::new(config)?;
54-
self.component_factory.replace(factory);
55+
let components = self.component_factory.create_components(config)?;
56+
self.components.replace(components);
5557

5658
debug!("updated configuration");
5759
Ok(())
5860
}
5961
}
6062

61-
impl<C> LSPServerInner<C>
63+
impl<C, F: ComponentFactory> LSPServerInner<C, F>
6264
where
6365
C: LSPClient + Send + Sync + 'static,
6466
{
@@ -88,7 +90,7 @@ where
8890
});
8991
};
9092

91-
self.update_component_factory(&config)?;
93+
self.update_components(&config)?;
9294

9395
Ok(InitializeResult {
9496
capabilities: ServerCapabilities {
@@ -117,7 +119,7 @@ where
117119
}
118120

119121
pub async fn did_change_configuration(&mut self, params: DidChangeConfigurationParams) {
120-
let _ = self.update_component_factory(&params.settings);
122+
let _ = self.update_components(&params.settings);
121123
}
122124

123125
pub async fn did_open(&self, params: DidOpenTextDocumentParams) {
@@ -162,31 +164,38 @@ where
162164
Ok(Some(code_lenses))
163165
}
164166

167+
fn components(&self) -> Result<&Components> {
168+
self.components
169+
.as_ref()
170+
.ok_or_else(|| Error::internal_error().with_message("LSP not initialized"))
171+
}
172+
165173
async fn execute_base_image_scan(
166174
&self,
167175
location: tower_lsp::lsp_types::Location,
168176
image: String,
169177
) -> Result<()> {
170-
let factory = self
171-
.component_factory
172-
.as_ref()
173-
.ok_or_else(|| Error::internal_error().with_message("LSP not initialized"))?;
174-
let image_scanner = factory.image_scanner();
175-
ScanBaseImageCommand::new(image_scanner, &self.interactor, location, image)
176-
.execute()
177-
.await
178+
let components = self.components()?;
179+
ScanBaseImageCommand::new(
180+
components.scanner.as_ref(),
181+
&self.interactor,
182+
location,
183+
image,
184+
)
185+
.execute()
186+
.await
178187
}
179188

180189
async fn execute_build_and_scan(&self, location: tower_lsp::lsp_types::Location) -> Result<()> {
181-
let factory = self
182-
.component_factory
183-
.as_ref()
184-
.ok_or_else(|| Error::internal_error().with_message("LSP not initialized"))?;
185-
let image_scanner = factory.image_scanner();
186-
let image_builder = factory.image_builder();
187-
BuildAndScanCommand::new(image_builder, image_scanner, &self.interactor, location)
188-
.execute()
189-
.await
190+
let components = self.components()?;
191+
BuildAndScanCommand::new(
192+
components.builder.as_ref(),
193+
components.scanner.as_ref(),
194+
&self.interactor,
195+
location,
196+
)
197+
.execute()
198+
.await
190199
}
191200

192201
async fn handle_command_error(&self, command_name: &str, e: Error) -> Error {

src/app/lsp_server/mod.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ pub mod command_generator;
1515
pub mod commands;
1616
mod lsp_server_inner;
1717
pub mod supported_commands;
18+
use crate::app::component_factory::ComponentFactory;
1819
use lsp_server_inner::LSPServerInner;
1920

2021
pub trait WithContext {
@@ -28,14 +29,14 @@ impl WithContext for Error {
2829
}
2930
}
3031

31-
pub struct LSPServer<C> {
32-
inner: RwLock<LSPServerInner<C>>,
32+
pub struct LSPServer<C, F: ComponentFactory> {
33+
inner: RwLock<LSPServerInner<C, F>>,
3334
}
3435

35-
impl<C> LSPServer<C> {
36-
pub fn new(client: C) -> LSPServer<C> {
36+
impl<C, F: ComponentFactory> LSPServer<C, F> {
37+
pub fn new(client: C, component_factory: F) -> LSPServer<C, F> {
3738
LSPServer {
38-
inner: RwLock::new(LSPServerInner::new(client)),
39+
inner: RwLock::new(LSPServerInner::new(client, component_factory)),
3940
}
4041
}
4142
}
@@ -48,9 +49,10 @@ struct CommandInfo {
4849
}
4950

5051
#[async_trait::async_trait]
51-
impl<C> LanguageServer for LSPServer<C>
52+
impl<C, F> LanguageServer for LSPServer<C, F>
5253
where
5354
C: LSPClient + Send + Sync + 'static,
55+
F: ComponentFactory + Send + Sync + 'static,
5456
{
5557
async fn initialize(&self, params: InitializeParams) -> Result<InitializeResult> {
5658
self.inner.write().await.initialize(params).await

src/app/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
mod component_factory;
1+
pub mod component_factory;
22
mod document_database;
33
mod image_builder;
44
mod image_scanner;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
use bollard::Docker;
2+
3+
use crate::{
4+
app::component_factory::{ComponentFactory, ComponentFactoryError, Components, Config},
5+
infra::{DockerImageBuilder, SysdigAPIToken, SysdigImageScanner},
6+
};
7+
8+
pub struct ConcreteComponentFactory;
9+
10+
impl ComponentFactory for ConcreteComponentFactory {
11+
fn create_components(&self, config: Config) -> Result<Components, ComponentFactoryError> {
12+
let token = config
13+
.sysdig
14+
.api_token
15+
.clone()
16+
.map(Ok)
17+
.unwrap_or_else(|| std::env::var("SECURE_API_TOKEN"))
18+
.map(SysdigAPIToken)?;
19+
20+
let scanner = SysdigImageScanner::new(config.sysdig.api_url.clone(), token);
21+
22+
let docker_client = Docker::connect_with_local_defaults()
23+
.map_err(|e| ComponentFactoryError::DockerClientError(e.to_string()))?;
24+
let builder = DockerImageBuilder::new(docker_client);
25+
26+
Ok(Components {
27+
scanner: Box::new(scanner),
28+
builder: Box::new(builder),
29+
})
30+
}
31+
}

src/infra/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
mod component_factory_impl;
12
mod compose_ast_parser;
23
mod docker_image_builder;
34
mod dockerfile_ast_parser;
@@ -7,6 +8,7 @@ mod sysdig_image_scanner_json_scan_result_v1;
78

89
pub use sysdig_image_scanner::{SysdigAPIToken, SysdigImageScanner};
910
pub mod lsp_logger;
11+
pub use component_factory_impl::ConcreteComponentFactory;
1012
pub use compose_ast_parser::{ImageInstruction, parse_compose_file};
1113
pub use docker_image_builder::DockerImageBuilder;
1214
pub use dockerfile_ast_parser::{Instruction, parse_dockerfile};

src/main.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
use clap::Parser;
2-
use sysdig_lsp::{app::LSPServer, infra::lsp_logger::LSPLogger};
2+
use sysdig_lsp::{
3+
app::LSPServer,
4+
infra::{ConcreteComponentFactory, lsp_logger::LSPLogger},
5+
};
36
use tower_lsp::{LspService, Server};
47
use tracing_subscriber::layer::SubscriberExt;
58

@@ -21,7 +24,7 @@ async fn main() {
2124
tracing::subscriber::set_global_default(subscriber)
2225
.expect("setting default subscriber failed");
2326

24-
LSPServer::new(client)
27+
LSPServer::new(client, ConcreteComponentFactory)
2528
});
2629

2730
Server::new(stdin, stdout, messages).serve(service).await;

0 commit comments

Comments
 (0)