I am making a native messaging application, with the web API, from firefox. The extension is supposed to call an application that parses stdin and then calls my other rust app, based on some of the data it parsed, but for no apparent reason, the rust app doesn't accept input from firefox (it works when I do it manually).
This is the code of the extension:
/*
On a click on the browser action, send the app a message.
*/
browser.browserAction.onClicked.addListener(() => {
console.log("Sending: ping");
var sending = browser.runtime.sendNativeMessage(
"themefox_manager",
"ping");
sending.then(onResponse, onError);
});
function onResponse(response) {
console.log("Received " + response);
}
function onError(error) {
console.log(`Error: ${error}`);
}
and this the code of the rust app:
use std::fs;
use std::io;
use std::io::prelude::*;
fn main() {
let stdin = io::stdin();
for line in stdin.lock().lines() {
let mut file = fs::File::create("/home/user/filename.txt").unwrap();
//
if line.unwrap() == "ping" {
file.write_all(b"TEST").expect("Error");
}
}
}
The weird thing is, that the text file in my home dir first appears, when I close firefox, not when the app gets started. And it also doen't have the text TEST.
Thanks for any help!
Cheers
I managed to make my own solution, taking a bit from this crate.
Quick note: "If you want to skip all of the code and immeditly want to start coding from a template repo, scoll to the bottom of this solution, and you should be able to find more info there."
The code, which reads the input, and then returns it, is the following:
pub fn read_input<R: Read>(mut input: R) -> io::Result<serde_json::Value> {
let length = input.read_u32::<NativeEndian>().unwrap();
let mut buffer = vec![0; length as usize];
input.read_exact(&mut buffer)?;
let json_val: serde_json::Value = serde_json::from_slice(&buffer).unwrap();
Ok(json_val)
}
What the code does, is read the input, which is being passed as a parameter to the function, and then it reads it parses it in a json var and returns the sucess/err value of it.
That code is being used in the main.rs file like this:
let json_val = match lib::read_input(io::stdin()) {
Err(why) => panic!("{}", why.to_string()),
Ok(json_val) => json_val,
};
Here the input is being passed as a parameter to the read_input function.
And to send the code i used the following function:
pub fn write_output<W: Write>(mut output: W, value: &serde_json::Value) -> io::Result<()> {
let msg = serde_json::to_string(value)?;
let len = msg.len();
// Chrome won't accept a message larger than 1MB
if len > 1024 * 1024 {
panic!("Message was too large", length: {}, len)
}
output.write_u32::<NativeEndian>(len as u32)?;
output.write_all(msg.as_bytes())?;
output.flush()?;
Ok(())
}
Which gets the stdout and the message passed as parameters. The function then writes the message to the output (normally stdout, could also be a file for debugging purposes).
The code which calls the function write_output is the following:
let response = serde_json::json!({ "msg": "pong" });
match lib::write_output(io::stdout(), &response) {
Err(why) => panic!("{}", why.to_string()),
Ok(_) => (),
};
The project uses these dependencies, so make sure to add them to Cargo.toml
"byteorder" = "*"
"serde_json" = "*"
The imports for the main.rs file are:
mod lib;
use std::io;
and for the lib.rs file, in which both functions reside:
extern crate serde_json;
use byteorder::{NativeEndian, ReadBytesExt, WriteBytesExt};
use std::error::Error;
use std::fs;
use std::io;
use std::io::{Read, Write};
I also created a git template repo, so that you can start really quick, you can find it here.
Related
I am attempting to use the windows rust crate API to print a jpeg in rust
first I import the PrintManager using use windows::Graphics::Printing::PrintManager;
then define the main function
fn main() {
and attempt to create a PrintManager using the GetForCurrentView method
let print_manager = PrintManager::GetForCurrentView().unwrap();
however when I run it I get the following error
called `Result::unwrap()` on an `Err` value: Error { code: HRESULT(0x80040155), message: "Interface not registered" }
Minimally reproducible example
Cargo.toml
...
[dependencies.windows]
version = "0.44.0"
features = [
"Devices_Printers",
"Foundation",
"Win32_Graphics_Printing",
"Graphics_Printing"
]
Main Program
use windows::Graphics::Printing::PrintManager;
fn main() {
let print_manager = PrintManager::IsSupported();
dbg!(print_manager)
// this is where the error gets thrown
let print_manager = PrintManager::GetForCurrentView().unwrap();
dbg!(print_manager);
}
I expect for the function to return a PrintManager struct that I can later use to print a JPEG however it keeps throwing this interface not registered error, I have tried running the program as an administrator with no luck.
I'm building on Solana and need some PDAs to store my program state.
The default with anchor is constantly serialize/deserialize the accounts, even when just passing the address into instructions, which crushes BPM's 4k stack limit REALLY soon for my app.
So I found the zero_copy feature, which is exactly what I need, see https://docs.rs/anchor-lang/latest/anchor_lang/attr.account.html.
The examples shown in the anchor docs, as well as some code samples I found through web search, all refer to wallet-owned accounts, not to PDAs. There is literally NOTHING to find online about zero_copy with PDAs, so I'm wondering if it's possible at all?!
Anyway - I feel I really, really need it, otherwise my PDA accounts will be limited to something around 400 bytes or so.
So I gave it a try:
#[program]
mod myapp {
use super::*;
pub fn create_aff(ctx: Context<CreateAff>, _i: u8) -> Result<()> {
let acc = &mut ctx.accounts.aff_account.load_init()?;
acc.aff_account = ctx.accounts.user.key();
acc.bump = *ctx.bumps.get("aff_account").unwrap();
Ok(())
}
}
#[account(zero_copy)]
pub struct Aff {
aff_account: Pubkey,
bump: u8,
}
#[derive(Accounts)]
#[instruction(i: u8)]
pub struct CreateAff<'info> {
#[account(init, space = 41, payer = user, seeds = [AFFSEED], bump)]
aff_account: AccountInfor<'info, Aff>,
#[account(mut)]
user: Signer<'info>,
/// CHECK: This is not dangerous because we don't read or write from this account
system_program: AccountInfo<'info>,
}
So far, so good. It compiles. Running a simple test:
it("Creates the aff account if doesn't exist", async () => {
const [affPDA, bump] = await PublicKey.findProgramAddress([anchor.utils.bytes.utf8.encode(AFFSEED)],program.programId);
console.log("CreateAff: affPDA is [", affPDA.toString(), "], bump is", bump);
const contents = await program.account.aff.fetchNullable(affPDA);
if (contents == null) {
await program.rpc.createAff({
accounts: {
affAccount: affPDA,
user: usr,
systemProgram: SystemProgram.programId,
},
signers: [],
});
const affe = await program.account.counts.fetch(affPDA);
console.log("affe: ", affe);
assert.ok(true);
} else {
assert.ok(true);
}
});
renders me an error:
Creates the aff account if doesn't exist:
Error: Invalid arguments: affAccount not provided.
at /Users/bb/app/nodestore/node_modules/#project-serum/anchor/dist/cjs/program/common.js:39:23
at Array.forEach (<anonymous>)
at validateAccounts (node_modules/#project-serum/anchor/dist/cjs/program/common.js:33:16)
at ix (node_modules/#project-serum/anchor/dist/cjs/program/namespace/instruction.js:38:46)
at txFn (node_modules/#project-serum/anchor/dist/cjs/program/namespace/transaction.js:16:20)
at Object.rpc [as createAff] (node_modules/#project-serum/anchor/dist/cjs/program/namespace/rpc.js:9:24)
at Context.<anonymous> (tests/nodeshop.js:63:25)
at processTicksAndRejections (node:internal/process/task_queues:96:5)
It's complaining affAccount is not provided, even though I'm clearly passing it in.
So the problem seems to be some part of the runtime cannot handle affAccount being AccountLoader (for zero_copy) rather than the standard AccountInfo.
Any help how I can fix or at least further debug this are highly appreciated.
I got it working. Sometimes it helps just posting a question, it helps thinking things through... ;-)
So great news: zero_copy with PDAs is possible! :-)
Here's what it was:
I originally gave the create_aff function (and the corresponding accounts struct) an i argument, even though I'm not using and additional i in the PDA account seeds. This was just a copy/paste error from a previous PDA I had been working on :-/
Since I was consistent with the i, the compiler didn't complain.
I removed the i parameter from the create_aff's parameter list as well as the #[instruction(i: u8)] from the CreateAff accounts struct declaration and, violĂ , it's working now.
Long live Solana and anchor. Oh, and a recommendation to the builders of anchor: Just make zero_copy the default, and lengthy borsh the exception!
const api = await ApiPromise.create({provider:wsProvider,
rpc:{
cf:{
getApps:{
description:"JUST For Test",
params:[{
name:"acc",
type:"AccountId"
}]
}
}
}});
const alice = keyring.addFromUri('//Alice');
let ker = crypto.encodeDerivedAddress(alice.publicKey,0);
let d = await api.rpc.cf.getApps(ker);
Rust Pallet Code
//function which I am calling
pub fn get_apps(acc:T::AccountId)->Vec<Vec<u8>>{
let data = <AppStore<T>>::get(acc);
data
}
Rust RPC
use jsonrpc_core::{Error as RpcError, ErrorCode, Result};
use jsonrpc_derive::rpc;
use sp_api::ProvideRuntimeApi;
use sp_blockchain::HeaderBackend;
use sp_runtime::{generic::BlockId,traits::Block as BlockT};
use std::sync::Arc;
use cf_runtime::AppApi as AppStorageApi;
#[rpc]
pub trait AppApi<BlockHash,AccountId>{
#[rpc(name="cf_getApps")]
fn get_apps(&self,at:Option<BlockHash>,acc:AccountId)->Result<Vec<Vec<u8>>>;
}
I have tried other prefix like 42 , 5 ,2 but not getting success , I did't understand anything how this error come again and again , and what is the default prefix for substrate node.
I have this URL :
https://www.acme.com/book/passengers?id=h1c7cafc-5457-4564-af9d-2599c6a37dde&hash=7EPbMqFFQu8T5R3AQr1GCw>msearchtype=City+Break
and want to store these values :
id=h1c7cafc-5457-4564-af9d-2599c6a37dde
hash=7EPbMqFFQu8T5R3AQr1GCw
for use in a later test.
How do I extract these values from the URL? I am using Cypress. Thanks.
Please follow the following steps and that's all there is to it.
You can put this snippet into before() hooks of your spec file and you can access them wherever you want.
cy.location().then(fullUrl => {
let pathName = fullUrl.pathname
let arr = pathName.split('?');
let arrayValues = arr[1].split('&');
cy.log(arrayValues[0]);
cy.log(arrayValues[1]);
cy.log(arrayValues[2]);
})
In case anyone needs the correct answer, use the cy.location('search') to extract the search part of the location data.
Then for convenience, convert it to a javascript object with key/value pairs for each item.
Finally, store it in a Cypress alias to use later in the test.
cy.location('search')
.then(search=> {
const searchValues = search.split('?')[1].split('&')
// yields: [
// id=h1c7cafc-5457-4564-af9d-2599c6a37dde,
// hash=7EPbMqFFQu8T5R3AQr1GCw,
// gtmsearchtype=City+Break
// ]
const searchMap = searchValues.reduce((acc,item) => {
const [key,value] = item.split('=')
acc[key] = value.replace('+', ' ')
return acc
}, {})
// yields: {
// id: "h1c7cafc-5457-4564-af9d-2599c6a37dde",
// hash: "7EPbMqFFQu8T5R3AQr1GCw",
// gtmsearchtype: "City Break"
// }
cy.wrap(searchMap).as('searchMap')
})
Using #Srinu Kodi's answer I got it working changing ...then(fullUrl => ... to
...then((fullUrl) => ...
I can't figure out why Jasmine is claiming that the function I'm spying on isn't being called, especially since it is logging in buildLinksObj when called through and not calling when I remove .and.callThrough() I feel like I've written similar code a bunch of times before without any problem. I'm using Jasmine 2.9
The error message I'm getting is:
1) addToLinks should call buildLinksObj if its given an object with children
it should add the personalized links to PageApp.meta.analytics.links
Expected spy buildLinksObj to have been called.
at UserContext.<anonymous> (http://localhost:9877webpack:///tests/specs/common/FetchPersonalContent.spec.js:854:0 <- tests/app-mcom.js:104553:48)
Here's the except of my code:
FetchPersonalContent.js
const buildLinksObj = (responseObj = {}, targetObj, PageApp) => {
console.log('it logs in buildLinksObj') // This is logging!
}
const addToLinks = (responseArr, personalizedLinks) => {
responseArr.forEach((media) => {
const type = media.type;
const typeObj = media[type];
buildLinksObj(typeObj, personalizedLinks, PageApp);
if (typeObj && typeObj.children) {
console.log('has children!')
console.log('typeObj.children is: ', typeObj.children);
typeObj.children.forEach((child) => {
console.log('has a child')
buildLinksObj(child, personalizedLinks, PageApp);
console.log('buildLinksObj was definitely called. what the heck?')
});
}
});
}
export {buildLinksObj, addToLinks, FetchPersonalContent as default,
};
FetchPersonalContent.spec.js
import * as FetchPersonalContent from '../../../src/FetchPersonalContent'; // my path is definitely correct
describe('it should add the personalized links to PageApp.meta.analytics.links', () => {
it('addToLinks should call buildLinksObj if its given an object with children ', () => {
spyOn(FetchPersonalContent, 'buildLinksObj').and.callThrough();
FetchPersonalContent.addToLinks([{
"personalId": 30718,
"type": "carousel",
"carousel": {}
}], {});
expect(FetchPersonalContent.buildLinksObj).toHaveBeenCalled();
});
});
I'd really appreciate any help!
I have a feeling FetchPersonalContent.buildLinksObj in the spec file is not pointing to the same instance as buildLinksObj in the FetchPersonalContent.js file.
Why is export {FetchPersonalContent as default} required? I am assuming you have shared the complete content of FetchPersonalContent.js in your question.
Possible solutions:
You can try removing FetchPersonalContent from the export statement.
Or
Instead of
export {buildLinksObj, addToLinks, FetchPersonalContent as default,
};
You can directly export the constants in FetchPersonalContent.js file.