Repetitive structs removed for import file extensions (#6211)
* get rid of repetitive structs Signed-off-by: Jess Frazelle <github@jessfraz.com> fmt Signed-off-by: Jess Frazelle <github@jessfraz.com> get rid of more Signed-off-by: Jess Frazelle <github@jessfraz.com> add more Signed-off-by: Jess Frazelle <github@jessfraz.com> updates Signed-off-by: Jess Frazelle <github@jessfraz.com> fix Signed-off-by: Jess Frazelle <github@jessfraz.com> updates Signed-off-by: Jess Frazelle <github@jessfraz.com> await the shit Signed-off-by: Jess Frazelle <github@jessfraz.com> updates Signed-off-by: Jess Frazelle <github@jessfraz.com> put it at the root Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix;es Signed-off-by: Jess Frazelle <github@jessfraz.com> * fixes Signed-off-by: Jess Frazelle <github@jessfraz.com> * fix Signed-off-by: Jess Frazelle <github@jessfraz.com> * kcl-language-server flake Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> --------- Signed-off-by: Jess Frazelle <github@jessfraz.com>
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@ -26,6 +26,7 @@ yarn-error.log*
|
|||||||
.idea
|
.idea
|
||||||
.vscode
|
.vscode
|
||||||
.helix
|
.helix
|
||||||
|
result
|
||||||
|
|
||||||
# rust
|
# rust
|
||||||
rust/target
|
rust/target
|
||||||
|
41
flake.lock
generated
41
flake.lock
generated
@ -1,6 +1,40 @@
|
|||||||
{
|
{
|
||||||
"nodes": {
|
"nodes": {
|
||||||
|
"naersk": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs"
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1743800763,
|
||||||
|
"narHash": "sha256-YFKV+fxEpMgP5VsUcM6Il28lI0NlpM7+oB1XxbBAYCw=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "naersk",
|
||||||
|
"rev": "ed0232117731a4c19d3ee93aa0c382a8fe754b01",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "naersk",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1744096231,
|
||||||
|
"narHash": "sha256-kUfx3FKU1Etnua3EaKvpeuXs7zoFiAcli1gBwkPvGSs=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "b2b0718004cc9a5bca610326de0a82e6ea75920b",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1736320768,
|
"lastModified": 1736320768,
|
||||||
"narHash": "sha256-nIYdTAiKIGnFNugbomgBJR+Xv5F1ZQU+HfaBqJKroC0=",
|
"narHash": "sha256-nIYdTAiKIGnFNugbomgBJR+Xv5F1ZQU+HfaBqJKroC0=",
|
||||||
@ -16,7 +50,7 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs_2": {
|
"nixpkgs_3": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1728538411,
|
"lastModified": 1728538411,
|
||||||
"narHash": "sha256-f0SBJz1eZ2yOuKUr5CA9BHULGXVSn6miBuUWdTyhUhU=",
|
"narHash": "sha256-f0SBJz1eZ2yOuKUr5CA9BHULGXVSn6miBuUWdTyhUhU=",
|
||||||
@ -34,13 +68,14 @@
|
|||||||
},
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": "nixpkgs",
|
"naersk": "naersk",
|
||||||
|
"nixpkgs": "nixpkgs_2",
|
||||||
"rust-overlay": "rust-overlay"
|
"rust-overlay": "rust-overlay"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"rust-overlay": {
|
"rust-overlay": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": "nixpkgs_2"
|
"nixpkgs": "nixpkgs_3"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1736476219,
|
"lastModified": 1736476219,
|
||||||
|
142
flake.nix
142
flake.nix
@ -1,84 +1,104 @@
|
|||||||
{
|
{
|
||||||
description = "modeling-app development environment";
|
description = "zoo.dev modeling-app";
|
||||||
|
|
||||||
# Flake inputs
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||||
rust-overlay.url = "github:oxalica/rust-overlay"; # A helper for Rust + Nix
|
rust-overlay.url = "github:oxalica/rust-overlay";
|
||||||
|
naersk.url = "github:nix-community/naersk";
|
||||||
};
|
};
|
||||||
|
|
||||||
# Flake outputs
|
outputs = {
|
||||||
outputs = { self, nixpkgs, rust-overlay }:
|
self,
|
||||||
let
|
nixpkgs,
|
||||||
# Overlays enable you to customize the Nixpkgs attribute set
|
rust-overlay,
|
||||||
overlays = [
|
naersk,
|
||||||
# Makes a `rust-bin` attribute available in Nixpkgs
|
}: let
|
||||||
(import rust-overlay)
|
overlays = [
|
||||||
# Provides a `rustToolchain` attribute for Nixpkgs that we can use to
|
(import rust-overlay)
|
||||||
# create a Rust environment
|
(self: super: {
|
||||||
(self: super: {
|
rustToolchain = super.rust-bin.stable.latest.default.override {
|
||||||
rustToolchain = super. rust-bin.stable.latest.default.override {
|
targets = ["wasm32-unknown-unknown"];
|
||||||
targets = [ "wasm32-unknown-unknown" ];
|
extensions = ["rustfmt" "llvm-tools-preview" "rust-src"];
|
||||||
extensions = [ "rustfmt" "llvm-tools-preview" "rust-src" ];
|
};
|
||||||
|
})
|
||||||
|
(self: super: {
|
||||||
|
cargo-llvm-cov = super.cargo-llvm-cov.overrideAttrs (oa: {
|
||||||
|
doCheck = false;
|
||||||
|
doInstallCheck = false;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
|
allSystems = [
|
||||||
|
"x86_64-linux"
|
||||||
|
"aarch64-linux"
|
||||||
|
"x86_64-darwin"
|
||||||
|
"aarch64-darwin"
|
||||||
|
];
|
||||||
|
|
||||||
|
forAllSystems = f:
|
||||||
|
nixpkgs.lib.genAttrs allSystems (system:
|
||||||
|
f {
|
||||||
|
pkgs = import nixpkgs {
|
||||||
|
inherit overlays system;
|
||||||
|
config.allowBroken = true;
|
||||||
};
|
};
|
||||||
})
|
system = system;
|
||||||
(self: super: {
|
});
|
||||||
cargo-llvm-cov = super.cargo-llvm-cov.overrideAttrs(oa: {
|
in {
|
||||||
doCheck = false; doInstallCheck = false;
|
devShells = forAllSystems ({pkgs, ...}: {
|
||||||
});
|
default = pkgs.mkShell {
|
||||||
})
|
packages =
|
||||||
];
|
(with pkgs; [
|
||||||
|
|
||||||
# Systems supported
|
|
||||||
allSystems = [
|
|
||||||
"x86_64-linux" # 64-bit Intel/AMD Linux
|
|
||||||
"aarch64-linux" # 64-bit ARM Linux
|
|
||||||
"x86_64-darwin" # 64-bit Intel macOS
|
|
||||||
"aarch64-darwin" # 64-bit ARM macOS
|
|
||||||
];
|
|
||||||
|
|
||||||
# Helper to provide system-specific attributes
|
|
||||||
forAllSystems = f: nixpkgs.lib.genAttrs allSystems (system: f {
|
|
||||||
pkgs = import nixpkgs { inherit overlays system; config.allowBroken = true; };
|
|
||||||
});
|
|
||||||
|
|
||||||
in
|
|
||||||
{
|
|
||||||
# Development environment output
|
|
||||||
devShells = forAllSystems ({ pkgs }: {
|
|
||||||
default = pkgs.mkShell {
|
|
||||||
# The Nix packages provided in the environment
|
|
||||||
packages = (with pkgs; [
|
|
||||||
# The package provided by our custom overlay. Includes cargo, Clippy, cargo-fmt,
|
|
||||||
# rustdoc, rustfmt, and other tools.
|
|
||||||
rustToolchain
|
rustToolchain
|
||||||
|
|
||||||
cargo-llvm-cov
|
cargo-llvm-cov
|
||||||
cargo-nextest
|
cargo-nextest
|
||||||
|
|
||||||
just
|
just
|
||||||
postgresql.lib
|
postgresql.lib
|
||||||
openssl
|
openssl
|
||||||
pkg-config
|
pkg-config
|
||||||
|
|
||||||
nodejs_22
|
nodejs_22
|
||||||
yarn
|
yarn
|
||||||
|
|
||||||
electron
|
electron
|
||||||
playwright-driver.browsers
|
playwright-driver.browsers
|
||||||
]) ++ pkgs.lib.optionals pkgs.stdenv.isDarwin (with pkgs; [
|
wasm-pack
|
||||||
|
python3Full
|
||||||
|
])
|
||||||
|
++ pkgs.lib.optionals pkgs.stdenv.isDarwin (with pkgs; [
|
||||||
libiconv
|
libiconv
|
||||||
darwin.apple_sdk.frameworks.Security
|
darwin.apple_sdk.frameworks.Security
|
||||||
]);
|
]);
|
||||||
|
|
||||||
TARGET_CC = "${pkgs.stdenv.cc}/bin/${pkgs.stdenv.cc.targetPrefix}cc";
|
TARGET_CC = "${pkgs.stdenv.cc}/bin/${pkgs.stdenv.cc.targetPrefix}cc";
|
||||||
LIBCLANG_PATH = "${pkgs.libclang.lib}/lib";
|
LIBCLANG_PATH = "${pkgs.libclang.lib}/lib";
|
||||||
ELECTRON_OVERRIDE_DIST_PATH = "${pkgs.electron}/bin/";
|
ELECTRON_OVERRIDE_DIST_PATH = "${pkgs.electron}/bin/";
|
||||||
PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS = true;
|
PLAYWRIGHT_SKIP_VALIDATE_HOST_REQUIREMENTS = true;
|
||||||
PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH = "${pkgs.playwright-driver.browsers}/chromium-1091/chrome-linux/chrome";
|
PLAYWRIGHT_CHROMIUM_EXECUTABLE_PATH = "${pkgs.playwright-driver.browsers}/chromium-1091/chrome-linux/chrome";
|
||||||
PLAYWRIGHT_BROWSERS_PATH = "${pkgs.playwright-driver.browsers}";
|
PLAYWRIGHT_BROWSERS_PATH = "${pkgs.playwright-driver.browsers}";
|
||||||
NODE_ENV = "development";
|
NODE_ENV = "development";
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
};
|
|
||||||
|
packages = forAllSystems ({
|
||||||
|
pkgs,
|
||||||
|
system,
|
||||||
|
}: let
|
||||||
|
naersk-lib = pkgs.callPackage naersk {
|
||||||
|
cargo = pkgs.rustToolchain;
|
||||||
|
rustc = pkgs.rustToolchain;
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
kcl-language-server = naersk-lib.buildPackage {
|
||||||
|
pname = "kcl-language-server";
|
||||||
|
version = "0.1.0";
|
||||||
|
release = true;
|
||||||
|
|
||||||
|
src = ./rust;
|
||||||
|
|
||||||
|
cargoBuildOptions = opt: opt ++ ["-p" "kcl-language-server"];
|
||||||
|
buildInputs = [pkgs.openssl pkgs.pkg-config];
|
||||||
|
};
|
||||||
|
default = self.packages.${system}.kcl-language-server;
|
||||||
|
});
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,7 @@ tokio-tungstenite = { version = "0.24.0", features = [
|
|||||||
tower-lsp = { workspace = true, features = ["proposed", "default"] }
|
tower-lsp = { workspace = true, features = ["proposed", "default"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["engine"]
|
default = ["cli", "engine"]
|
||||||
cli = ["dep:clap", "kittycad/clap"]
|
cli = ["dep:clap", "kittycad/clap"]
|
||||||
dhat-heap = ["dep:dhat"]
|
dhat-heap = ["dep:dhat"]
|
||||||
# For the lsp server, when run with stdout for rpc we want to disable println.
|
# For the lsp server, when run with stdout for rpc we want to disable println.
|
||||||
|
@ -20,7 +20,6 @@ pub(crate) const SETTINGS_UNIT_ANGLE: &str = "defaultAngleUnit";
|
|||||||
pub(super) const NO_PRELUDE: &str = "no_std";
|
pub(super) const NO_PRELUDE: &str = "no_std";
|
||||||
|
|
||||||
pub(super) const IMPORT_FORMAT: &str = "format";
|
pub(super) const IMPORT_FORMAT: &str = "format";
|
||||||
pub(super) const IMPORT_FORMAT_VALUES: [&str; 9] = ["fbx", "gltf", "glb", "obj", "ply", "sldprt", "stp", "step", "stl"];
|
|
||||||
pub(super) const IMPORT_COORDS: &str = "coords";
|
pub(super) const IMPORT_COORDS: &str = "coords";
|
||||||
pub(super) const IMPORT_COORDS_VALUES: [(&str, &System); 3] =
|
pub(super) const IMPORT_COORDS_VALUES: [(&str, &System); 3] =
|
||||||
[("zoo", KITTYCAD), ("opengl", OPENGL), ("vulkan", VULKAN)];
|
[("zoo", KITTYCAD), ("opengl", OPENGL), ("vulkan", VULKAN)];
|
||||||
|
@ -173,7 +173,7 @@ pub(super) fn format_from_annotations(
|
|||||||
KclError::Semantic(KclErrorDetails {
|
KclError::Semantic(KclErrorDetails {
|
||||||
message: format!(
|
message: format!(
|
||||||
"Unknown format for import, expected one of: {}",
|
"Unknown format for import, expected one of: {}",
|
||||||
annotations::IMPORT_FORMAT_VALUES.join(", ")
|
crate::IMPORT_FILE_EXTENSIONS.join(", ")
|
||||||
),
|
),
|
||||||
source_ranges: vec![p.as_source_range()],
|
source_ranges: vec![p.as_source_range()],
|
||||||
})
|
})
|
||||||
|
@ -131,11 +131,36 @@ pub mod pretty {
|
|||||||
pub use crate::{parsing::token::NumericSuffix, unparser::format_number};
|
pub use crate::{parsing::token::NumericSuffix, unparser::format_number};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "cli")]
|
||||||
|
use clap::ValueEnum;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use crate::log::{log, logln};
|
use crate::log::{log, logln};
|
||||||
|
|
||||||
|
lazy_static::lazy_static! {
|
||||||
|
|
||||||
|
pub static ref IMPORT_FILE_EXTENSIONS: Vec<String> = {
|
||||||
|
let mut import_file_extensions = vec!["stp".to_string(), "glb".to_string(), "fbxb".to_string()];
|
||||||
|
#[cfg(feature = "cli")]
|
||||||
|
let named_extensions = kittycad::types::FileImportFormat::value_variants()
|
||||||
|
.iter()
|
||||||
|
.map(|x| format!("{}", x))
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
#[cfg(not(feature = "cli"))]
|
||||||
|
let named_extensions = vec![]; // We don't really need this outside of the CLI.
|
||||||
|
// Add all the default import formats.
|
||||||
|
import_file_extensions.extend_from_slice(&named_extensions);
|
||||||
|
import_file_extensions
|
||||||
|
};
|
||||||
|
|
||||||
|
pub static ref RELEVANT_FILE_EXTENSIONS: Vec<String> = {
|
||||||
|
let mut relevant_extensions = IMPORT_FILE_EXTENSIONS.clone();
|
||||||
|
relevant_extensions.push("kcl".to_string());
|
||||||
|
relevant_extensions
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Program {
|
pub struct Program {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
|
@ -35,7 +35,7 @@ use crate::{
|
|||||||
token::{Token, TokenSlice, TokenType},
|
token::{Token, TokenSlice, TokenType},
|
||||||
PIPE_OPERATOR, PIPE_SUBSTITUTION_OPERATOR,
|
PIPE_OPERATOR, PIPE_SUBSTITUTION_OPERATOR,
|
||||||
},
|
},
|
||||||
SourceRange,
|
SourceRange, IMPORT_FILE_EXTENSIONS,
|
||||||
};
|
};
|
||||||
|
|
||||||
thread_local! {
|
thread_local! {
|
||||||
@ -1843,8 +1843,6 @@ fn import_stmt(i: &mut TokenSlice) -> PResult<BoxNode<ImportStatement>> {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
const FOREIGN_IMPORT_EXTENSIONS: [&str; 8] = ["fbx", "gltf", "glb", "obj", "ply", "sldprt", "step", "stl"];
|
|
||||||
|
|
||||||
/// Validates the path string in an `import` statement.
|
/// Validates the path string in an `import` statement.
|
||||||
///
|
///
|
||||||
/// `var_name` is `true` if the path will be used as a variable name.
|
/// `var_name` is `true` if the path will be used as a variable name.
|
||||||
@ -1909,12 +1907,11 @@ fn validate_path_string(path_string: String, var_name: bool, path_range: SourceR
|
|||||||
|
|
||||||
ImportPath::Std { path: segments }
|
ImportPath::Std { path: segments }
|
||||||
} else if path_string.contains('.') {
|
} else if path_string.contains('.') {
|
||||||
// TODO should allow other extensions if there is a format attribute.
|
let extn = std::path::Path::new(&path_string).extension().unwrap_or_default();
|
||||||
let extn = &path_string[path_string.rfind('.').unwrap() + 1..];
|
if !IMPORT_FILE_EXTENSIONS.contains(&extn.to_string_lossy().to_string()) {
|
||||||
if !FOREIGN_IMPORT_EXTENSIONS.contains(&extn) {
|
|
||||||
ParseContext::warn(CompilationError::err(
|
ParseContext::warn(CompilationError::err(
|
||||||
path_range,
|
path_range,
|
||||||
format!("unsupported import path format. KCL files can be imported from the current project, CAD files with the following formats are supported: {}", FOREIGN_IMPORT_EXTENSIONS.join(", ")),
|
format!("unsupported import path format. KCL files can be imported from the current project, CAD files with the following formats are supported: {}", IMPORT_FILE_EXTENSIONS.join(", ")),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
ImportPath::Foreign { path: path_string }
|
ImportPath::Foreign { path: path_string }
|
||||||
@ -1922,7 +1919,7 @@ fn validate_path_string(path_string: String, var_name: bool, path_range: SourceR
|
|||||||
return Err(ErrMode::Cut(
|
return Err(ErrMode::Cut(
|
||||||
CompilationError::fatal(
|
CompilationError::fatal(
|
||||||
path_range,
|
path_range,
|
||||||
format!("unsupported import path format. KCL files can be imported from the current project, CAD files with the following formats are supported: {}", FOREIGN_IMPORT_EXTENSIONS.join(", ")),
|
format!("unsupported import path format. KCL files can be imported from the current project, CAD files with the following formats are supported: {}", IMPORT_FILE_EXTENSIONS.join(", ")),
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
));
|
));
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
use std::fmt::Write;
|
use std::fmt::Write;
|
||||||
|
|
||||||
#[cfg(feature = "cli")]
|
|
||||||
use clap::ValueEnum;
|
|
||||||
|
|
||||||
use crate::parsing::{
|
use crate::parsing::{
|
||||||
ast::types::{
|
ast::types::{
|
||||||
Annotation, ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem,
|
Annotation, ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem,
|
||||||
@ -867,29 +864,6 @@ impl Parameter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static::lazy_static! {
|
|
||||||
|
|
||||||
pub static ref IMPORT_FILE_EXTENSIONS: Vec<String> = {
|
|
||||||
let mut import_file_extensions = vec!["stp".to_string(), "glb".to_string(), "fbxb".to_string()];
|
|
||||||
#[cfg(feature = "cli")]
|
|
||||||
let named_extensions = kittycad::types::FileImportFormat::value_variants()
|
|
||||||
.iter()
|
|
||||||
.map(|x| format!("{}", x))
|
|
||||||
.collect::<Vec<String>>();
|
|
||||||
#[cfg(not(feature = "cli"))]
|
|
||||||
let named_extensions = vec![]; // We don't really need this outside of the CLI.
|
|
||||||
// Add all the default import formats.
|
|
||||||
import_file_extensions.extend_from_slice(&named_extensions);
|
|
||||||
import_file_extensions
|
|
||||||
};
|
|
||||||
|
|
||||||
pub static ref RELEVANT_EXTENSIONS: Vec<String> = {
|
|
||||||
let mut relevant_extensions = IMPORT_FILE_EXTENSIONS.clone();
|
|
||||||
relevant_extensions.push("kcl".to_string());
|
|
||||||
relevant_extensions
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Collect all the kcl (and other relevant) files in a directory, recursively.
|
/// Collect all the kcl (and other relevant) files in a directory, recursively.
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
#[async_recursion::async_recursion]
|
#[async_recursion::async_recursion]
|
||||||
@ -909,7 +883,7 @@ pub async fn walk_dir(dir: &std::path::PathBuf) -> Result<Vec<std::path::PathBuf
|
|||||||
files.extend(walk_dir(&path).await?);
|
files.extend(walk_dir(&path).await?);
|
||||||
} else if path
|
} else if path
|
||||||
.extension()
|
.extension()
|
||||||
.is_some_and(|ext| RELEVANT_EXTENSIONS.contains(&ext.to_string_lossy().to_string()))
|
.is_some_and(|ext| crate::RELEVANT_FILE_EXTENSIONS.contains(&ext.to_string_lossy().to_string()))
|
||||||
{
|
{
|
||||||
files.push(path);
|
files.push(path);
|
||||||
}
|
}
|
||||||
|
@ -292,3 +292,22 @@ pub fn get_kcl_version() -> String {
|
|||||||
|
|
||||||
kcl_lib::version().to_string()
|
kcl_lib::version().to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the allowed import file extensions.
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn import_file_extensions() -> Result<Vec<String>, String> {
|
||||||
|
console_error_panic_hook::set_once();
|
||||||
|
|
||||||
|
Ok(kcl_lib::IMPORT_FILE_EXTENSIONS.iter().map(|s| s.to_string()).collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the allowed relevant file extensions (imports + kcl).
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn relevant_file_extensions() -> Result<Vec<String>, String> {
|
||||||
|
console_error_panic_hook::set_once();
|
||||||
|
|
||||||
|
Ok(kcl_lib::RELEVANT_FILE_EXTENSIONS
|
||||||
|
.iter()
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
.collect::<Vec<String>>())
|
||||||
|
}
|
||||||
|
12
src/lang/wasmUtils.ts
Normal file
12
src/lang/wasmUtils.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import {
|
||||||
|
import_file_extensions,
|
||||||
|
relevant_file_extensions,
|
||||||
|
} from '@rust/kcl-wasm-lib/pkg/kcl_wasm_lib'
|
||||||
|
|
||||||
|
export function importFileExtensions(): string[] {
|
||||||
|
return import_file_extensions()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function relevantFileExtensions(): string[] {
|
||||||
|
return relevant_file_extensions()
|
||||||
|
}
|
@ -1,5 +1,4 @@
|
|||||||
import type { Models } from '@kittycad/lib/dist/types/src'
|
import type { Models } from '@kittycad/lib/dist/types/src'
|
||||||
import type { FileImportFormat_type } from '@kittycad/lib/dist/types/src/models'
|
|
||||||
|
|
||||||
import type { UnitAngle, UnitLength } from '@rust/kcl-lib/bindings/ModelingCmd'
|
import type { UnitAngle, UnitLength } from '@rust/kcl-lib/bindings/ModelingCmd'
|
||||||
|
|
||||||
@ -36,29 +35,6 @@ export const PROJECT_IMAGE_NAME = `thumbnail.png`
|
|||||||
export const FILE_PERSIST_KEY = `${PROJECT_FOLDER}-last-opened` as const
|
export const FILE_PERSIST_KEY = `${PROJECT_FOLDER}-last-opened` as const
|
||||||
/** The default name given to new kcl files in a project */
|
/** The default name given to new kcl files in a project */
|
||||||
export const DEFAULT_FILE_NAME = 'Untitled'
|
export const DEFAULT_FILE_NAME = 'Untitled'
|
||||||
/** The file endings that will appear in
|
|
||||||
* the file explorer if found in a project directory */
|
|
||||||
// TODO: make stp part of this enum as an alias to step
|
|
||||||
// TODO: make glb part of this enum as it is in fact supported
|
|
||||||
export type NativeFileType = 'kcl'
|
|
||||||
export type RelevantFileType =
|
|
||||||
| FileImportFormat_type
|
|
||||||
| NativeFileType
|
|
||||||
| 'stp'
|
|
||||||
| 'glb'
|
|
||||||
export const NATIVE_FILE_TYPE: NativeFileType = 'kcl'
|
|
||||||
export const RELEVANT_FILE_TYPES: RelevantFileType[] = [
|
|
||||||
'kcl',
|
|
||||||
'fbx',
|
|
||||||
'gltf',
|
|
||||||
'glb',
|
|
||||||
'obj',
|
|
||||||
'ply',
|
|
||||||
'sldprt',
|
|
||||||
'stp',
|
|
||||||
'step',
|
|
||||||
'stl',
|
|
||||||
] as const
|
|
||||||
/** The default name for a tutorial project */
|
/** The default name for a tutorial project */
|
||||||
export const ONBOARDING_PROJECT_NAME = 'Tutorial Project $nn'
|
export const ONBOARDING_PROJECT_NAME = 'Tutorial Project $nn'
|
||||||
/**
|
/**
|
||||||
|
@ -2,9 +2,14 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|||||||
|
|
||||||
import type { Configuration } from '@rust/kcl-lib/bindings/Configuration'
|
import type { Configuration } from '@rust/kcl-lib/bindings/Configuration'
|
||||||
|
|
||||||
import { isRelevantFile, listProjects } from '@src/lib/desktop'
|
import { initPromise } from '@src/lang/wasm'
|
||||||
|
import { listProjects } from '@src/lib/desktop'
|
||||||
import type { DeepPartial } from '@src/lib/types'
|
import type { DeepPartial } from '@src/lib/types'
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
await initPromise
|
||||||
|
})
|
||||||
|
|
||||||
// Mock the electron window global
|
// Mock the electron window global
|
||||||
const mockElectron = {
|
const mockElectron = {
|
||||||
readdir: vi.fn(),
|
readdir: vi.fn(),
|
||||||
@ -112,41 +117,6 @@ describe('desktop utilities', () => {
|
|||||||
mockElectron.kittycad.mockResolvedValue({})
|
mockElectron.kittycad.mockResolvedValue({})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('isRelevantFile', () => {
|
|
||||||
it('finds supported extension files relevant', () => {
|
|
||||||
expect(isRelevantFile('part.kcl')).toEqual(true)
|
|
||||||
expect(isRelevantFile('part.fbx')).toEqual(true)
|
|
||||||
expect(isRelevantFile('part.gltf')).toEqual(true)
|
|
||||||
expect(isRelevantFile('part.glb')).toEqual(true)
|
|
||||||
expect(isRelevantFile('part.obj')).toEqual(true)
|
|
||||||
expect(isRelevantFile('part.ply')).toEqual(true)
|
|
||||||
expect(isRelevantFile('part.sldprt')).toEqual(true)
|
|
||||||
expect(isRelevantFile('part.stp')).toEqual(true)
|
|
||||||
expect(isRelevantFile('part.step')).toEqual(true)
|
|
||||||
expect(isRelevantFile('part.stl')).toEqual(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
// TODO: we should be lowercasing the extension here to check. .sldprt or .SLDPRT should be supported
|
|
||||||
// But the api doesn't allow it today, so revisit this and the tests once this is done
|
|
||||||
it('finds (now) supported uppercase extension files *not* relevant', () => {
|
|
||||||
expect(isRelevantFile('part.KCL')).toEqual(false)
|
|
||||||
expect(isRelevantFile('part.FBX')).toEqual(false)
|
|
||||||
expect(isRelevantFile('part.GLTF')).toEqual(false)
|
|
||||||
expect(isRelevantFile('part.GLB')).toEqual(false)
|
|
||||||
expect(isRelevantFile('part.OBJ')).toEqual(false)
|
|
||||||
expect(isRelevantFile('part.PLY')).toEqual(false)
|
|
||||||
expect(isRelevantFile('part.SLDPRT')).toEqual(false)
|
|
||||||
expect(isRelevantFile('part.STP')).toEqual(false)
|
|
||||||
expect(isRelevantFile('part.STEP')).toEqual(false)
|
|
||||||
expect(isRelevantFile('part.STL')).toEqual(false)
|
|
||||||
})
|
|
||||||
|
|
||||||
it("doesn't find .docx or .SLDASM relevant", () => {
|
|
||||||
expect(isRelevantFile('paper.docx')).toEqual(false)
|
|
||||||
expect(isRelevantFile('assembly.SLDASM')).toEqual(false)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('listProjects', () => {
|
describe('listProjects', () => {
|
||||||
it('does not list .git directories', async () => {
|
it('does not list .git directories', async () => {
|
||||||
const projects = await listProjects(mockConfig)
|
const projects = await listProjects(mockConfig)
|
||||||
|
@ -10,13 +10,13 @@ import {
|
|||||||
parseAppSettings,
|
parseAppSettings,
|
||||||
parseProjectSettings,
|
parseProjectSettings,
|
||||||
} from '@src/lang/wasm'
|
} from '@src/lang/wasm'
|
||||||
|
import { relevantFileExtensions } from '@src/lang/wasmUtils'
|
||||||
import {
|
import {
|
||||||
DEFAULT_DEFAULT_LENGTH_UNIT,
|
DEFAULT_DEFAULT_LENGTH_UNIT,
|
||||||
PROJECT_ENTRYPOINT,
|
PROJECT_ENTRYPOINT,
|
||||||
PROJECT_FOLDER,
|
PROJECT_FOLDER,
|
||||||
PROJECT_IMAGE_NAME,
|
PROJECT_IMAGE_NAME,
|
||||||
PROJECT_SETTINGS_FILE_NAME,
|
PROJECT_SETTINGS_FILE_NAME,
|
||||||
RELEVANT_FILE_TYPES,
|
|
||||||
SETTINGS_FILE_NAME,
|
SETTINGS_FILE_NAME,
|
||||||
TELEMETRY_FILE_NAME,
|
TELEMETRY_FILE_NAME,
|
||||||
TELEMETRY_RAW_FILE_NAME,
|
TELEMETRY_RAW_FILE_NAME,
|
||||||
@ -201,15 +201,13 @@ export async function listProjects(
|
|||||||
return projects
|
return projects
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: we should be lowercasing the extension here to check. .sldprt or .SLDPRT should be supported
|
|
||||||
// But the api doesn't allow it today, so revisit this and the tests once this is done
|
|
||||||
export const isRelevantFile = (filename: string): boolean =>
|
|
||||||
RELEVANT_FILE_TYPES.some((ext) => filename.endsWith('.' + ext))
|
|
||||||
|
|
||||||
const collectAllFilesRecursiveFrom = async (
|
const collectAllFilesRecursiveFrom = async (
|
||||||
path: string,
|
path: string,
|
||||||
canReadWritePath: boolean
|
canReadWritePath: boolean
|
||||||
) => {
|
) => {
|
||||||
|
const RELEVANT_FILE_EXTENSIONS = relevantFileExtensions()
|
||||||
|
const isRelevantFile = (filename: string): boolean =>
|
||||||
|
RELEVANT_FILE_EXTENSIONS.some((ext) => filename.endsWith('.' + ext))
|
||||||
// Make sure the filesystem object exists.
|
// Make sure the filesystem object exists.
|
||||||
try {
|
try {
|
||||||
await window.electron.stat(path)
|
await window.electron.stat(path)
|
||||||
|
@ -3,8 +3,13 @@ import os from 'os'
|
|||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { v4 as uuidv4 } from 'uuid'
|
import { v4 as uuidv4 } from 'uuid'
|
||||||
|
|
||||||
|
import { initPromise } from '@src/lang/wasm'
|
||||||
import getCurrentProjectFile from '@src/lib/getCurrentProjectFile'
|
import getCurrentProjectFile from '@src/lib/getCurrentProjectFile'
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
await initPromise
|
||||||
|
})
|
||||||
|
|
||||||
describe('getCurrentProjectFile', () => {
|
describe('getCurrentProjectFile', () => {
|
||||||
test('with explicit open file with space (URL encoded)', async () => {
|
test('with explicit open file with space (URL encoded)', async () => {
|
||||||
const name = `kittycad-modeling-projects-${uuidv4()}`
|
const name = `kittycad-modeling-projects-${uuidv4()}`
|
||||||
|
@ -1,17 +1,12 @@
|
|||||||
|
import {
|
||||||
|
importFileExtensions,
|
||||||
|
relevantFileExtensions,
|
||||||
|
} from '@src/lang/wasmUtils'
|
||||||
import type { Stats } from 'fs'
|
import type { Stats } from 'fs'
|
||||||
import * as fs from 'fs/promises'
|
import * as fs from 'fs/promises'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
|
|
||||||
import {
|
import { PROJECT_ENTRYPOINT } from '@src/lib/constants'
|
||||||
NATIVE_FILE_TYPE,
|
|
||||||
PROJECT_ENTRYPOINT,
|
|
||||||
RELEVANT_FILE_TYPES,
|
|
||||||
type RelevantFileType,
|
|
||||||
} from '@src/lib/constants'
|
|
||||||
|
|
||||||
const shouldWrapExtension = (extension: string) =>
|
|
||||||
RELEVANT_FILE_TYPES.includes(extension as RelevantFileType) &&
|
|
||||||
extension !== NATIVE_FILE_TYPE
|
|
||||||
|
|
||||||
/// Get the current project file from the path.
|
/// Get the current project file from the path.
|
||||||
/// This is used for double-clicking on a file in the file explorer,
|
/// This is used for double-clicking on a file in the file explorer,
|
||||||
@ -19,6 +14,12 @@ const shouldWrapExtension = (extension: string) =>
|
|||||||
export default async function getCurrentProjectFile(
|
export default async function getCurrentProjectFile(
|
||||||
pathString: string
|
pathString: string
|
||||||
): Promise<string | Error> {
|
): Promise<string | Error> {
|
||||||
|
// Extract the values into an array
|
||||||
|
const allFileImportFormats: string[] = importFileExtensions()
|
||||||
|
const relevantExtensions: string[] = relevantFileExtensions()
|
||||||
|
const shouldWrapExtension = (extension: string) =>
|
||||||
|
allFileImportFormats.includes(extension)
|
||||||
|
|
||||||
// Fix for "." path, which is the current directory.
|
// Fix for "." path, which is the current directory.
|
||||||
let sourcePath = pathString === '.' ? process.cwd() : pathString
|
let sourcePath = pathString === '.' ? process.cwd() : pathString
|
||||||
|
|
||||||
@ -71,12 +72,9 @@ export default async function getCurrentProjectFile(
|
|||||||
// Check if the extension on what we are trying to open is a relevant file type.
|
// Check if the extension on what we are trying to open is a relevant file type.
|
||||||
const extension = path.extname(sourcePath).slice(1).toLowerCase()
|
const extension = path.extname(sourcePath).slice(1).toLowerCase()
|
||||||
|
|
||||||
if (
|
if (!relevantExtensions.includes(extension) && extension !== 'toml') {
|
||||||
!RELEVANT_FILE_TYPES.includes(extension as RelevantFileType) &&
|
|
||||||
extension !== 'toml'
|
|
||||||
) {
|
|
||||||
return new Error(
|
return new Error(
|
||||||
`File type (${extension}) cannot be opened with this app: '${sourcePath}', try opening one of the following file types: ${RELEVANT_FILE_TYPES.join(
|
`File type (${extension}) cannot be opened with this app: '${sourcePath}', try opening one of the following file types: ${relevantExtensions.join(
|
||||||
', '
|
', '
|
||||||
)}`
|
)}`
|
||||||
)
|
)
|
||||||
|
@ -15,6 +15,7 @@ import type {
|
|||||||
format_number as FormatNumber,
|
format_number as FormatNumber,
|
||||||
get_kcl_version as GetKclVersion,
|
get_kcl_version as GetKclVersion,
|
||||||
get_tangential_arc_to_info as GetTangentialArcToInfo,
|
get_tangential_arc_to_info as GetTangentialArcToInfo,
|
||||||
|
import_file_extensions as ImportFileExtensions,
|
||||||
is_kcl_empty_or_only_settings as IsKclEmptyOrOnlySettings,
|
is_kcl_empty_or_only_settings as IsKclEmptyOrOnlySettings,
|
||||||
is_points_ccw as IsPointsCcw,
|
is_points_ccw as IsPointsCcw,
|
||||||
kcl_lint as KclLint,
|
kcl_lint as KclLint,
|
||||||
@ -23,6 +24,7 @@ import type {
|
|||||||
parse_project_settings as ParseProjectSettings,
|
parse_project_settings as ParseProjectSettings,
|
||||||
parse_wasm as ParseWasm,
|
parse_wasm as ParseWasm,
|
||||||
recast_wasm as RecastWasm,
|
recast_wasm as RecastWasm,
|
||||||
|
relevant_file_extensions as RelevantFileExtensions,
|
||||||
serialize_configuration as SerializeConfiguration,
|
serialize_configuration as SerializeConfiguration,
|
||||||
serialize_project_configuration as SerializeProjectConfiguration,
|
serialize_project_configuration as SerializeProjectConfiguration,
|
||||||
} from '@rust/kcl-wasm-lib/pkg/kcl_wasm_lib'
|
} from '@rust/kcl-wasm-lib/pkg/kcl_wasm_lib'
|
||||||
@ -111,3 +113,9 @@ export const serialize_project_configuration: typeof SerializeProjectConfigurati
|
|||||||
(...args) => {
|
(...args) => {
|
||||||
return getModule().serialize_project_configuration(...args)
|
return getModule().serialize_project_configuration(...args)
|
||||||
}
|
}
|
||||||
|
export const import_file_extensions: typeof ImportFileExtensions = () => {
|
||||||
|
return getModule().import_file_extensions()
|
||||||
|
}
|
||||||
|
export const relevant_file_extensions: typeof RelevantFileExtensions = () => {
|
||||||
|
return getModule().relevant_file_extensions()
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user