@@ -651,20 +651,25 @@ fn contains_code(haystack: &str, code: &str) -> bool {
651651
652652fn parse_resistance ( raw : & str ) -> Option < String > {
653653 // Try common project convention: "R_10k_0402"
654- let parts: Vec < & str > = raw
655- . split ( |c : char | c == '_' || c == '-' || c. is_whitespace ( ) )
656- . collect ( ) ;
654+ let parts = tokenize_import_value ( raw) ;
657655 if parts. len ( ) >= 2 && parts[ 0 ] . eq_ignore_ascii_case ( "r" ) {
658656 if let Some ( v) = parse_resistance_token ( parts[ 1 ] ) {
659657 return Some ( v) ;
660658 }
661659 }
662- for part in parts {
660+ for part in & parts {
663661 if let Some ( v) = parse_resistance_token ( part) {
664662 return Some ( v) ;
665663 }
666664 }
667- None
665+ // Avoid merging a standalone refdes prefix token like "R" with following
666+ // value/package tokens ("R"+"10" -> "R10", "R"+"0402" -> "R0402").
667+ let merge_parts = if parts. first ( ) . is_some_and ( |p| p. eq_ignore_ascii_case ( "r" ) ) {
668+ & parts[ 1 ..]
669+ } else {
670+ & parts[ ..]
671+ } ;
672+ parse_from_merged_tokens ( merge_parts, parse_resistance_token)
668673}
669674
670675fn parse_resistance_token ( token : & str ) -> Option < String > {
@@ -674,7 +679,10 @@ fn parse_resistance_token(token: &str) -> Option<String> {
674679 }
675680 let mut s = t. to_ascii_uppercase ( ) ;
676681 s = s. replace ( 'Ω' , "OHM" ) ;
682+ s = s. replace ( 'Ω' , "OHM" ) ;
677683 s = s. replace ( 'µ' , "U" ) ;
684+ s = s. replace ( 'μ' , "U" ) ;
685+ s = s. replace ( ',' , "." ) ;
678686 s = s. replace ( "OHMS" , "OHM" ) ;
679687 s = s. replace ( "KOHM" , "K" ) ;
680688 s = s. replace ( "MOHM" , "M" ) ;
@@ -691,6 +699,14 @@ fn parse_resistance_token(token: &str) -> Option<String> {
691699 return None ;
692700 }
693701
702+ // R10 / R005 => 0.10 / 0.005
703+ if let Some ( frac) = s. strip_prefix ( 'R' ) {
704+ if !frac. is_empty ( ) && frac. chars ( ) . all ( |c| c. is_ascii_digit ( ) ) {
705+ let out = format ! ( "0.{frac}" ) ;
706+ return Some ( normalize_decimal_string ( & out) ) ;
707+ }
708+ }
709+
694710 // Reject tokens that contain capacitance-like units.
695711 if s. contains ( "UF" ) || s. contains ( "NF" ) || s. contains ( "PF" ) {
696712 return None ;
@@ -759,20 +775,18 @@ fn parse_resistance_token(token: &str) -> Option<String> {
759775
760776fn parse_capacitance ( raw : & str ) -> Option < String > {
761777 // Try common project convention: "C_100n_0402"
762- let parts: Vec < & str > = raw
763- . split ( |c : char | c == '_' || c == '-' || c. is_whitespace ( ) )
764- . collect ( ) ;
778+ let parts = tokenize_import_value ( raw) ;
765779 if parts. len ( ) >= 2 && parts[ 0 ] . eq_ignore_ascii_case ( "c" ) {
766780 if let Some ( v) = parse_capacitance_token ( parts[ 1 ] ) {
767781 return Some ( v) ;
768782 }
769783 }
770- for part in parts {
784+ for part in & parts {
771785 if let Some ( v) = parse_capacitance_token ( part) {
772786 return Some ( v) ;
773787 }
774788 }
775- None
789+ parse_from_merged_tokens ( & parts , parse_capacitance_token )
776790}
777791
778792fn parse_capacitance_token ( token : & str ) -> Option < String > {
@@ -782,6 +796,8 @@ fn parse_capacitance_token(token: &str) -> Option<String> {
782796 }
783797 let mut s = t. to_ascii_uppercase ( ) ;
784798 s = s. replace ( 'µ' , "U" ) ;
799+ s = s. replace ( 'μ' , "U" ) ;
800+ s = s. replace ( ',' , "." ) ;
785801 s = s. replace ( ' ' , "" ) ;
786802
787803 // Reject tokens that look like a resistance designation (prevents "10K" being treated as cap).
@@ -828,6 +844,57 @@ fn parse_capacitance_token(token: &str) -> Option<String> {
828844 None
829845}
830846
847+ fn parse_from_merged_tokens (
848+ parts : & [ & str ] ,
849+ parse_token : fn ( & str ) -> Option < String > ,
850+ ) -> Option < String > {
851+ for width in [ 2usize , 3usize ] {
852+ if parts. len ( ) < width {
853+ continue ;
854+ }
855+ for i in 0 ..=parts. len ( ) - width {
856+ let merged = parts[ i..i + width] . join ( "" ) ;
857+ if let Some ( v) = parse_token ( & merged) {
858+ return Some ( v) ;
859+ }
860+ }
861+ }
862+ None
863+ }
864+
865+ fn tokenize_import_value ( raw : & str ) -> Vec < & str > {
866+ raw. split ( |c : char | {
867+ c == '_'
868+ || c == '-'
869+ || c == '/'
870+ || c == ':'
871+ || c == '|'
872+ || c == '('
873+ || c == ')'
874+ || c == '['
875+ || c == ']'
876+ || c == '{'
877+ || c == '}'
878+ || c. is_whitespace ( )
879+ } )
880+ . filter ( |p| !p. is_empty ( ) )
881+ . collect ( )
882+ }
883+
884+ fn normalize_decimal_string ( s : & str ) -> String {
885+ let trimmed = s. trim ( ) ;
886+ if let Some ( ( a, b) ) = trimmed. split_once ( '.' ) {
887+ let b = b. trim_end_matches ( '0' ) ;
888+ if b. is_empty ( ) {
889+ a. to_string ( )
890+ } else {
891+ format ! ( "{a}.{b}" )
892+ }
893+ } else {
894+ trimmed. to_string ( )
895+ }
896+ }
897+
831898fn is_number ( s : & str ) -> bool {
832899 let s = s. trim ( ) ;
833900 if s. is_empty ( ) {
@@ -912,7 +979,11 @@ mod tests {
912979 ( "4K7" , "4.7k" ) ,
913980 ( "49R9" , "49.9" ) ,
914981 ( "10R0" , "10" ) ,
982+ ( "R10" , "0.1" ) ,
915983 ( "1M" , "1M" ) ,
984+ ( "10 k" , "10k" ) ,
985+ ( "10 kΩ" , "10k" ) ,
986+ ( "10 kOhm" , "10k" ) ,
916987 ( "R_10k_0402" , "10k" ) ,
917988 ( "10ohm" , "10" ) ,
918989 ( "10Ω" , "10" ) ,
@@ -923,7 +994,14 @@ mod tests {
923994 "resistance parse: {raw}"
924995 ) ;
925996 }
926- for raw in [ "Murata-BLM21PG_0805" , "LED_G_0603" , "100n" , "0.1uF" ] {
997+ for raw in [
998+ "Murata-BLM21PG_0805" ,
999+ "LED_G_0603" ,
1000+ "100n" ,
1001+ "0.1uF" ,
1002+ "R_10_0402" ,
1003+ "R_0402" ,
1004+ ] {
9271005 assert ! ( parse_resistance( raw) . is_none( ) , "expected none: {raw}" ) ;
9281006 }
9291007 }
@@ -934,6 +1012,11 @@ mod tests {
9341012 ( "100n" , "100nF" ) ,
9351013 ( "0.1uF" , "0.1uF" ) ,
9361014 ( "1UF" , "1uF" ) ,
1015+ ( "1 uF" , "1uF" ) ,
1016+ ( "2.2 uF" , "2.2uF" ) ,
1017+ ( "2,2uF" , "2.2uF" ) ,
1018+ ( "1uF/16V" , "1uF" ) ,
1019+ ( "1 μF" , "1uF" ) ,
9371020 ( "22P" , "22pF" ) ,
9381021 ( "C_100n_0402" , "100nF" ) ,
9391022 ( "10u" , "10uF" ) ,
0 commit comments