Skip to content

Commit

Permalink
feat: add protols.toml to configure protols in a workspace
Browse files Browse the repository at this point in the history
  • Loading branch information
coder3101 committed Oct 20, 2024
1 parent c1c30a0 commit 397f25a
Show file tree
Hide file tree
Showing 16 changed files with 324 additions and 70 deletions.
14 changes: 12 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ walkdir = "2.5.0"
hard-xml = "1.36.0"
tempfile = "3.12.0"
serde = { version = "1.0.209", features = ["derive"] }
basic-toml = "0.1.9"

[dev-dependencies]
insta = { version = "1.39.0", features = ["yaml"] }
9 changes: 9 additions & 0 deletions src/config/input/protols-valid.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[config]
include_paths = ["foobar", "bazbaaz"]
disable_parse_diagnostics = true

[config.experimental]
use_protoc_diagnostics = true

[formatter]
clang_format_path = "/usr/bin/clang-format"
52 changes: 52 additions & 0 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use serde::{Deserialize, Serialize};

pub mod workspace;

fn default_clang_format_path() -> String {
"clang-format".to_string()
}

#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(default)]
pub struct ProtolsConfig {
pub config: Config,
pub formatter: FormatterConfig,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct FormatterConfig {
#[serde(default = "default_clang_format_path")]
pub clang_format_path: String,
}

#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[serde(default)]
pub struct Config {
pub include_paths: Vec<String>,
pub single_file_mode: bool,
pub disable_parse_diagnostics: bool,
pub experimental: ExperimentalConfig,
}

#[derive(Serialize, Deserialize, Debug, Clone, Default)]
#[serde(default)]
pub struct ExperimentalConfig {
pub use_protoc_diagnostics: bool,
}

impl Default for ProtolsConfig {
fn default() -> Self {
Self {
config: Config::default(),
formatter: FormatterConfig::default(),
}
}
}

impl Default for FormatterConfig {
fn default() -> Self {
Self {
clang_format_path: default_clang_format_path(),
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
source: src/config/workspace.rs
expression: ws.get_config_for_uri(&inworkspace2).unwrap()
---
config:
include_paths: []
single_file_mode: false
disable_parse_diagnostics: false
experimental:
use_protoc_diagnostics: false
formatter:
clang_format_path: clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
source: src/config/workspace.rs
expression: ws.get_config_for_uri(&inworkspace).unwrap()
---
config:
include_paths:
- foobar
- bazbaaz
single_file_mode: false
disable_parse_diagnostics: true
experimental:
use_protoc_diagnostics: true
formatter:
clang_format_path: /usr/bin/clang-format
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
source: src/config/workspace.rs
expression: ws.get_config_for_uri(&inworkspace)
---
config:
include_paths:
- foobar
- bazbaaz
single_file_mode: false
disable_parse_diagnostics: true
experimental:
use_protoc_diagnostics: true
formatter:
clang_format_path: /usr/bin/clang-format
141 changes: 141 additions & 0 deletions src/config/workspace.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
use std::{
collections::{HashMap, HashSet},
path::Path,
};

use async_lsp::lsp_types::{Url, WorkspaceFolder};

use crate::formatter::clang::ClangFormatter;

use super::ProtolsConfig;

const CONFIG_FILE_NAME: &str = "protols.toml";

pub struct WorkspaceProtoConfigs {
workspaces: HashSet<Url>,
configs: HashMap<Url, ProtolsConfig>,
formatters: HashMap<Url, ClangFormatter>,
}

impl WorkspaceProtoConfigs {
pub fn new() -> Self {
Self {
workspaces: Default::default(),
formatters: Default::default(),
configs: Default::default(),
}
}

pub fn add_workspace(&mut self, w: &WorkspaceFolder) {
let Ok(wpath) = w.uri.to_file_path() else {
return;
};

let p = Path::new(&wpath).join(CONFIG_FILE_NAME);
let content = std::fs::read_to_string(p).unwrap_or_default();

let wr: ProtolsConfig = basic_toml::from_str(&content).unwrap_or_default();
let fmt = ClangFormatter::new(
&wr.formatter.clang_format_path,
wpath.to_str().expect("non-utf8 path"),
);

self.workspaces.insert(w.uri.clone());
self.configs.insert(w.uri.clone(), wr);
self.formatters.insert(w.uri.clone(), fmt);
}

pub fn get_config_for_uri(&self, u: &Url) -> Option<&ProtolsConfig> {
self.get_workspace_for_uri(u)
.and_then(|w| self.configs.get(w))
}

pub fn get_formatter_for_uri(&self, u: &Url) -> Option<&ClangFormatter> {
self.get_workspace_for_uri(u)
.and_then(|w| self.formatters.get(w))
}

pub fn get_workspace_for_uri(&self, u: &Url) -> Option<&Url> {
let upath = u.to_file_path().ok()?;
self.workspaces
.iter()
.find(|&k| upath.starts_with(k.to_file_path().unwrap()))
}
}

#[cfg(test)]
mod test {
use async_lsp::lsp_types::{Url, WorkspaceFolder};
use insta::assert_yaml_snapshot;
use tempfile::tempdir;

use super::WorkspaceProtoConfigs;

#[test]
fn test_get_for_workspace() {
let tmpdir = tempdir().expect("failed to create temp directory");
let tmpdir2 = tempdir().expect("failed to create temp2 directory");
let f = tmpdir.path().join("protols.toml");
std::fs::write(f, include_str!("input/protols-valid.toml")).unwrap();

let mut ws = WorkspaceProtoConfigs::new();
ws.add_workspace(&WorkspaceFolder {
uri: Url::from_directory_path(tmpdir.path()).unwrap(),
name: "Test".to_string(),
});
ws.add_workspace(&WorkspaceFolder {
uri: Url::from_directory_path(tmpdir2.path()).unwrap(),
name: "Test2".to_string(),
});

let inworkspace = Url::from_file_path(tmpdir.path().join("foobar.proto")).unwrap();
let outworkspace =
Url::from_file_path(tempdir().unwrap().path().join("out.proto")).unwrap();
let inworkspace2 = Url::from_file_path(tmpdir2.path().join("foobar.proto")).unwrap();

assert!(ws.get_config_for_uri(&inworkspace).is_some());
assert!(ws.get_config_for_uri(&inworkspace2).is_some());
assert!(ws.get_config_for_uri(&outworkspace).is_none());

assert!(ws.get_workspace_for_uri(&inworkspace).is_some());
assert!(ws.get_workspace_for_uri(&inworkspace2).is_some());
assert!(ws.get_workspace_for_uri(&outworkspace).is_none());

assert_yaml_snapshot!(ws.get_config_for_uri(&inworkspace).unwrap());
assert_yaml_snapshot!(ws.get_config_for_uri(&inworkspace2).unwrap());
}

#[test]
fn test_get_formatter_for_uri() {
let tmpdir = tempdir().expect("failed to create temp directory");
let tmpdir2 = tempdir().expect("failed to create temp2 directory");
let f = tmpdir.path().join("protols.toml");
std::fs::write(f, include_str!("input/protols-valid.toml")).unwrap();

let mut ws = WorkspaceProtoConfigs::new();
ws.add_workspace(&WorkspaceFolder {
uri: Url::from_directory_path(tmpdir.path()).unwrap(),
name: "Test".to_string(),
});

ws.add_workspace(&WorkspaceFolder {
uri: Url::from_directory_path(tmpdir2.path()).unwrap(),
name: "Test2".to_string(),
});

let inworkspace = Url::from_file_path(tmpdir.path().join("foobar.proto")).unwrap();
let outworkspace =
Url::from_file_path(tempdir().unwrap().path().join("out.proto")).unwrap();
let inworkspace2 = Url::from_file_path(tmpdir2.path().join("foobar.proto")).unwrap();

assert!(ws.get_formatter_for_uri(&outworkspace).is_none());
assert_eq!(
ws.get_formatter_for_uri(&inworkspace).unwrap().path,
"/usr/bin/clang-format"
);
assert_eq!(
ws.get_formatter_for_uri(&inworkspace2).unwrap().path,
"clang-format"
);
}
}
24 changes: 9 additions & 15 deletions src/formatter/clang.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#![allow(clippy::needless_late_init)]
use std::{
borrow::Cow,
error::Error,
fs::File,
io::Write,
path::{Path, PathBuf},
Expand All @@ -16,8 +15,8 @@ use tempfile::{tempdir, TempDir};
use super::ProtoFormatter;

pub struct ClangFormatter {
path: String,
working_dir: Option<String>,
pub path: String,
working_dir: String,
temp_dir: TempDir,
}

Expand Down Expand Up @@ -67,15 +66,12 @@ impl<'a> Replacement<'a> {
}

impl ClangFormatter {
pub fn new(path: &str, workdir: Option<&str>) -> Result<Self, Box<dyn Error>> {
let mut c = Command::new(path);
c.arg("--version").status()?;

Ok(Self {
temp_dir: tempdir()?,
path: path.to_owned(),
working_dir: workdir.map(ToOwned::to_owned),
})
pub fn new(cmd: &str, wdir: &str) -> Self {
Self {
temp_dir: tempdir().expect("faile to creat temp dir"),
path: cmd.to_owned(),
working_dir: wdir.to_owned()
}
}

fn get_temp_file_path(&self, content: &str) -> Option<PathBuf> {
Expand All @@ -87,9 +83,7 @@ impl ClangFormatter {

fn get_command(&self, u: &Path) -> Command {
let mut c = Command::new(self.path.as_str());
if let Some(wd) = self.working_dir.as_ref() {
c.current_dir(wd.as_str());
}
c.current_dir(self.working_dir.as_str());
c.args([u.to_str().unwrap(), "--output-replacements-xml"]);
c
}
Expand Down
Loading

0 comments on commit 397f25a

Please sign in to comment.