Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

check and rework wildcard pub re-export #473

Merged
merged 1 commit into from
Mar 29, 2023
Merged

Conversation

echevrier
Copy link
Contributor

@echevrier echevrier commented Mar 9, 2023

  • remove wildcard pub re-export if possible: re-export explicitly instead
  • clean the public API:
    • improve primitives structure: add an extrinsics module
    • update public WsRpcClient API to be similar to TungsteniteRpcClient
    • dont' re-export the top level: the crate name is in the public API, as it is an important information. This should- solve the initial problem that the same type is defined in several locations.
  • re-order the use: use, pub use, type def
    Fix issue Do not use wildcard pub re-export #430

@echevrier echevrier added F5-refactor Does not change any functionality of the code E2-breaksapi and removed F5-refactor Does not change any functionality of the code labels Mar 9, 2023
@echevrier echevrier linked an issue Mar 9, 2023 that may be closed by this pull request
@echevrier echevrier linked an issue Mar 13, 2023 that may be closed by this pull request
@echevrier echevrier requested review from haerdib and clangenb March 13, 2023 10:52
Copy link
Collaborator

@clangenb clangenb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for these fixes. I think it goes in the right direction, but I have a few remarks. :)

I see that you have removed the wildcard * re-exports in many places. I think this is a good chance, but I think only in very few cases this should be replaced by only re-exporting a subset of the definition. The questions should rather have been, do we need to re-export anything here at all, or is the wildcard re-export just obscuring that the crate structure is not so good?

So good:

  • no more wildcard re-exports. We should always see explicitly what is exported from a module, even if everything is re-exported.

Needs some more work:

  • Especially the primitives crate has a weird public api. I think this should be reworked.
  • Re-consider partial re-export in some cases. I think, most of the time, it makes sense to re-export everything from a module or nothing.

