use std::{collections::HashSet, path::PathBuf};
use bevy_ecs::prelude::*;
use serde::Deserialize;
use crate::{
lsp_types::{Url, WorkspaceFolder},
util::fs::Fs,
};
#[derive(Debug, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Disabled {
#[serde(alias = "SHAPES", alias = "shapes")]
Shapes,
}
#[derive(Resource, Debug, Default)]
pub struct ServerConfig {
pub workspaces: Vec<WorkspaceFolder>,
pub config: Config,
}
#[derive(Debug, Deserialize)]
pub struct Config {
#[serde(default = "debug")]
pub log: String,
pub turtle: Option<bool>,
pub trig: Option<bool>,
pub jsonld: Option<bool>,
pub sparql: Option<bool>,
#[serde(flatten)]
pub local: LocalConfig,
}
#[derive(Debug, Deserialize, Default)]
#[serde(default)]
pub struct LocalConfig {
pub ontologies: HashSet<String>,
pub shapes: HashSet<String>,
pub disabled: HashSet<Disabled>,
pub prefix_disabled: HashSet<String>,
pub completion: CompletionConfig,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(untagged)]
pub enum CompletionConfig {
Mode(CompletionMode),
Except(ExceptRules),
Strict(StrictRules),
}
impl Default for CompletionConfig {
fn default() -> Self {
Self::Mode(CompletionMode::None)
}
}
impl CompletionConfig {
fn combine(&mut self, other: CompletionConfig) {
use CompletionConfig::*;
if matches!(other, Mode(CompletionMode::None)) {
return;
}
if matches!(self, Mode(CompletionMode::None)) {
*self = other;
return;
}
if let Strict(r) = self {
if let Strict(r2) = other {
r.strict.extend(r2.strict);
return;
}
}
if let Except(r) = self {
if let Except(r2) = other {
r.loose.extend(r2.loose);
return;
}
}
*self = other;
}
pub fn correct_domain_required(&self, property: &str) -> bool {
match self {
CompletionConfig::Mode(CompletionMode::Loose)
| CompletionConfig::Mode(CompletionMode::None) => false,
CompletionConfig::Mode(CompletionMode::Strict) => true,
CompletionConfig::Except(completion_rules) => !completion_rules
.loose
.iter()
.any(|x| property.starts_with(x)),
CompletionConfig::Strict(completion_rules) => completion_rules
.strict
.iter()
.any(|x| property.starts_with(x)),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum CompletionMode {
#[default]
None,
Loose,
Strict,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Default)]
#[serde(deny_unknown_fields)]
pub struct ExceptRules {
#[serde(default)]
pub loose: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Default)]
#[serde(deny_unknown_fields)]
pub struct StrictRules {
#[serde(default)]
pub strict: Vec<String>,
}
impl LocalConfig {
pub fn combine(&mut self, other: LocalConfig) {
self.ontologies.extend(other.ontologies);
self.shapes.extend(other.shapes);
self.disabled.extend(other.disabled);
self.prefix_disabled.extend(other.prefix_disabled);
self.completion.combine(other.completion);
}
#[cfg(target_arch = "wasm32")]
pub async fn global(_: &Fs) -> Option<Self> {
None
}
#[cfg(not(target_arch = "wasm32"))]
pub async fn global(fs: &Fs) -> Option<Self> {
let global_path = dirs::config_dir()
.unwrap_or_else(|| PathBuf::from("."))
.join("swls/config.json");
let url = crate::lsp_types::Url::from_file_path(global_path).ok()?;
tracing::debug!("Found global config url {}", url.as_str());
let content = fs.0.read_file(&url).await?;
tracing::debug!("Read global config content");
match serde_json::from_str(&content) {
Ok(x) => Some(x),
Err(e) => {
tracing::error!("Deserialize failed\n{:?}", e);
None
}
}
}
pub async fn local(fs: &Fs, url: &Url) -> Option<Self> {
let url = Url::parse(&format!("{}/.swls/config.json", url.as_str())).ok()?;
tracing::debug!("Found local config url {}", url.as_str());
let content = fs.0.read_file(&url).await?;
tracing::debug!("Read local config content");
match serde_json::from_str(&content) {
Ok(x) => Some(x),
Err(e) => {
tracing::error!("Deserialize failed\n{:?}", e);
None
}
}
}
}
impl Default for Config {
fn default() -> Self {
Self {
log: "debug".to_string(),
turtle: None,
trig: None,
jsonld: None,
sparql: None,
local: LocalConfig::default(),
}
}
}
fn debug() -> String {
String::from("debug")
}