diff --git a/Cargo.lock b/Cargo.lock index d74d9849..b6f9631d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -240,6 +240,12 @@ dependencies = [ "encoding_rs", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -377,6 +383,16 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "indexmap" +version = "2.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "itertools" version = "0.8.2" @@ -698,6 +714,15 @@ dependencies = [ "syn 2.0.48", ] +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + [[package]] name = "shrinkwraprs" version = "0.3.0" @@ -878,12 +903,37 @@ dependencies = [ ] [[package]] -name = "toml-span" -version = "0.2.0" +name = "toml" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "369db38ce6d1fc320a54ea3f032d07c07a232ca19c40e287246aff06d57c2abe" +checksum = "9a9aad4a3066010876e8dcf5a8a06e70a558751117a145c6ce2b82c2e2054290" dependencies = [ - "smallvec", + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c1b5fd4128cc8d3e0cb74d4ed9a9cc7c7284becd4df68f5f940e1ad123606f6" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", ] [[package]] @@ -1188,6 +1238,15 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +[[package]] +name = "winnow" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dffa400e67ed5a4dd237983829e66475f0a4a26938c4b04c21baede6262215b8" +dependencies = [ + "memchr", +] + [[package]] name = "witcherscript" version = "0.1.0" @@ -1236,9 +1295,10 @@ dependencies = [ "lsp-types", "ropey", "semver", + "serde", "shrinkwraprs", "thiserror", - "toml-span", + "toml", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index a20b6869..4d737598 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,6 @@ clap = {version = "4.4", features = ["derive"]} anyhow = "1.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -toml-span = "0.2" +toml = "0.8" semver = { version = "1.0", features = ["serde"] } dyn-clone = "1.0" \ No newline at end of file diff --git a/crates/project/Cargo.toml b/crates/project/Cargo.toml index 1d427743..8f75a749 100644 --- a/crates/project/Cargo.toml +++ b/crates/project/Cargo.toml @@ -7,7 +7,8 @@ authors.workspace = true [dependencies] semver.workspace = true -toml-span.workspace = true +toml.workspace = true +serde.workspace = true thiserror.workspace = true lsp-types.workspace = true ropey.workspace = true diff --git a/crates/project/src/manifest.rs b/crates/project/src/manifest.rs index c3b0e3ef..fc1898d9 100644 --- a/crates/project/src/manifest.rs +++ b/crates/project/src/manifest.rs @@ -1,13 +1,12 @@ -use std::{borrow::Cow, fs::File, io::{self, Read}, path::{Path, PathBuf}, sync::Arc}; +use std::{fs::File, io::{self, Read}, path::{Path, PathBuf}, str::FromStr, sync::Arc}; use ropey::Rope; use semver::Version; use shrinkwraprs::Shrinkwrap; use thiserror::Error; use lsp_types as lsp; -use toml_span::{de_helpers::{expected, TableHelper}, Deserialize}; -/// Deserialized WitcherScript manifest file containing project metadata. +/// WitcherScript manifest file containing project metadata. #[derive(Debug, Clone)] pub struct Manifest { /// Content metadata of this project @@ -28,15 +27,18 @@ pub struct Content { pub game_version: String, // CDPR's versioning system doesn't comply with semver, so string will have to do for now } +/// A list of dependency entries #[derive(Debug, Clone, Shrinkwrap)] pub struct Dependencies(Vec); +// Dependency item as a key-value pair of dependency name and dependency source specifier #[derive(Debug, Clone)] pub struct DependencyEntry { pub name: Ranged, pub value: Ranged } +/// Value of the dependency entry #[derive(Debug, Clone, PartialEq, Eq)] pub enum DependencyValue { /// Get the dependency from one of project repositories by name @@ -62,25 +64,21 @@ impl Manifest { Self::from_str(&buff) } +} + +impl FromStr for Manifest { + type Err = ManifestParseError; - pub fn from_str(s: &str) -> Result { + fn from_str(s: &str) -> Result { let rope = Rope::from_str(s); - let raw = toml_span::parse(s) - .map_err(|err| toml_span::DeserError::from(err)) - .and_then(|mut v| ManifestRaw::deserialize(&mut v)); + let raw: Result = toml::from_str(s); match raw { - Ok(raw) => { - Ok(Self { - content: raw.content, - dependencies: Dependencies::from_raw(raw.dependencies, &rope) - }) - }, + Ok(raw) => Ok(Self::from_raw(raw, &rope)), Err(err) => { - let first_error = err.errors.into_iter().next().unwrap(); Err(ManifestParseError::Toml { - range: span_to_range(first_error.span, &rope), - msg: first_error.to_string() + range: span_to_range(err.span().unwrap_or_default(), &rope), + msg: err.to_string() }) }, } @@ -99,63 +97,43 @@ pub enum ManifestParseError { } -// These "raw" types are the ones with toml_span's span type -// The proper type has range type from lsp_types -// Can't convert between those without accessing the entire file (here in a form of rope) -struct ManifestRaw { - content: Content, - dependencies: DependenciesRaw -} - -impl<'de> toml_span::Deserialize<'de> for ManifestRaw { - fn deserialize(value: &mut toml_span::Value<'de>) -> Result { - let mut th = TableHelper::new(value)?; - - let content = th.required("content")?; - let dependencies = th.required("dependencies")?; - - th.finalize(None)?; +impl FromRaw for Manifest { + type RawType = raw::Manifest; - Ok(Self { - content, - dependencies - }) + fn from_raw(raw: Self::RawType, rope: &Rope) -> Self { + Self { + content: Content::from_raw(raw.content, rope), + dependencies: Dependencies::from_raw(raw.dependencies, rope) + } } } -impl<'de> toml_span::Deserialize<'de> for Content { - fn deserialize(value: &mut toml_span::Value<'de>) -> Result { - let mut th = TableHelper::new(value)?; - - let name = th.required("name")?; - let version_str: toml_span::Spanned = th.required("version")?; - let version = Version::parse(&version_str.value) - .map_err(|e| toml_span::Error::from((toml_span::ErrorKind::Custom(Cow::Owned(e.to_string())), version_str.span)))?; - let authors = th.optional("authors"); - let game_version = th.required("game_version")?; +impl FromRaw for Content { + type RawType = raw::Content; - th.finalize(None)?; - - Ok(Content { - name, - version, - authors, - game_version - }) + fn from_raw(raw: Self::RawType, _: &Rope) -> Self { + Self { + name: raw.name, + version: raw.version, + authors: raw.authors, + game_version: raw.game_version + } } } -impl Dependencies { - fn from_raw(raw: DependenciesRaw, rope: &Rope) -> Self { +impl FromRaw for Dependencies { + type RawType = raw::Dependencies; + + fn from_raw(raw: Self::RawType, rope: &Rope) -> Self { let mut entries = Vec::new(); - for (k, v) in raw.0 { - let dep_name = Ranged::new(k.value, span_to_range(k.span, rope)); - let dep_val = Ranged::new(v.value, span_to_range(v.span, rope)); - entries.push(DependencyEntry{ - name: dep_name, - value: dep_val + for (k, v) in raw { + let dep_name = Ranged::from_raw(k, rope); + let dep_val = Ranged::from_raw(v, rope); + entries.push(DependencyEntry { + name: dep_name, + value: dep_val }); } @@ -173,53 +151,27 @@ impl IntoIterator for Dependencies { } -struct DependenciesRaw(Vec<(toml_span::Spanned, toml_span::Spanned)>); - -impl<'de> toml_span::Deserialize<'de> for DependenciesRaw { - fn deserialize(value: &mut toml_span::Value<'de>) -> Result { - let mut entries = Vec::new(); +impl FromRaw for DependencyValue { + type RawType = raw::DependencyValue; - let inner = value.take(); - if let toml_span::value::ValueInner::Table(tab) = inner { - for (k, mut v) in tab { - let dep_name = toml_span::Spanned::with_span(k.name.to_string(), k.span.clone()); - let dep_val = toml_span::Spanned::deserialize(&mut v)?; - entries.push((dep_name, dep_val)); - } - - Ok(DependenciesRaw(entries)) - } else { - Err(expected("table", inner, value.span).into()) + fn from_raw(raw: Self::RawType, _: &Rope) -> Self { + match raw { + raw::DependencyValue::FromRepo(b) => Self::FromRepo(b), + raw::DependencyValue::FromPath { path } => Self::FromPath { path }, } } } -impl<'de> toml_span::Deserialize<'de> for DependencyValue { - fn deserialize(value: &mut toml_span::Value<'de>) -> Result { - match value.take() { - toml_span::value::ValueInner::Boolean(b) => { - Ok(DependencyValue::FromRepo(b)) - }, - toml_span::value::ValueInner::Table(tab) => { - let mut th = TableHelper::from((tab, value.span)); - - let path_str: String = th.required("path")?; - let path = PathBuf::from(path_str); - - th.finalize(None)?; +impl FromRaw for String { + type RawType = String; - Ok(DependencyValue::FromPath { path }) - }, - other => { - Err(expected("bool or table", other, value.span).into()) - } - } + fn from_raw(raw: Self::RawType, _: &Rope) -> Self { + raw } } - #[derive(Debug, Clone, Shrinkwrap)] pub struct Ranged { #[shrinkwrap(main_field)] @@ -257,8 +209,7 @@ impl PartialEq for Ranged { impl Eq for Ranged {} - -fn span_to_range(span: toml_span::Span, rope: &Rope) -> lsp::Range { +fn span_to_range(span: std::ops::Range, rope: &Rope) -> lsp::Range { let start_line = rope.char_to_line(span.start); let start_char = span.start - rope.line_to_char(start_line); let end_line = rope.char_to_line(span.end); @@ -276,6 +227,58 @@ fn span_to_range(span: toml_span::Span, rope: &Rope) -> lsp::Range { } } +impl FromRaw for Ranged { + type RawType = toml::Spanned; + + fn from_raw(raw: Self::RawType, rope: &Rope) -> Self { + let span = span_to_range(raw.span(), rope); + Self::new(T::from_raw(raw.into_inner(), rope), span) + } +} + + + +// These "raw" types are the ones with toml's span type +// This is the form that can be directly passed into serde +// The proper type has range type from lsp_types +// Can't convert between those without knowing contents of the entire file (here in a form of rope) +mod raw { + use std::{collections::BTreeMap, path::PathBuf}; + use semver::Version; + use serde::{Deserialize, Serialize}; + + #[derive(Serialize, Deserialize)] + pub struct Manifest { + pub content: Content, + pub dependencies: Dependencies + } + + #[derive(Serialize, Deserialize)] + pub struct Content { + pub name: String, + pub version: Version, + pub authors: Option>, + pub game_version: String, + } + + pub type Dependencies = BTreeMap, toml::Spanned>; + + #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] + #[serde(untagged)] + pub enum DependencyValue { + FromRepo(bool), + FromPath { + path: PathBuf + } + } +} + +trait FromRaw { + type RawType; + + fn from_raw(raw: Self::RawType, rope: &Rope) -> Self; +} + #[cfg(test)]