pub use storage::*;
//re-export only "important" items: used in examples, ...
pub use decoder::{
decode_as_type, decode_value_as_type, encode_as_type, encode_value_as_type, Primitive, Value,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we do this, we should at least export the DecodeError too, be a random choice in my opinion.

However, in the case of the decoder, I believe that might be better not to re-export at all, and keep the hierarchy to have a better domain overview.

Copy link
Contributor Author

@echevrier echevrier Mar 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not re-export decoder is not consistent with the other re-exports. We would have node_api::MetadataError but node_api::decoder::DecodeError.
But you're right, if we decide to re-export some part of the crate, we should not only re-export the items used in the examples.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I vote for not having re-exports at all in the node_api

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The decoder is needed for internal use only (by default at least). I doubt any regular user ever has to touch and use the decoder directly. And as it's pub mod, users may still access it via ::decoder::

That's exactly the opposite of EventDetails, Events and Metadata stuff, which are meant to be used by the regular users. So I vote for removing this pub reexport completely.. Except for maybe the Error types.

primitives/src/extrinsics.rs Outdated Show resolved Hide resolved
pub use extrinsics::*;
pub use pallet_traits::*;
pub use rpc_numbers::*;
//re-export only "important" items: items used in examples
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't 100% agree with this comment. In my opinion, importance should rather be a matter of pub/private identifiers. From my perspective, this should be the question asked:

Does it make sense that the internal module structure is the same structure the user needs to use to import things? The answer to this question is in my opinion:

  • Don't re-export and keep the structure if the module represents a bigger domain.
  • Do re-export from modules that are only separating tiny domains, or implementation details.

There is also one other case, which is unrelated to the above question:

  • If you want to make a type available from another 3rd party crate, which would be needed as a dependency otherwise. However, in this case it is the same, there is no black and white answer. Sometimes it makes sense, and sometimes it doesn't.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

src/rpc/ws_client/mod.rs Outdated Show resolved Hide resolved
src/lib.rs Outdated
Comment on lines 22 to 26
// Required wildcard re-exporting to make all items available.
// Should not short-circuit the "privacy chain".
pub use ac_compose_macros::*;
pub use ac_node_api::*;
pub use ac_primitives::*;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I am actually against this re-export, we have quite big domains here, and re-exporting everything here adds too many things to the top-level. I think this is also what confused my IDE. The same types were available at about 3 different locations in the path coming from here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do believe, that if we do not re-export these crates, we will have to add them individually over Cargo.
Yes, we have here a problem with "general" type names, like Event,Error, ... because by re-exporting these base modules, we lose the specific module name in the path.

Copy link
Contributor Author

@echevrier echevrier Mar 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad, we can use pub extern crate
But we still re-export api::* ?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, sorry I misformulated this. I think we can re-export this, but only the module itself:

pub use ac_compose_macros;
pub use ac_node_api;
pub use ac_primitives;

hmm, maybe we can rename this:

pub use ac_compose_macros as macros;
pub use ac_node_api as node_api;
pub use ac_primitives as primitives;

I am unsure though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should rename them

@@ -21,7 +21,8 @@
use crate::{rpc::Request, Api};
use ac_compose_macros::compose_extrinsic;
use ac_primitives::{
CallIndex, ExtrinsicParams, FrameSystemConfig, SignExtrinsic, UncheckedExtrinsicV4,
extrinsic_params::ExtrinsicParams, extrinsics::CallIndex, FrameSystemConfig, SignExtrinsic,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems a bit weird to me, we have extrinsics_params::ExtrinsicParams, extrinsics::CallIndex and SignExtrinsic, which is arguably not only a matter of the re-exporting, but also how the crate is structured itself. Here it would make so much more sense to be able to do:

use ac_primitives::extrinsics::{ExtrinsicParams, CallIndex, SignExtrinsic};

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To do this, we need to add an `extrinsics' module to the primitives:

  • extrinsics/
    • mod.rs (CallIndex, UncheckedExtrinsicV4)
    • extrinsic_params.rs
    • signer.rs

And we will get: use ac_primitives::extrinsics::{AssetTipExtrinsicParams, ExtrinsicParams, CallIndex, SignExtrinsic,UncheckedExtrinsicV4};

This would not be consistent with the RpcParams use ac_primitives::RpcParams. Similarly to #473 (comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

src/api/mod.rs Show resolved Hide resolved
Comment on lines 26 to 29
pub use extrinsics::UncheckedExtrinsicV4;
pub use pallet_traits::FrameSystemConfig;
pub use rpc_params::RpcParams;
pub use serde_impls::*;
pub use signer::*;
pub use types::*;
pub use signer::{ExtrinsicSigner, SignExtrinsic};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To me, this seems like absolutely random re-exports, I think it is better to not have any re-exports at all. This crate should export its internal types differently, this is something that makes sense to me:

// this is only for illustration for the structure. I think the crate should be
// reworked to have a `extrinsics` module (in a folder). 
//
// And **don't** do `pub use extrinsics::*` this is a prime example of a good domain
// that should stay encapsulated.
pub mod extrinsics {
    pub use extrinsics_params::*;
    pub use signer::*;

   // all the pub types that are currently in extrinsics.
}

// cleanly re-export rpc stuff.
pub mod rpc {
   pub use rpc_numbers::*;
   pub use rpc_params::*;
}

// cleanly re-export other substrate types
pub mod substrate {
   // that we have this module because we add `serde_impl` in `no_std` is an implementation detail.
   // The user of the api-client does not care about this.
   pub use serde_impl::*;

   // maybe we can put here the types that lived in the top-level `types` before.
   pub use pallet_types::*;
}

This leaves three clean modules that can be referred from the outside:

pub mod extrinsics;
pub mod substrate;
pub mod rpc;
pub mod pallet_traits;

Copy link
Contributor Author

@echevrier echevrier Mar 20, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK. This answers my previous comment #473 (comment)
I agree that we should add modules. But I'm not sure about not re-exporting the API. We do it in the other crates. Ex:
pub use rpc_api::SubmitExtrinsic -> call from outside: use substrate_api_client::SubmitExtrinsic
but we would request for UncheckedExtrinsicV4 : use substrate_api_client::extrinsics::UncheckedExtrinsicV4

I think what's bad is that the most important info it's a primitive is no longer in the exported API, but that it's an extrinsic, it's clear, we can deduce it from the name or the context. And this also applies to the other extrinsic primitives: AssetTipExtrinsicParams, ExtrinsicParams, GenericExtrinsicParams, GenericAdditionalSigned, GenericAdditionalParams GenericSignedExtra, SignExtrinsic

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think we should not re-export it. It should probably be:

substrate_api_client::ac_primitives::extrinsics::UncheckedExtrinsicV4

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines 21 to 19
pub use subscription::WsSubscriptionWrapper;

use log::*;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please order pub use to be at the end of the import statements after all regular use statements.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What/where are the guidelines? Do you mean, we should order like this:

  • use
  • pub use
  • pub type def
    ?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think this is how we have been doing it everywhere. However, I just checked. It was never put into our coding guidelines. :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK

@@ -18,6 +18,8 @@
use crate::rpc::Error as RpcClientError;
pub use ac_node_api::{events::EventDetails, StaticEvent};
pub use client::WsRpcClient;
pub use subscription::WsSubscriptionWrapper;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a good example, where the outside does not care if the WsSubscriptionWrapper lives inside the subscription module or not.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it is

@echevrier
Copy link
Contributor Author

echevrier commented Mar 20, 2023

The questions should rather have been, do we need to re-export anything here at all, or is the wildcard re-export just obscuring that the crate structure is not so good

I agree with it. We can't fix this wildcard re-export perfectly without thinking/reworking our structure.

@echevrier
Copy link
Contributor Author

After a discussion with @clangenb we agree to :

  • create add module "extrinsics" in the primitives
  • to create a pubic API like :
- substrate_api_client::ac_primitives::UncheckedExtrinsicV4
- substrate_api_client::ac_primitives::RpcParams
- substrate_api_client::ac_primitives::BalancesConfig
- substrate_api_client::ac_primitives::AccountInfo
- substrate_api_client::ac_node_api::MetadataError
- substrate_api_client::ac_node_api::DecodeError
- substrate_api_client::ac_compose_macros::compose_extrinsic
- substrate_api_client::API
- substrate_api_client::SubmitAndWatch

@@ -12,9 +12,10 @@
*/

use crate::{
ac_node_api::Phase,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you move that to the use ac_node_api::{EventDetails, EventRecord, Events}; ?

@@ -11,9 +11,10 @@
limitations under the License.
*/
use crate::{
ac_node_api::MetadataError,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(optional) move this outside of crate

@@ -17,12 +17,12 @@

use super::{subscription::WsSubscriptionWrapper, HandleMessage};
use crate::{
ac_primitives::RpcParams,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(optional) move this outside of crate?

pub use storage::*;
//re-export only "important" items: used in examples, ...
pub use decoder::{
decode_as_type, decode_value_as_type, encode_as_type, encode_value_as_type, Primitive, Value,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The decoder is needed for internal use only (by default at least). I doubt any regular user ever has to touch and use the decoder directly. And as it's pub mod, users may still access it via ::decoder::

That's exactly the opposite of EventDetails, Events and Metadata stuff, which are meant to be used by the regular users. So I vote for removing this pub reexport completely.. Except for maybe the Error types.

- re-export only "important" items, that is items used in example
- clean JsonrpseeClient API
- didn't touch node-api/src/decoder as taken from parity

export dependencies crate with pub extern crate instead of problematic re-export with pub use wildcard

changes from review, remove invalid comments

Order of uses: guideline should be:
- use
- pub use
- type def

Add extrinsics module and improve public API of ac_primitives:
- Remove internal structure of extrinsics for Public API
- Remove internal structure of ac_primitives for Public API: all primitives' definitions part of public ac_primitives API
- Unify use path to ac_primitives types

Remove unused use

clean re-export decoder

remove unnecessary re-export

missed order use

Error re-export is required

Changes from review
@echevrier echevrier requested a review from haerdib March 29, 2023 13:29
Copy link
Contributor

@haerdib haerdib left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, thanks a lot!

@echevrier echevrier merged commit 19588b1 into master Mar 29, 2023
@echevrier echevrier deleted the ec/pub_reexport branch April 18, 2023 14:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Do not use wildcard pub re-export
3 participants