From 738dda4502227a25af3e91289292575d04388b5b Mon Sep 17 00:00:00 2001 From: Marat Dulin Date: Thu, 21 Mar 2024 00:24:33 +0100 Subject: [PATCH] feat: add url support for applescript, fix examples in readme --- Cargo.lock | 2 +- Cargo.toml | 2 +- Makefile | 2 +- README.md | 60 ++++++++++--------- src/lib.rs | 136 +------------------------------------------- src/value/output.rs | 23 ++++++++ 6 files changed, 59 insertions(+), 166 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bf1a6dd..35b46c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -61,7 +61,7 @@ checksum = "2ff06a6505cde0766484f38d8479ac8e6d31c66fbc2d5492f65ca8c091456379" [[package]] name = "osakit" -version = "0.1.2" +version = "0.2.0" dependencies = [ "icrate", "serde", diff --git a/Cargo.toml b/Cargo.toml index 0a91506..42ceaac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "osakit" -version = "0.1.2" +version = "0.2.0" edition = "2021" authors = ["Marat Dulin "] description = "OSAKit macOS Framework adapted for Rust" diff --git a/Makefile b/Makefile index dd62c71..3dac8d7 100644 --- a/Makefile +++ b/Makefile @@ -40,6 +40,6 @@ lint-fix: cargo fmt test-watch: - cargo bin cargo-watch -x "test --all-features --no-fail-fast $T" + cargo bin cargo-watch -x "bin cargo-nextest run --all-features --no-fail-fast $T" pre-commit: test lint diff --git a/README.md b/README.md index 38d169a..2536b9f 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,8 @@ Input and output data are represented using `Value` from Comes with `declare_script!` macro (unstable) to simplify working with `OSAKit Framework`. +[Source code on GitHub](https://github.com/mdevils/rust-osakit) + ## Installation Add `osakit` to the dependencies. Specify `"full"` feature if you want to use `declare_script` @@ -26,7 +28,8 @@ osakit = { version = "0.1.0", features = ["full"] } ```rust use serde::{Deserialize, Serialize}; use osakit::declare_script; - +use std::error::Error; + declare_script! { #[language(JavaScript)] #[source(" @@ -57,25 +60,25 @@ struct User { id: u16, name: String, } - -#[test] -fn it_runs_my_js_script() { + +fn main() -> Result<(), Box> { let script = MyJsScript::new().unwrap(); assert_eq!( script.multiply(3, 2).unwrap(), 6 ); assert_eq!( - script.concat("Hello, ", "World").unwrap(), + script.concat("Hello, ", "World")?, "Hello, World" ); assert_eq!( - script.current_user().unwrap(), + script.current_user()?, User { id: 21, name: "root".into() } ); + Ok(()) } ``` @@ -83,43 +86,39 @@ fn it_runs_my_js_script() { ```rust use osakit::{Language, Map, Script, Value, Number}; - -#[test] -fn it_constructs_and_executes_scripts() { - let mut script = Script::new_from_source( - Language::AppleScript, " - on launch_terminal() - tell application \"Terminal\" to launch - end launch_terminal - +use std::error::Error; + +fn main() -> Result<(), Box> { + let mut script = Script::new_from_source(Language::AppleScript, " + on is_app_running() + tell application \"Hopefully Non-Existing Application\" to running + end is_app_running on concat(x, y) return x & y end concat - return {id: 21, name: \"root\"} "); - - script.compile().unwrap(); - + script.compile()?; assert_eq!( - script.execute().unwrap(), + script.execute()?, Value::Object(Map::from_iter(vec![ ("id".into(), Value::Number(Number::from(21))), ("name".into(), Value::String("root".into())) ])) ); - assert_eq!( - script.execute_function("concat", &vec![ + script.execute_function("concat", vec![ Value::String("Hello, ".into()), Value::String("World!".into()) - ]).unwrap(), + ])?, Value::String("Hello, World!".into()) ); - - assert!( - script.execute_function("launch_terminal", &vec![]).is_ok() + assert_eq!( + script.execute_function("is_app_running", vec![])?, + Value::Bool(false) ); + + Ok(()) } ``` @@ -127,6 +126,11 @@ fn it_constructs_and_executes_scripts() { See [Full Documentation](https://docs.rs/osakit/). +## Limitations + +Due to limitations on `OSAKit Framework`-side integer values returned from `JavaScript` code +are limited to `i32` type. + ## Supported platforms Due to the fact that OSAKit is Mac-specific, only `macOS` is supported. @@ -136,9 +140,9 @@ Due to the fact that OSAKit is Mac-specific, only `macOS` is supported. Licensed under either of * Apache License, Version 2.0 - ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + ([LICENSE-APACHE](LICENSE-APACHE) or ) * MIT license - ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) + ([LICENSE-MIT](LICENSE-MIT) or ) at your option. diff --git a/src/lib.rs b/src/lib.rs index 19a5dfe..92d4c03 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,138 +1,4 @@ -//! `osakit` aims to provide direct access to `OSAKit Framework` of macOS. Is uses ObjC-bindings -//! to access OSAKit and run both `AppleScript` and `JavaScript`. -//! -//! `osakit` is built using [`serde`](https://crates.io/crates/serde) for input-output -//! serialization/deserialization. -//! Allows passing data to `JavaScript`/`AppleScript` functions and returns back the results. -//! Input and output data are represented using `Value` from [`serde_json`]. -//! -//! Comes with [`declare_script!`] macro (unstable) to simplify -//! working with `OSAKit Framework`. -//! -//! [Source code on GitHub](https://github.com/mdevils/rust-osakit) -//! -//! ## Installation -//! -//! Add `osakit` to the dependencies. Specify `"full"` feature if you want to use `declare_script` -//! macro or `"stable"` feature to only include stable API. -//! -//! ```toml -//! [dependencies] -//! osakit = { version = "0.1.2", features = ["full"] } -//! ``` -//! -//! ## Example using `declare_script` -//! -//! ``` -//! use serde::{Deserialize, Serialize}; -//! use osakit::declare_script; -//! -//! declare_script! { -//! #[language(JavaScript)] -//! #[source(" -//! function concat(x, y) { -//! return x + y; -//! } -//! -//! function multiply(a, b) { -//! return a * b; -//! } -//! -//! function current_user() { -//! return { -//! id: 21, -//! name: \"root\" -//! }; -//! } -//! ")] -//! MyJsScript { -//! fn concat(x: &str, y: &str) -> String; -//! fn multiply(a: i32, b: i32) -> i32; -//! fn current_user() -> User; -//! } -//! } -//! -//! #[derive(Deserialize, Serialize, Debug, PartialEq, Eq)] -//! struct User { -//! id: u16, -//! name: String, -//! } -//! -//! # use std::error::Error; -//! # fn main() -> Result<(), Box> { -//! # -//! let script = MyJsScript::new()?; -//! assert_eq!( -//! script.multiply(3, 2)?, -//! 6 -//! ); -//! assert_eq!( -//! script.concat("Hello, ", "World")?, -//! "Hello, World" -//! ); -//! assert_eq!( -//! script.current_user()?, -//! User { -//! id: 21, -//! name: "root".into() -//! } -//! ); -//! # -//! # Ok(()) -//! # } -//! ``` -//! -//! ## Example using `Script` -//! -//! ``` -//! use osakit::{Language, Map, Script, Value, Number}; -//! -//! # use std::error::Error; -//! # fn main() -> Result<(), Box> { -//! # -//! let mut script = Script::new_from_source( -//! Language::AppleScript, " -//! on is_app_running() -//! tell application \"Hopefully Non-Existing Application\" to running -//! end is_app_running -//! -//! on concat(x, y) -//! return x & y -//! end concat -//! -//! return {id: 21, name: \"root\"} -//! "); -//! -//! script.compile()?; -//! -//! assert_eq!( -//! script.execute()?, -//! Value::Object(Map::from_iter(vec![ -//! ("id".into(), Value::Number(Number::from(21))), -//! ("name".into(), Value::String("root".into())) -//! ])) -//! ); -//! -//! assert_eq!( -//! script.execute_function("concat", vec![ -//! Value::String("Hello, ".into()), -//! Value::String("World!".into()) -//! ])?, -//! Value::String("Hello, World!".into()) -//! ); -//! -//! assert_eq!( -//! script.execute_function("is_app_running", vec![])?, -//! Value::Bool(false) -//! ); -//! # -//! # Ok(()) -//! # } -//! ``` -//! -//! ## Supported platforms -//! -//! Due to the fact that OSAKit is Mac-specific, only `macOS` is supported. +#![doc = include_str!("../README.md")] mod script; mod value; diff --git a/src/value/output.rs b/src/value/output.rs index f982999..35637bc 100644 --- a/src/value/output.rs +++ b/src/value/output.rs @@ -22,6 +22,8 @@ pub enum ScriptOutputConversionError { DescriptorNotFoundAtIndex(isize), #[error("infinite float cannot be converted: `{0}`")] InfiniteFloat(String), + #[error("url expected, but none found")] + UrlExpectedButNoneFound, } type FourCharCode = u32; @@ -80,6 +82,7 @@ four_char_codes! { DESC_TYPE_NULL: "null", DESC_TYPE_ENUM: "enum", DESC_TYPE_LDATE: "ldt ", + DESC_TYPE_URL: "url ", OSTYPE_MISSING: "msng", OSTYPE_NULL: "null", OSTYPE_YES: "yes ", @@ -141,6 +144,10 @@ pub(crate) fn get_value_from_ns_apple_event_descriptor( )) } }, + DESC_TYPE_URL => match unsafe { descriptor.stringValue() } { + Some(url) => Value::String(url.to_string()), + None => return Err(ScriptOutputConversionError::UrlExpectedButNoneFound), + }, DESC_TYPE_NULL => Value::Null, DESC_TYPE_RECORD => { let mut result: Map = Map::new(); @@ -407,6 +414,14 @@ mod test { ); } + #[test] + fn it_returns_positive_longlong_as_float() { + assert_eq!( + value_from_apple_script("9000000000"), + Value::Number(Number::from_f64(9000000000.0).unwrap()) + ); + } + #[test] fn it_returns_negative_long() { assert_eq!( @@ -446,6 +461,14 @@ mod test { ); } + #[test] + fn it_returns_url_as_string() { + assert_eq!( + value_from_apple_script("\"http://example.com\" as URL"), + Value::String("http://example.com".into()) + ); + } + #[test] fn it_returns_null() { assert_eq!(value_from_apple_script("null"), Value::Null);