diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fc30d5a..09d3bc0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,10 +8,11 @@ repos: - id: end-of-file-fixer - id: mixed-line-ending - id: trailing-whitespace - - repo: https://github.com/pre-commit/mirrors-prettier - rev: "v3.1.0" + - repo: https://github.com/biomejs/pre-commit + rev: "v0.4.0" hooks: - - id: prettier + - id: biome-check + additional_dependencies: ["@biomejs/biome@1.9.4"] - repo: local hooks: @@ -28,11 +29,3 @@ repos: language: system types_or: [toml] entry: taplo format - - - repo: local - hooks: - - id: clang-format - name: clang-format - language: system - types_or: [c, c++, cuda] - entry: clang-format -i --style=file diff --git a/Cargo.lock b/Cargo.lock index 04c9538..d109cc8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,18 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if 1.0.0", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.0.5" @@ -212,11 +224,11 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.83" +version = "1.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "a012a0df96dd6d06ba9a1b29d6402d1a5d77c6befd2566afdc26e10603dc93d7" dependencies = [ - "libc", + "shlex", ] [[package]] @@ -421,12 +433,30 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + [[package]] name = "flate2" version = "1.0.27" @@ -589,9 +619,21 @@ checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashlink" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +dependencies = [ + "hashbrown 0.14.5", +] [[package]] name = "heck" @@ -773,7 +815,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" dependencies = [ "equivalent", - "hashbrown 0.14.0", + "hashbrown 0.14.5", ] [[package]] @@ -782,6 +824,15 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.9" @@ -832,6 +883,17 @@ version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +[[package]] +name = "libsqlite3-sys" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + [[package]] name = "lock_api" version = "0.4.10" @@ -932,12 +994,13 @@ dependencies = [ "clap 4.5.23", "derive", "futures", + "itertools", "lazy-regex", - "lazy_static", "minijinja", "nixpkgs-fmt", "redb", "reqwest", + "rusqlite", "rust-embed", "semver", "serde", @@ -1046,6 +1109,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1294,6 +1363,20 @@ dependencies = [ "text-size", ] +[[package]] +name = "rusqlite" +version = "0.32.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" +dependencies = [ + "bitflags 2.6.0", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink", + "libsqlite3-sys", + "smallvec", +] + [[package]] name = "rust-embed" version = "8.5.0" @@ -1487,6 +1570,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -1906,6 +1995,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "vec_map" version = "0.8.2" @@ -2247,6 +2342,26 @@ dependencies = [ "memchr", ] +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zeroize" version = "1.7.0" diff --git a/Cargo.toml b/Cargo.toml index dc4bec1..4e0e2cd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,8 +34,9 @@ thiserror = "2.0" lazy-regex = "3.4" chrono = "0.4" derive = { path = "./derive" } -lazy_static = "1.5" rust-embed = "8.5" +rusqlite = { version = "0.32.1", features = ["bundled"] } +itertools = "0.14.0" [package.metadata.deb] assets = [ diff --git a/biome.jsonc b/biome.jsonc new file mode 100644 index 0000000..b8d927d --- /dev/null +++ b/biome.jsonc @@ -0,0 +1,57 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.8.3/schema.json", + "organizeImports": { + "enabled": true + }, + "formatter": { + "indentStyle": "space", + "indentWidth": 4 + }, + "javascript": { + "formatter": { + "quoteStyle": "single", + "jsxQuoteStyle": "double", + "arrowParentheses": "asNeeded", + "indentWidth": 2 + } + }, + "json": { + "formatter": { + "indentWidth": 2 + } + }, + "linter": { + "rules": { + "correctness": { + "noUnusedImports": "warn", + "noUnusedVariables": "warn", + "useExhaustiveDependencies": "off" + }, + "style": { + "useConst": "warn", + "useNamingConvention": { + "level": "warn", + "options": { + "requireAscii": true, + "strictCase": true + } + } + }, + "complexity": { + "noForEach": "off" + }, + "security": { + "noDangerouslySetInnerHtml": "off" + }, + "a11y": { + "useKeyWithClickEvents": "off" + }, + "nursery": { + "useSortedClasses": { + "level": "warn", + "fix": "safe" + } + } + } + } +} diff --git a/config.toml b/config.toml index 076a509..44781ae 100644 --- a/config.toml +++ b/config.toml @@ -1,6 +1,10 @@ vscode_version = "1.84.2" autogen_warning = "# Warning, this file is autogenerated by nix4vscode. Don't modify this manually." +[[extensions]] +publisher_name = "ms-python" +extension_name = "debugpy" + [[extensions]] publisher_name = "42crunch" extension_name = "vscode-openapi" diff --git a/src/code/extensions.rs b/src/code/extensions.rs index b4e1c94..4cd3b17 100644 --- a/src/code/extensions.rs +++ b/src/code/extensions.rs @@ -2,7 +2,7 @@ use derive::api; use serde::{Deserialize, Serialize}; // https://github.com/microsoft/vscode/blob/d187d50a482ff80dcf74c35affb09dda1a7cd2fe/src/vs/platform/extensions/common/extensions.ts -#[derive(Debug, Serialize, Deserialize, Clone, Copy)] +#[derive(Debug, Serialize, Deserialize, Clone, Copy, Hash, Eq, PartialEq)] #[serde(rename_all = "kebab-case")] pub enum TargetPlatform { Win32X64, diff --git a/src/code/version.rs b/src/code/version.rs index 55c47da..274cff0 100644 --- a/src/code/version.rs +++ b/src/code/version.rs @@ -14,7 +14,7 @@ macro_rules! texpr { } #[derive(Debug, Clone, Default)] -struct IParsedVersion { +pub struct IParsedVersion { has_caret: bool, has_greater_equals: bool, major_base: u64, @@ -27,7 +27,7 @@ struct IParsedVersion { } impl IParsedVersion { - fn new(version: &str) -> anyhow::Result { + pub fn new(version: &str) -> anyhow::Result { let version = version.trim(); if version == "*" { return Ok(Default::default()); diff --git a/src/config.rs b/src/config.rs index f0f3b46..6ff0389 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,6 +1,7 @@ use anyhow::anyhow; use lazy_regex::regex; use serde::{Deserialize, Serialize}; +use tokio::fs; use crate::jinja::{Generator, SystemContext}; @@ -22,6 +23,11 @@ pub struct Config { } impl Config { + pub async fn from_file(path: &str) -> anyhow::Result { + let content = fs::read_to_string(path).await?; + Self::new(&content) + } + pub fn new(content: &str) -> anyhow::Result { let mut obj: Config = toml::from_str(content)?; let reg = regex!(r#"(\d+.\d+.\d+)(.*)?"#) diff --git a/src/db.rs b/src/db.rs new file mode 100644 index 0000000..0073567 --- /dev/null +++ b/src/db.rs @@ -0,0 +1,125 @@ +#![allow(dead_code)] +use std::sync::LazyLock; + +use rusqlite::Connection; +use tokio::sync::Mutex; + +pub static GLOBAL_DB: LazyLock = LazyLock::new(|| { + let path = format!( + "{}/{}/{}", + std::env::var("HOME").unwrap(), + ".cache", + "nix4vscode" + ); + Db::new(&path).unwrap() +}); + +#[derive(Debug)] +pub struct Entry { + publisher: String, + name: String, + url: String, + sha256: Option, + platform: String, + engine: String, +} + +pub struct Db { + conn: Mutex, +} + +impl Db { + pub fn new(path: &str) -> anyhow::Result { + let conn = Connection::open(path)?; + + conn.execute( + r" +CREATE TABLE IF NOT EXISTS extension ( + publisher TEXT NOT NULL, + name TEXT NOT NULL, + url TEXT PRIMARY KEY NOT NULL, + sha256 TEXT NULL, + platform TEXT NOT NULL, + engine TEXT NOT NULL +); + ", + (), + )?; + + Ok(Self { conn: conn.into() }) + } + + pub async fn insert(&self, entry: &Entry) -> anyhow::Result<()> { + self.conn.lock().await.execute( + r" +INSERT INTO extension (publisher, name, url, platform, engine) +VALUES(?1, ?2, ?3, ?4, ?5); + ", + ( + &entry.publisher, + &entry.name, + &entry.url, + &entry.platform, + &entry.engine, + ), + )?; + Ok(()) + } + + pub async fn update_sha256(&self, url: &str, sha256: &str) -> anyhow::Result<()> { + self.conn.lock().await.execute( + r" +UPDATE extension SET sha256 = ?1 WHERE url = ?2; + ", + (url, sha256), + )?; + Ok(()) + } + + pub async fn query( + &self, + publisher: &str, + name: &str, + predicate: Option, + ) -> anyhow::Result> + where + F: Fn(&Entry) -> bool, + { + let conn = self.conn.lock().await; + + let mut stmt = conn + .prepare( + r" +SELECT publisher, name, url, sha256, platform, engine FROM extension +WHERE publisher = :publisher AND name = :name +", + ) + .unwrap(); + let iter = stmt.query_map(&[(":publisher", publisher), (":name", name)], |row| { + let entry = Entry { + publisher: row.get(0)?, + name: row.get(1)?, + url: row.get(2)?, + sha256: row.get(3)?, + platform: row.get(4)?, + engine: row.get(5)?, + }; + + if let Some(predicate) = &predicate { + if !predicate(&entry) { + return Err(rusqlite::Error::ExecuteReturnedResults); + } + } + + Ok(entry) + })?; + + let mut res = vec![]; + + for item in iter.flatten() { + res.push(item) + } + + Ok(res) + } +} diff --git a/src/main.rs b/src/main.rs index 0dfa608..c136d62 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,16 @@ pub mod code; pub mod config; +mod db; pub mod error; pub mod jinja; pub mod utils; +use std::collections::HashMap; + +use itertools::Itertools; +use jinja::NixContext; +use tokio::fs; use tracing::*; -use tracing_subscriber::{fmt, prelude::*, util::SubscriberInitExt, EnvFilter}; use clap::Parser; use config::Config; @@ -23,37 +28,44 @@ struct Args { output: Option, #[arg(long, hide = true)] export: bool, - #[arg(long, hide = true)] - dump: bool, - #[arg(long)] - openvsx: bool, } #[tokio::main] async fn main() -> anyhow::Result<()> { + init_logger(); let args = Args::parse(); - init_logger(); + let config = Config::from_file(&args.file).await?; + debug!(?config); - let config = Config::new(tokio::fs::read_to_string(&args.file).await?.as_str())?; - debug!("request: {config:?}"); let mut generator = Generator::new(); let mut code = CodeNix::new(config.clone()); - let res = code.get_extensions(generator.clone()).await; + let ctx = code.get_extensions(generator.clone()).await; + let mut ctx2 = HashMap::::new(); + for item in ctx { + ctx2.insert( + format!( + "{}-{}-{:?}", + item.publisher_name, item.extension_name, item.target_platform + ), + item, + ); + } + let ctx = ctx2.into_values().collect_vec(); + debug!("{ctx:#?}"); - debug!("{res:#?}"); if args.export { - let res = serde_json::to_string(&res).unwrap(); + let res = serde_json::to_string_pretty(&ctx)?; match args.output { - Some(filepath) => tokio::fs::write(filepath, res).await.unwrap(), + Some(filepath) => fs::write(filepath, res).await?, None => println!("{res}",), } return Ok(()); } let res = generator.render(&GeneratorContext { - extensions: res, + extensions: ctx, config: config.clone().into(), })?; @@ -66,6 +78,8 @@ async fn main() -> anyhow::Result<()> { } fn init_logger() { + use tracing_subscriber::{fmt, prelude::*, util::SubscriberInitExt, EnvFilter}; + let log_level = std::env::var("RUST_LOG") .unwrap_or("INFO".into()) .to_lowercase(); diff --git a/src/utils/cacher.rs b/src/utils/cacher.rs index 41b4aad..94f10e5 100644 --- a/src/utils/cacher.rs +++ b/src/utils/cacher.rs @@ -1,27 +1,24 @@ -use lazy_static::lazy_static; use redb::TableDefinition; -use std::path::PathBuf; +use std::{path::PathBuf, sync::LazyLock}; use tracing::*; use crate::error::Error; -lazy_static! { - pub static ref GLOBAL_CACHER: Cacher = { - let path = format!( - "{}/{}/{}", - std::env::var("HOME").unwrap(), - ".cache", - "nix4vscode" - ); - let path = PathBuf::from(path); - if !path.exists() { - std::fs::create_dir_all(path.clone()).unwrap(); - } - let path = path.join("cache.redb"); +pub static GLOBAL_CACHER: LazyLock = LazyLock::new(|| { + let path = format!( + "{}/{}/{}", + std::env::var("HOME").unwrap(), + ".cache", + "nix4vscode" + ); + let path = PathBuf::from(path); + if !path.exists() { + std::fs::create_dir_all(path.clone()).unwrap(); + } + let path = path.join("cache.redb"); - Cacher::new(path) - }; -} + Cacher::new(path) +}); static TABLE_SHA256: TableDefinition<&str, &str> = TableDefinition::new("SHA256"); static TABLE_HTTP_CLIENT: TableDefinition<&str, &str> = TableDefinition::new("HTTP_CLIENT");